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 */ 017package org.apache.commons.math4.legacy.analysis.interpolation; 018 019import org.apache.commons.numbers.angle.Reduce; 020import org.apache.commons.numbers.arrays.SortInPlace; 021import org.apache.commons.math4.legacy.analysis.UnivariateFunction; 022import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException; 023import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException; 024import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException; 025import org.apache.commons.math4.legacy.core.MathArrays; 026 027/** 028 * Adapter for classes implementing the {@link UnivariateInterpolator} 029 * interface. 030 * The data to be interpolated is assumed to be periodic. Thus values that are 031 * outside of the range can be passed to the interpolation function: They will 032 * be wrapped into the initial range before being passed to the class that 033 * actually computes the interpolation. 034 * 035 */ 036public class UnivariatePeriodicInterpolator 037 implements UnivariateInterpolator { 038 /** Default number of extension points of the samples array. */ 039 public static final int DEFAULT_EXTEND = 5; 040 /** Interpolator. */ 041 private final UnivariateInterpolator interpolator; 042 /** Period. */ 043 private final double period; 044 /** Number of extension points. */ 045 private final int extend; 046 047 /** 048 * Builds an interpolator. 049 * 050 * @param interpolator Interpolator. 051 * @param period Period. 052 * @param extend Number of points to be appended at the beginning and 053 * end of the sample arrays in order to avoid interpolation failure at 054 * the (periodic) boundaries of the original interval. The value is the 055 * number of sample points which the original {@code interpolator} needs 056 * on each side of the interpolated point. 057 */ 058 public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator, 059 double period, 060 int extend) { 061 this.interpolator = interpolator; 062 this.period = period; 063 this.extend = extend; 064 } 065 066 /** 067 * Builds an interpolator. 068 * Uses {@link #DEFAULT_EXTEND} as the number of extension points on each side 069 * of the original abscissae range. 070 * 071 * @param interpolator Interpolator. 072 * @param period Period. 073 */ 074 public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator, 075 double period) { 076 this(interpolator, period, DEFAULT_EXTEND); 077 } 078 079 /** 080 * {@inheritDoc} 081 * 082 * @throws NumberIsTooSmallException if the number of extension points 083 * is larger than the size of {@code xval}. 084 */ 085 @Override 086 public UnivariateFunction interpolate(double[] xval, 087 double[] yval) 088 throws NumberIsTooSmallException, NonMonotonicSequenceException { 089 if (xval.length < extend) { 090 throw new NumberIsTooSmallException(xval.length, extend, true); 091 } 092 093 MathArrays.checkOrder(xval); 094 final double offset = xval[0]; 095 final Reduce reduce = new Reduce(offset, period); 096 097 final int len = xval.length + extend * 2; 098 final double[] x = new double[len]; 099 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}