@Override
 protected double doFirstDerivative(double xValue) {
   int lowerIndex = lowerBoundIndex(xValue, xValues);
   int higherIndex = lowerIndex + 1;
   // at start of curve, or only one interval
   if (lowerIndex == 0 || intervalCount == 1) {
     RealPolynomialFunction1D quadraticFirstDerivative = quadraticsFirstDerivative[0];
     double x = xValue - xValues[1];
     return quadraticFirstDerivative.applyAsDouble(x);
   }
   // at end of curve
   if (higherIndex >= intervalCount) {
     RealPolynomialFunction1D quadraticFirstDerivative =
         quadraticsFirstDerivative[intervalCount - 2];
     double x = xValue - xValues[intervalCount - 1];
     return quadraticFirstDerivative.applyAsDouble(x);
   }
   RealPolynomialFunction1D quadratic1 = quadratics[lowerIndex - 1];
   RealPolynomialFunction1D quadratic2 = quadratics[higherIndex - 1];
   RealPolynomialFunction1D quadratic1FirstDerivative =
       quadraticsFirstDerivative[lowerIndex - 1];
   RealPolynomialFunction1D quadratic2FirstDerivative =
       quadraticsFirstDerivative[higherIndex - 1];
   double w =
       WEIGHT_FUNCTION.getWeight(
           (xValues[higherIndex] - xValue) / (xValues[higherIndex] - xValues[lowerIndex]));
   return w * quadratic1FirstDerivative.applyAsDouble(xValue - xValues[lowerIndex])
       + (1 - w) * quadratic2FirstDerivative.applyAsDouble(xValue - xValues[higherIndex])
       + (quadratic2.applyAsDouble(xValue - xValues[higherIndex])
               - quadratic1.applyAsDouble(xValue - xValues[lowerIndex]))
           / (xValues[higherIndex] - xValues[lowerIndex]);
 }
 // -------------------------------------------------------------------------
 @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);
   int higherIndex = lowerIndex + 1;
   // at start of curve
   if (lowerIndex == 0) {
     RealPolynomialFunction1D quadratic = quadratics[0];
     double x = xValue - xValues[1];
     return quadratic.applyAsDouble(x);
   }
   // at end of curve
   if (higherIndex == intervalCount) {
     RealPolynomialFunction1D quadratic = quadratics[intervalCount - 2];
     double x = xValue - xValues[intervalCount - 1];
     return quadratic.applyAsDouble(x);
   }
   // normal case
   RealPolynomialFunction1D quadratic1 = quadratics[lowerIndex - 1];
   RealPolynomialFunction1D quadratic2 = quadratics[higherIndex - 1];
   double w =
       WEIGHT_FUNCTION.getWeight(
           (xValues[higherIndex] - xValue) / (xValues[higherIndex] - xValues[lowerIndex]));
   return w * quadratic1.applyAsDouble(xValue - xValues[lowerIndex])
       + (1 - w) * quadratic2.applyAsDouble(xValue - xValues[higherIndex]);
 }
 @Override
 protected DoubleArray doParameterSensitivity(double xValue) {
   int lowerIndex = lowerBoundIndex(xValue, xValues);
   int higherIndex = lowerIndex + 1;
   int n = xValues.length;
   double[] result = new double[n];
   // at start of curve
   if (lowerIndex == 0) {
     double[] temp = quadraticSensitivities(xValues, xValue, 1);
     result[0] = temp[0];
     result[1] = temp[1];
     result[2] = temp[2];
     return DoubleArray.ofUnsafe(result);
   }
   // at end of curve
   if (higherIndex == intervalCount) {
     double[] temp = quadraticSensitivities(xValues, xValue, n - 2);
     result[n - 3] = temp[0];
     result[n - 2] = temp[1];
     result[n - 1] = temp[2];
     return DoubleArray.ofUnsafe(result);
   }
   // at last node
   if (lowerIndex == intervalCount) {
     result[n - 1] = 1;
     return DoubleArray.ofUnsafe(result);
   }
   double[] temp1 = quadraticSensitivities(xValues, xValue, lowerIndex);
   double[] temp2 = quadraticSensitivities(xValues, xValue, higherIndex);
   double w =
       WEIGHT_FUNCTION.getWeight(
           (xValues[higherIndex] - xValue) / (xValues[higherIndex] - xValues[lowerIndex]));
   result[lowerIndex - 1] = w * temp1[0];
   result[lowerIndex] = w * temp1[1] + (1 - w) * temp2[0];
   result[higherIndex] = w * temp1[2] + (1 - w) * temp2[1];
   result[higherIndex + 1] = (1 - w) * temp2[2];
   return DoubleArray.ofUnsafe(result);
 }