/**
  * Compute the present value of a CMS cap/floor by replication in SABR framework with
  * extrapolation on the right.
  *
  * @param cmsCapFloor The CMS cap/floor.
  * @param sabrData The SABR data bundle.
  * @return The present value.
  */
 @Override
 public CurrencyAmount presentValue(
     final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
   Validate.notNull(cmsCapFloor);
   Validate.notNull(sabrData);
   final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
   final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
   final double forward = underlyingSwap.accept(PRC, sabrData);
   final double discountFactorTp =
       sabrData
           .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
           .getDiscountFactor(cmsCapFloor.getPaymentTime());
   final double maturity =
       underlyingSwap
               .getFixedLeg()
               .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1)
               .getPaymentTime()
           - cmsCapFloor.getSettlementTime();
   final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity);
   final double alpha = sabrParameter.getAlpha(expiryMaturity);
   final double beta = sabrParameter.getBeta(expiryMaturity);
   final double rho = sabrParameter.getRho(expiryMaturity);
   final double nu = sabrParameter.getNu(expiryMaturity);
   final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu);
   final CMSIntegrant integrant =
       new CMSIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double strike = cmsCapFloor.getStrike();
   final double factor = discountFactorTp / integrant.h(forward) * integrant.g(forward);
   final double strikePart = factor * integrant.k(strike) * integrant.bs(strike);
   final double absoluteTolerance =
       1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction());
   final double relativeTolerance = 1E-10;
   final RungeKuttaIntegrator1D integrator =
       new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
   double integralPart;
   try {
     if (cmsCapFloor.isCap()) {
       integralPart =
           discountFactorTp
               * integrator.integrate(integrant, strike, strike + getIntegrationInterval());
     } else {
       integralPart = discountFactorTp * integrator.integrate(integrant, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double priceCMS =
       (strikePart + integralPart)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   return CurrencyAmount.of(cmsCapFloor.getCurrency(), priceCMS);
 }
  /**
   * Computes the present value sensitivity to the strike of a CMS cap/floor by replication in SABR
   * framework with extrapolation on the right.
   *
   * @param cmsCapFloor The CMS cap/floor.
   * @param sabrData The SABR data bundle. The SABR function need to be the Hagan function.
   * @return The present value sensitivity to strike.
   */
  @Override
  public double presentValueStrikeSensitivity(
      final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
    final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
    final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
    final double forward = underlyingSwap.accept(PRC, sabrData);
    final double discountFactor =
        sabrData
            .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
            .getDiscountFactor(cmsCapFloor.getPaymentTime());
    final double strike = cmsCapFloor.getStrike();
    final double maturity =
        underlyingSwap
                .getFixedLeg()
                .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1)
                .getPaymentTime()
            - cmsCapFloor.getSettlementTime();
    final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity);
    final double alpha = sabrParameter.getAlpha(expiryMaturity);
    final double beta = sabrParameter.getBeta(expiryMaturity);
    final double rho = sabrParameter.getRho(expiryMaturity);
    final double nu = sabrParameter.getNu(expiryMaturity);
    final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu);
    final CMSStrikeIntegrant integrant =
        new CMSStrikeIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
    final double factor = discountFactor * integrant.g(forward) / integrant.h(forward);
    final double absoluteTolerance = 1.0E-9;
    final double relativeTolerance = 1.0E-5;
    final RungeKuttaIntegrator1D integrator =
        new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
    final SABRExtrapolationRightFunction sabrExtrapolation =
        new SABRExtrapolationRightFunction(
            forward, sabrPoint, _cutOffStrike, cmsCapFloor.getFixingTime(), _mu);
    final EuropeanVanillaOption option =
        new EuropeanVanillaOption(strike, cmsCapFloor.getFixingTime(), cmsCapFloor.isCap());
    final double[] kpkpp = integrant.kpkpp(strike);
    double firstPart;
    double thirdPart;
    if (cmsCapFloor.isCap()) {
      firstPart = -kpkpp[0] * integrant.bs(strike);
      thirdPart = integrator.integrate(integrant, strike, strike + getIntegrationInterval());
    } else {
      firstPart = 3 * kpkpp[0] * integrant.bs(strike);
      thirdPart = integrator.integrate(integrant, 0.0, strike);
    }
    final double secondPart = integrant.k(strike) * sabrExtrapolation.priceDerivativeStrike(option);

    return cmsCapFloor.getNotional()
        * cmsCapFloor.getPaymentYearFraction()
        * factor
        * (firstPart + secondPart + thirdPart);
  }
 /**
  * Computes the par rate of a swap with one fixed leg.
  *
  * @param swap The Fixed coupon swap.
  * @param curves The curves.
  * @return The par swap rate. If the fixed leg has been set up with some fixed payments these are
  *     ignored for the purposes of finding the swap rate
  */
 @Override
 public Double visitFixedCouponSwap(final SwapFixedCoupon<?> swap, final YieldCurveBundle curves) {
   final double pvSecond = swap.getSecondLeg().accept(PVC, curves);
   final double pvbp = swap.getFixedLeg().withUnitCoupon().accept(PVC, curves);
   return -pvSecond / pvbp;
 }
 /**
  * Computes the present value sensitivity to the SABR parameters of a CMS cap/floor by replication
  * in SABR framework with extrapolation on the right.
  *
  * @param cmsCapFloor The CMS cap/floor.
  * @param sabrData The SABR data bundle. The SABR function need to be the Hagan function.
  * @return The present value sensitivity to SABR parameters.
  */
 @Override
 public PresentValueSABRSensitivityDataBundle presentValueSABRSensitivity(
     final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
   final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
   final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
   final double forward = underlyingSwap.accept(PRC, sabrData);
   final double discountFactorTp =
       sabrData
           .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
           .getDiscountFactor(cmsCapFloor.getPaymentTime());
   final double strike = cmsCapFloor.getStrike();
   final double maturity =
       underlyingSwap
               .getFixedLeg()
               .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1)
               .getPaymentTime()
           - cmsCapFloor.getSettlementTime();
   final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity);
   final double alpha = sabrParameter.getAlpha(expiryMaturity);
   final double beta = sabrParameter.getBeta(expiryMaturity);
   final double rho = sabrParameter.getRho(expiryMaturity);
   final double nu = sabrParameter.getNu(expiryMaturity);
   final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu);
   final CMSVegaIntegrant integrantVega =
       new CMSVegaIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double factor = discountFactorTp / integrantVega.h(forward) * integrantVega.g(forward);
   final SABRExtrapolationRightFunction sabrExtrapolation =
       new SABRExtrapolationRightFunction(
           forward, sabrPoint, _cutOffStrike, cmsCapFloor.getFixingTime(), _mu);
   final EuropeanVanillaOption option =
       new EuropeanVanillaOption(strike, cmsCapFloor.getFixingTime(), cmsCapFloor.isCap());
   final double factor2 = factor * integrantVega.k(strike);
   final double[] strikePartPrice = new double[4];
   sabrExtrapolation.priceAdjointSABR(option, strikePartPrice);
   for (int loopvega = 0; loopvega < 4; loopvega++) {
     strikePartPrice[loopvega] *= factor2;
   }
   final double absoluteTolerance =
       1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction());
   final double relativeTolerance = 1E-3;
   final RungeKuttaIntegrator1D integrator =
       new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
   final double[] integralPart = new double[4];
   final double[] totalSensi = new double[4];
   for (int loopparameter = 0; loopparameter < 4; loopparameter++) {
     integrantVega.setParameterIndex(loopparameter);
     try {
       if (cmsCapFloor.isCap()) {
         integralPart[loopparameter] =
             discountFactorTp
                 * integrator.integrate(integrantVega, strike, strike + getIntegrationInterval());
       } else {
         integralPart[loopparameter] =
             discountFactorTp * integrator.integrate(integrantVega, 0.0, strike);
       }
     } catch (final Exception e) {
       throw new RuntimeException(e);
     }
     totalSensi[loopparameter] =
         (strikePartPrice[loopparameter] + integralPart[loopparameter])
             * cmsCapFloor.getNotional()
             * cmsCapFloor.getPaymentYearFraction();
   }
   final PresentValueSABRSensitivityDataBundle sensi = new PresentValueSABRSensitivityDataBundle();
   sensi.addAlpha(expiryMaturity, totalSensi[0]);
   sensi.addBeta(expiryMaturity, totalSensi[1]);
   sensi.addRho(expiryMaturity, totalSensi[2]);
   sensi.addNu(expiryMaturity, totalSensi[3]);
   return sensi;
 }
 /**
  * Computes the present value sensitivity to the yield curves of a CMS cap/floor by replication in
  * the SABR framework with extrapolation on the right.
  *
  * @param cmsCapFloor The CMS cap/floor.
  * @param sabrData The SABR data bundle. The SABR function need to be the Hagan function.
  * @return The present value sensitivity to curves.
  */
 @Override
 public InterestRateCurveSensitivity presentValueCurveSensitivity(
     final CapFloorCMS cmsCapFloor, final SABRInterestRateDataBundle sabrData) {
   Validate.notNull(cmsCapFloor);
   Validate.notNull(sabrData);
   final SABRInterestRateParameters sabrParameter = sabrData.getSABRParameter();
   final SwapFixedCoupon<? extends Payment> underlyingSwap = cmsCapFloor.getUnderlyingSwap();
   final double forward = underlyingSwap.accept(PRC, sabrData);
   final double discountFactor =
       sabrData
           .getCurve(underlyingSwap.getFixedLeg().getNthPayment(0).getFundingCurveName())
           .getDiscountFactor(cmsCapFloor.getPaymentTime());
   final double strike = cmsCapFloor.getStrike();
   final double maturity =
       underlyingSwap
               .getFixedLeg()
               .getNthPayment(underlyingSwap.getFixedLeg().getNumberOfPayments() - 1)
               .getPaymentTime()
           - cmsCapFloor.getSettlementTime();
   final DoublesPair expiryMaturity = new DoublesPair(cmsCapFloor.getFixingTime(), maturity);
   final double alpha = sabrParameter.getAlpha(expiryMaturity);
   final double beta = sabrParameter.getBeta(expiryMaturity);
   final double rho = sabrParameter.getRho(expiryMaturity);
   final double nu = sabrParameter.getNu(expiryMaturity);
   final SABRFormulaData sabrPoint = new SABRFormulaData(alpha, beta, rho, nu);
   // Common
   final CMSIntegrant integrantPrice =
       new CMSIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final CMSDeltaIntegrant integrantDelta =
       new CMSDeltaIntegrant(cmsCapFloor, sabrPoint, forward, _cutOffStrike, _mu);
   final double factor = discountFactor / integrantDelta.h(forward) * integrantDelta.g(forward);
   final double absoluteTolerance =
       1.0 / (factor * Math.abs(cmsCapFloor.getNotional()) * cmsCapFloor.getPaymentYearFraction());
   final double relativeTolerance = 1E-10;
   final RungeKuttaIntegrator1D integrator =
       new RungeKuttaIntegrator1D(absoluteTolerance, relativeTolerance, getNbIteration());
   // Price
   final double[] bs = integrantDelta.bsbsp(strike);
   @SuppressWarnings("synthetic-access")
   final double[] n = integrantDelta.nnp(forward);
   final double strikePartPrice = discountFactor * integrantDelta.k(strike) * n[0] * bs[0];
   double integralPartPrice;
   try {
     if (cmsCapFloor.isCap()) {
       integralPartPrice =
           discountFactor
               * integrator.integrate(integrantPrice, strike, strike + getIntegrationInterval());
     } else {
       integralPartPrice = discountFactor * integrator.integrate(integrantPrice, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double price =
       (strikePartPrice + integralPartPrice)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   // Delta
   final double strikePart =
       discountFactor * integrantDelta.k(strike) * (n[1] * bs[0] + n[0] * bs[1]);
   double integralPart;
   try {
     if (cmsCapFloor.isCap()) {
       integralPart =
           discountFactor
               * integrator.integrate(integrantDelta, strike, strike + getIntegrationInterval());
     } else {
       integralPart = discountFactor * integrator.integrate(integrantDelta, 0.0, strike);
     }
   } catch (final Exception e) {
     throw new RuntimeException(e);
   }
   final double deltaS0 =
       (strikePart + integralPart)
           * cmsCapFloor.getNotional()
           * cmsCapFloor.getPaymentYearFraction();
   final double deltaPD = price / discountFactor;
   final double sensiDF = -cmsCapFloor.getPaymentTime() * discountFactor * deltaPD;
   final List<DoublesPair> list = new ArrayList<>();
   list.add(new DoublesPair(cmsCapFloor.getPaymentTime(), sensiDF));
   final Map<String, List<DoublesPair>> resultMap = new HashMap<>();
   resultMap.put(
       cmsCapFloor.getUnderlyingSwap().getFixedLeg().getNthPayment(0).getFundingCurveName(), list);
   InterestRateCurveSensitivity result = new InterestRateCurveSensitivity(resultMap);
   final InterestRateCurveSensitivity forwardDr =
       new InterestRateCurveSensitivity(cmsCapFloor.getUnderlyingSwap().accept(PRSC, sabrData));
   result = result.plus(forwardDr.multipliedBy(deltaS0));
   return result;
 }