View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math4.legacy.analysis.interpolation;
18  
19  import org.apache.commons.numbers.angle.Reduce;
20  import org.apache.commons.numbers.arrays.SortInPlace;
21  import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
22  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
23  import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
24  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
25  import org.apache.commons.math4.legacy.core.MathArrays;
26  
27  /**
28   * Adapter for classes implementing the {@link UnivariateInterpolator}
29   * interface.
30   * The data to be interpolated is assumed to be periodic. Thus values that are
31   * outside of the range can be passed to the interpolation function: They will
32   * be wrapped into the initial range before being passed to the class that
33   * actually computes the interpolation.
34   *
35   */
36  public class UnivariatePeriodicInterpolator
37      implements UnivariateInterpolator {
38      /** Default number of extension points of the samples array. */
39      public static final int DEFAULT_EXTEND = 5;
40      /** Interpolator. */
41      private final UnivariateInterpolator interpolator;
42      /** Period. */
43      private final double period;
44      /** Number of extension points. */
45      private final int extend;
46  
47      /**
48       * Builds an interpolator.
49       *
50       * @param interpolator Interpolator.
51       * @param period Period.
52       * @param extend Number of points to be appended at the beginning and
53       * end of the sample arrays in order to avoid interpolation failure at
54       * the (periodic) boundaries of the original interval. The value is the
55       * number of sample points which the original {@code interpolator} needs
56       * on each side of the interpolated point.
57       */
58      public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator,
59                                            double period,
60                                            int extend) {
61          this.interpolator = interpolator;
62          this.period = period;
63          this.extend = extend;
64      }
65  
66      /**
67       * Builds an interpolator.
68       * Uses {@link #DEFAULT_EXTEND} as the number of extension points on each side
69       * of the original abscissae range.
70       *
71       * @param interpolator Interpolator.
72       * @param period Period.
73       */
74      public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator,
75                                            double period) {
76          this(interpolator, period, DEFAULT_EXTEND);
77      }
78  
79      /**
80       * {@inheritDoc}
81       *
82       * @throws NumberIsTooSmallException if the number of extension points
83       * is larger than the size of {@code xval}.
84       */
85      @Override
86      public UnivariateFunction interpolate(double[] xval,
87                                            double[] yval)
88          throws NumberIsTooSmallException, NonMonotonicSequenceException {
89          if (xval.length < extend) {
90              throw new NumberIsTooSmallException(xval.length, extend, true);
91          }
92  
93          MathArrays.checkOrder(xval);
94          final double offset = xval[0];
95          final Reduce reduce = new Reduce(offset, period);
96  
97          final int len = xval.length + extend * 2;
98          final double[] x = new double[len];
99          final double[] y = new double[len];
100         for (int i = 0; i < xval.length; i++) {
101             final int index = i + extend;
102             x[index] = reduce.applyAsDouble(xval[i]);
103             y[index] = yval[i];
104         }
105 
106         // Wrap to enable interpolation at the boundaries.
107         for (int i = 0; i < extend; i++) {
108             int index = xval.length - extend + i;
109             x[i] = reduce.applyAsDouble(xval[index]) - period;
110             y[i] = yval[index];
111 
112             index = len - extend + i;
113             x[index] = reduce.applyAsDouble(xval[i]) + period;
114             y[index] = yval[i];
115         }
116 
117         SortInPlace.ASCENDING.apply(x, y);
118 
119         final UnivariateFunction f = interpolator.interpolate(x, y);
120         return new UnivariateFunction() {
121             /** {@inheritDoc} */
122             @Override
123             public double value(final double x) throws MathIllegalArgumentException {
124                 return f.value(reduce.applyAsDouble(x));
125             }
126         };
127     }
128 }