/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.market.curve.interpolator;
import java.io.Serializable;
import com.opengamma.strata.collect.array.DoubleArray;
/**
* Interpolator implementation that returns the log linearly interpolated value.
* <p>
* The interpolated value of the function <i>y</i> at <i>x</i> between two data points
* <i>(x<sub>1</sub>, y<sub>1</sub>)</i> and <i>(x<sub>2</sub>, y<sub>2</sub>)</i> is given by:<br>
* <i>y = y<sub>1</sub> (y<sub>2</sub> / y<sub>1</sub>) ^ ((x - x<sub>1</sub>) /
* (x<sub>2</sub> - x<sub>1</sub>))</i><br>
* It is the equivalent of performing a linear interpolation on a data set after
* taking the logarithm of the y-values.
*/
final class LogLinearCurveInterpolator
implements CurveInterpolator, Serializable {
/**
* The serialization version id.
*/
private static final long serialVersionUID = 1L;
/**
* The interpolator name.
*/
public static final String NAME = "LogLinear";
/**
* The interpolator instance.
*/
public static final CurveInterpolator INSTANCE = new LogLinearCurveInterpolator();
/**
* Restricted constructor.
*/
private LogLinearCurveInterpolator() {
}
// resolve instance
private Object readResolve() {
return INSTANCE;
}
//-------------------------------------------------------------------------
@Override
public String getName() {
return NAME;
}
@Override
public BoundCurveInterpolator bind(DoubleArray xValues, DoubleArray yValues) {
return new Bound(xValues, yValues);
}
//-----------------------------------------------------------------------
@Override
public String toString() {
return NAME;
}
//-------------------------------------------------------------------------
/**
* Bound interpolator.
*/
static class Bound extends AbstractBoundCurveInterpolator {
private final double[] xValues;
private final double[] yValues;
private final int intervalCount;
Bound(DoubleArray xValues, DoubleArray yValues) {
super(xValues, yValues);
this.xValues = xValues.toArrayUnsafe();
this.yValues = yValues.toArrayUnsafe();
this.intervalCount = xValues.size() - 1;
}
Bound(Bound base, BoundCurveExtrapolator extrapolatorLeft, BoundCurveExtrapolator extrapolatorRight) {
super(base, extrapolatorLeft, extrapolatorRight);
this.xValues = base.xValues;
this.yValues = base.yValues;
this.intervalCount = base.intervalCount;
}
//-------------------------------------------------------------------------
@Override
protected double doInterpolate(double xValue) {
// x-value is less than the x-value of the last node (lowerIndex < intervalCount)
int lowerIndex = lowerBoundIndex(xValue, xValues);
double x1 = xValues[lowerIndex];
double x2 = xValues[lowerIndex + 1];
double y1 = yValues[lowerIndex];
double y2 = yValues[lowerIndex + 1];
return Math.pow(y2 / y1, (xValue - x1) / (x2 - x1)) * y1;
}
@Override
protected double doFirstDerivative(double xValue) {
int lowerIndex = lowerBoundIndex(xValue, xValues);
// check if x-value is at the last node
if (lowerIndex == intervalCount) {
// if value is at last node, calculate the gradient from the previous interval
double x1 = xValues[lowerIndex - 1];
double x2 = xValues[lowerIndex];
double y1 = yValues[lowerIndex - 1];
double y2 = yValues[lowerIndex];
return y2 * Math.log(y2 / y1) / (x2 - x1);
}
double x1 = xValues[lowerIndex];
double x2 = xValues[lowerIndex + 1];
double y1 = yValues[lowerIndex];
double y2 = yValues[lowerIndex + 1];
double yDiv = y2 / y1;
double xDiff = (x2 - x1);
return Math.pow(yDiv, (xValue - x1) / xDiff) * y1 * Math.log(yDiv) / xDiff;
}
@Override
protected DoubleArray doParameterSensitivity(double xValue) {
double[] result = new double[yValues.length];
int lowerIndex = lowerBoundIndex(xValue, xValues);
// check if x-value is at the last node
if (lowerIndex == intervalCount) {
// sensitivity is entirely to the last node
result[intervalCount] = 1d;
} else {
double x1 = xValues[lowerIndex];
double x2 = xValues[lowerIndex + 1];
double y1 = yValues[lowerIndex];
double y2 = yValues[lowerIndex + 1];
double diffInv = 1.0 / (x2 - x1);
double x1diffInv = (xValue - x1) * diffInv;
double x2diffInv = (x2 - xValue) * diffInv;
double yDiv = y1 / y2;
result[lowerIndex] = Math.pow(yDiv, -x1diffInv) * x2diffInv;
result[lowerIndex + 1] = Math.pow(yDiv, x2diffInv) * x1diffInv;
}
return DoubleArray.ofUnsafe(result);
}
@Override
public BoundCurveInterpolator bind(
BoundCurveExtrapolator extrapolatorLeft,
BoundCurveExtrapolator extrapolatorRight) {
return new Bound(this, extrapolatorLeft, extrapolatorRight);
}
}
}