/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.interpolation;
import org.apache.commons.lang.Validate;
import com.opengamma.analytics.math.MathException;
import com.opengamma.analytics.math.interpolation.data.ArrayInterpolator1DDataBundle;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DCubicSplineDataBundle;
import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle;
/**
*
*/
public class NaturalCubicSplineInterpolator1D extends Interpolator1D {
private static final long serialVersionUID = 1L;
private final double _eps;
public NaturalCubicSplineInterpolator1D() {
_eps = 1e-12;
}
public NaturalCubicSplineInterpolator1D(final double eps) {
_eps = eps;
}
@Override
public Double interpolate(final Interpolator1DDataBundle data, final Double value) {
Validate.notNull(value, "value");
Validate.notNull(data, "data bundle");
Validate.isTrue(data instanceof Interpolator1DCubicSplineDataBundle);
Interpolator1DCubicSplineDataBundle splineData = (Interpolator1DCubicSplineDataBundle) data;
final int low = data.getLowerBoundIndex(value);
final int high = low + 1;
final int n = data.size() - 1;
final double[] xData = data.getKeys();
final double[] yData = data.getValues();
if (data.getLowerBoundIndex(value) == n) {
return yData[n];
}
final double delta = xData[high] - xData[low];
if (Math.abs(delta) < _eps) {
throw new MathException("x data points were not distinct");
}
final double a = (xData[high] - value) / delta;
final double b = (value - xData[low]) / delta;
final double[] y2 = splineData.getSecondDerivatives();
return a * yData[low] + b * yData[high] + (a * (a * a - 1) * y2[low] + b * (b * b - 1) * y2[high]) * delta * delta / 6.;
}
@Override
public double firstDerivative(final Interpolator1DDataBundle data, final Double value) {
Validate.notNull(value, "value");
Validate.notNull(data, "data bundle");
Validate.isTrue(data instanceof Interpolator1DCubicSplineDataBundle);
Interpolator1DCubicSplineDataBundle splineData = (Interpolator1DCubicSplineDataBundle) data;
int low = data.getLowerBoundIndex(value);
int high = low + 1;
final int n = data.size() - 1;
final double[] xData = data.getKeys();
final double[] yData = data.getValues();
if (low == n) {
low = n - 1;
high = n;
}
// if (data.getLowerBoundIndex(value) == n) {
// return yData[n];
// }
final double delta = xData[high] - xData[low];
if (Math.abs(delta) < _eps) {
throw new MathException("x data points were not distinct");
}
final double a = (xData[high] - value) / delta;
final double b = (value - xData[low]) / delta;
final double[] y2 = splineData.getSecondDerivatives();
return (yData[high] - yData[low]) / delta + ((-3. * a * a + 1.) * y2[low] + (3. * b * b - 1.) * y2[high]) * delta / 6.;
}
@Override
public double[] getNodeSensitivitiesForValue(final Interpolator1DDataBundle data, final Double value) {
Validate.notNull(data, "data");
Validate.isTrue(data instanceof Interpolator1DCubicSplineDataBundle);
Interpolator1DCubicSplineDataBundle cubicData = (Interpolator1DCubicSplineDataBundle) data;
final int n = cubicData.size();
final double[] result = new double[n];
if (cubicData.getLowerBoundIndex(value) == n - 1) {
result[n - 1] = 1.0;
return result;
}
final double[] xData = cubicData.getKeys();
final int low = cubicData.getLowerBoundIndex(value);
final int high = low + 1;
final double delta = xData[high] - xData[low];
final double a = (xData[high] - value) / delta;
final double b = (value - xData[low]) / delta;
final double c = a * (a * a - 1) * delta * delta / 6.;
final double d = b * (b * b - 1) * delta * delta / 6.;
final double[][] y2Sensitivities = cubicData.getSecondDerivativesSensitivities();
for (int i = 0; i < n; i++) {
result[i] = c * y2Sensitivities[low][i] + d * y2Sensitivities[high][i];
}
result[low] += a;
result[high] += b;
return result;
}
@Override
public Interpolator1DDataBundle getDataBundle(final double[] x, final double[] y) {
return new Interpolator1DCubicSplineDataBundle(new ArrayInterpolator1DDataBundle(x, y));
}
@Override
public Interpolator1DDataBundle getDataBundleFromSortedArrays(final double[] x, final double[] y) {
return new Interpolator1DCubicSplineDataBundle(new ArrayInterpolator1DDataBundle(x, y, true));
}
}