@Test
  public void firstDerivativeTest() {
    double a = 1.0;
    double b = 1.5;
    double c = -0.5;
    double[] x = new double[] {0., 2., 5.};
    int n = x.length;
    double[] y = new double[n];
    for (int i = 0; i < n; i++) {
      y[i] = a + b * x[i] + c * x[i] * x[i];
    }
    Interpolator1D interpolator = new NaturalCubicSplineInterpolator1D();
    Interpolator1DDataBundle db = interpolator.getDataBundle(x, y);
    Double grad = interpolator.firstDerivative(db, x[n - 1]);
    Function1D<Double, Double> func = interpolator.getFunction(db);
    ScalarFirstOrderDifferentiator diff = new ScalarFirstOrderDifferentiator();
    Function1D<Double, Boolean> domain =
        new Function1D<Double, Boolean>() {
          @Override
          public Boolean evaluate(Double x) {
            return x <= 5.0;
          }
        };
    Function1D<Double, Double> gradFunc = diff.differentiate(func, domain);

    assertEquals(gradFunc.evaluate(x[n - 1]), grad, 1e-8);
  }
  /**
   * Calculates the present value of the swaption product.
   *
   * <p>The result is expressed using the currency of the swapion.
   *
   * @param swaption the product to price
   * @param ratesProvider the rates provider
   * @param volatilityProvider the normal volatility provider
   * @return the present value of the swaption product
   */
  public CurrencyAmount presentValue(
      SwaptionProduct swaption,
      RatesProvider ratesProvider,
      NormalVolatilitySwaptionProvider volatilityProvider) {

    ExpandedSwaption expanded = swaption.expand();
    validate(ratesProvider, expanded, volatilityProvider);
    ZonedDateTime expiryDateTime = expanded.getExpiryDateTime();
    double expiry = volatilityProvider.relativeTime(expiryDateTime);
    ExpandedSwap underlying = expanded.getUnderlying();
    ExpandedSwapLeg fixedLeg = fixedLeg(underlying);
    if (expiry < 0.0d) { // Option has expired already
      return CurrencyAmount.of(fixedLeg.getCurrency(), 0.0d);
    }
    double forward = swapPricer.parRate(underlying, ratesProvider);
    double pvbp = swapPricer.getLegPricer().pvbp(fixedLeg, ratesProvider);
    double strike = swapPricer.getLegPricer().couponEquivalent(fixedLeg, ratesProvider, pvbp);
    double tenor = volatilityProvider.tenor(fixedLeg.getStartDate(), fixedLeg.getEndDate());
    double volatility = volatilityProvider.getVolatility(expiryDateTime, tenor, strike, forward);
    NormalFunctionData normalData = NormalFunctionData.of(forward, Math.abs(pvbp), volatility);
    boolean isCall = (fixedLeg.getPayReceive() == PayReceive.PAY);
    // Payer at strike is exercise when rate > strike, i.e. call on rate
    EuropeanVanillaOption option =
        EuropeanVanillaOption.of(strike, expiry, isCall ? PutCall.CALL : PutCall.PUT);
    // option required to pass the strike (in case the swap has non-constant coupon).
    Function1D<NormalFunctionData, Double> func = NORMAL.getPriceFunction(option);
    double pv =
        func.evaluate(normalData) * ((expanded.getLongShort() == LongShort.LONG) ? 1.0 : -1.0);
    return CurrencyAmount.of(fixedLeg.getCurrency(), pv);
  }
 static {
   final double[] t =
       new double[] {0.0, 0.5, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0, 15.0, 17.5, 20.0, 25.0, 30.0};
   final int n = t.length;
   final double[] r = new double[n];
   for (int i = 0; i < n; i++) {
     r[i] = FUNCTION.evaluate(t[i]);
   }
   DATA = new Interpolator1DCubicSplineDataBundle(new ArrayInterpolator1DDataBundle(t, r));
 }