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    
018    package org.apache.commons.math3.analysis.function;
019    
020    import java.util.Arrays;
021    import org.apache.commons.math3.analysis.UnivariateFunction;
022    import org.apache.commons.math3.exception.DimensionMismatchException;
023    import org.apache.commons.math3.exception.NullArgumentException;
024    import org.apache.commons.math3.exception.NoDataException;
025    import org.apache.commons.math3.util.MathArrays;
026    
027    /**
028     * <a href="http://en.wikipedia.org/wiki/Step_function">
029     *  Step function</a>.
030     *
031     * @since 3.0
032     * @version $Id: StepFunction.java 1379218 2012-08-30 23:18:57Z erans $
033     */
034    public class StepFunction implements UnivariateFunction {
035        /** Abscissae. */
036        private final double[] abscissa;
037        /** Ordinates. */
038        private final double[] ordinate;
039    
040        /**
041         * Builds a step function from a list of arguments and the corresponding
042         * values. Specifically, returns the function h(x) defined by <pre><code>
043         * h(x) = y[0] for all x < x[1]
044         *        y[1] for x[1] <= x < x[2]
045         *        ...
046         *        y[y.length - 1] for x >= x[x.length - 1]
047         * </code></pre>
048         * The value of {@code x[0]} is ignored, but it must be strictly less than
049         * {@code x[1]}.
050         *
051         * @param x Domain values where the function changes value.
052         * @param y Values of the function.
053         * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException
054         * if the {@code x} array is not sorted in strictly increasing order.
055         * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
056         * @throws NoDataException if {@code x} or {@code y} are zero-length.
057         * @throws DimensionMismatchException if {@code x} and {@code y} do not
058         * have the same length.
059         */
060        public StepFunction(double[] x,
061                            double[] y)
062            throws NullArgumentException,
063                   NoDataException,
064                   DimensionMismatchException {
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 = MathArrays.copyOf(x);
079            ordinate = MathArrays.copyOf(y);
080        }
081    
082        /** {@inheritDoc} */
083        public double value(double x) {
084            int index = Arrays.binarySearch(abscissa, x);
085            double fx = 0;
086    
087            if (index < -1) {
088                // "x" is between "abscissa[-index-2]" and "abscissa[-index-1]".
089                fx = ordinate[-index-2];
090            } else if (index >= 0) {
091                // "x" is exactly "abscissa[index]".
092                fx = ordinate[index];
093            } else {
094                // Otherwise, "x" is smaller than the first value in "abscissa"
095                // (hence the returned value should be "ordinate[0]").
096                fx = ordinate[0];
097            }
098    
099            return fx;
100        }
101    }