// -------------------------------------------------------------------------
 @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 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]);
 }