001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.math4.analysis.function;
019
020import java.util.Arrays;
021
022import org.apache.commons.math4.analysis.UnivariateFunction;
023import org.apache.commons.math4.exception.DimensionMismatchException;
024import org.apache.commons.math4.exception.NoDataException;
025import org.apache.commons.math4.exception.NonMonotonicSequenceException;
026import org.apache.commons.math4.exception.NullArgumentException;
027import org.apache.commons.math4.util.MathArrays;
028
029/**
030 * <a href="http://en.wikipedia.org/wiki/Step_function">
031 *  Step function</a>.
032 *
033 * @since 3.0
034 */
035public class StepFunction implements UnivariateFunction {
036    /** Abscissae. */
037    private final double[] abscissa;
038    /** Ordinates. */
039    private final double[] ordinate;
040
041    /**
042     * Builds a step function from a list of arguments and the corresponding
043     * values. Specifically, returns the function h(x) defined by <pre><code>
044     * h(x) = y[0] for all x &lt; x[1]
045     *        y[1] for x[1] &le; x &lt; x[2]
046     *        ...
047     *        y[y.length - 1] for x &ge; x[x.length - 1]
048     * </code></pre>
049     * The value of {@code x[0]} is ignored, but it must be strictly less than
050     * {@code x[1]}.
051     *
052     * @param x Domain values where the function changes value.
053     * @param y Values of the function.
054     * @throws NonMonotonicSequenceException
055     * if the {@code x} array is not sorted in strictly increasing order.
056     * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
057     * @throws NoDataException if {@code x} or {@code y} are zero-length.
058     * @throws DimensionMismatchException if {@code x} and {@code y} do not
059     * have the same length.
060     */
061    public StepFunction(double[] x,
062                        double[] y)
063        throws NullArgumentException, NoDataException,
064               DimensionMismatchException, NonMonotonicSequenceException {
065        if (x == null ||
066            y == null) {
067            throw new NullArgumentException();
068        }
069        if (x.length == 0 ||
070            y.length == 0) {
071            throw new NoDataException();
072        }
073        if (y.length != x.length) {
074            throw new DimensionMismatchException(y.length, x.length);
075        }
076        MathArrays.checkOrder(x);
077
078        abscissa = Arrays.copyOf(x, x.length);
079        ordinate = Arrays.copyOf(y, y.length);
080    }
081
082    /** {@inheritDoc} */
083    @Override
084    public double value(double x) {
085        int index = Arrays.binarySearch(abscissa, x);
086        double fx = 0;
087
088        if (index < -1) {
089            // "x" is between "abscissa[-index-2]" and "abscissa[-index-1]".
090            fx = ordinate[-index-2];
091        } else if (index >= 0) {
092            // "x" is exactly "abscissa[index]".
093            fx = ordinate[index];
094        } else {
095            // Otherwise, "x" is smaller than the first value in "abscissa"
096            // (hence the returned value should be "ordinate[0]").
097            fx = ordinate[0];
098        }
099
100        return fx;
101    }
102}