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    package org.apache.commons.math.analysis.polynomials;
018    
019    import java.util.Arrays;
020    import junit.framework.TestCase;
021    
022    import org.apache.commons.math.FunctionEvaluationException;
023    import org.apache.commons.math.analysis.UnivariateRealFunction;
024    
025    /**
026     * Tests the PolynomialSplineFunction implementation.
027     *
028     * @version $Revision: 799857 $
029     */
030    public class PolynomialSplineFunctionTest extends TestCase {
031    
032        /** Error tolerance for tests */
033        protected double tolerance = 1.0e-12;
034        
035        /** 
036         * Quadratic polynomials used in tests: 
037         * 
038         * x^2 + x            [-1, 0)
039         * x^2 + x + 2        [0, 1)
040         * x^2 + x + 4        [1, 2)
041         * 
042         * Defined so that evaluation using PolynomialSplineFunction evaluation
043         * algorithm agrees at knot point boundaries.
044         */
045        protected PolynomialFunction[] polynomials = {
046            new PolynomialFunction(new double[] {0d, 1d, 1d}), 
047            new PolynomialFunction(new double[] {2d, 1d, 1d}),
048            new PolynomialFunction(new double[] {4d, 1d, 1d})
049        };
050        
051        /** Knot points  */
052        protected double[] knots = {-1, 0, 1, 2};
053        
054        /** Derivative of test polynomials -- 2x + 1  */
055        protected PolynomialFunction dp = 
056            new PolynomialFunction(new double[] {1d, 2d});
057        
058        
059        public void testConstructor() {
060            PolynomialSplineFunction spline = 
061                new PolynomialSplineFunction(knots, polynomials);
062            assertTrue(Arrays.equals(knots, spline.getKnots()));
063            assertEquals(1d, spline.getPolynomials()[0].getCoefficients()[2], 0);
064            assertEquals(3, spline.getN());
065            
066            try { // too few knots
067                new PolynomialSplineFunction(new double[] {0}, polynomials);
068                fail("Expecting IllegalArgumentException");
069            } catch (IllegalArgumentException ex) {
070                // expected
071            }
072            
073            try { // too many knots
074                new PolynomialSplineFunction(new double[] {0,1,2,3,4}, polynomials);
075                fail("Expecting IllegalArgumentException");
076            } catch (IllegalArgumentException ex) {
077                // expected
078            }
079            
080            try { // knots not increasing
081                new PolynomialSplineFunction(new double[] {0,1, 3, 2}, polynomials);
082                fail("Expecting IllegalArgumentException");
083            } catch (IllegalArgumentException ex) {
084                // expected
085            }
086        }
087        
088        public void testValues() throws Exception {
089            PolynomialSplineFunction spline = 
090                new PolynomialSplineFunction(knots, polynomials);
091            UnivariateRealFunction dSpline = spline.derivative();
092            
093            /**
094             * interior points -- spline value at x should equal p(x - knot)
095             * where knot is the largest knot point less than or equal to x and p 
096             * is the polynomial defined over the knot segment to which x belongs.
097             */
098            double x = -1;
099            int index = 0;
100            for (int i = 0; i < 10; i++) {
101               x+=0.25;
102               index = findKnot(knots, x);
103               assertEquals("spline function evaluation failed for x=" + x, 
104                       polynomials[index].value(x - knots[index]), spline.value(x), tolerance);
105               assertEquals("spline derivative evaluation failed for x=" + x,
106                       dp.value(x - knots[index]), dSpline.value(x), tolerance);
107            }
108            
109            // knot points -- centering should zero arguments
110            for (int i = 0; i < 3; i++) {
111                assertEquals("spline function evaluation failed for knot=" + knots[i],
112                        polynomials[i].value(0), spline.value(knots[i]), tolerance);
113                assertEquals("spline function evaluation failed for knot=" + knots[i],
114                        dp.value(0), dSpline.value(knots[i]), tolerance);
115            }
116            
117            try { //outside of domain -- under min
118                x = spline.value(-1.5);
119                fail("Expecting IllegalArgumentException");
120            } catch (FunctionEvaluationException ex) {
121                // expected
122            }
123            
124            try { //outside of domain -- over max
125                x = spline.value(2.5);
126                fail("Expecting IllegalArgumentException");
127            } catch (FunctionEvaluationException ex) {
128                // expected
129            }         
130        }  
131        
132        /**
133         *  Do linear search to find largest knot point less than or equal to x.
134         *  Implementation does binary search.
135         */
136         protected int findKnot(double[] knots, double x) {
137             if (x < knots[0] || x >= knots[knots.length -1]) {
138                 throw new IllegalArgumentException("x is out of range");
139             }
140             for (int i = 0; i < knots.length; i++) {
141                 if (knots[i] > x) {
142                     return i -1;
143                 }
144             }
145             throw new IllegalArgumentException("x is out of range");
146         }
147    }
148