class CapFloorIborHullWhiteMethod {

  public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final CapFloorIbor cap, final HullWhiteOneFactorProviderInterface hullWhite) {
    ArgumentChecker.notNull(cap, "The cap/floor shoud not be null");
    ArgumentChecker.notNull(hullWhite, "The Hull-White data shoud not be null");
    final Currency ccy = cap.getCurrency();
    final HullWhiteOneFactorPiecewiseConstantParameters parameters = hullWhite.getHullWhiteParameters();
    final MulticurveProviderInterface multicurves = hullWhite.getMulticurveProvider();
    final double tp = cap.getPaymentTime();
    final double t0 = cap.getFixingPeriodStartTime();
    final double t1 = cap.getFixingPeriodEndTime();
    final double deltaF = cap.getFixingAccrualFactor();
    final double deltaP = cap.getPaymentYearFraction();
    final double k = cap.getStrike();
    final double omega = (cap.isCap() ? 1.0 : -1.0);
    // Forward sweep
    final double dfPay = multicurves.getDiscountFactor(ccy, tp);
    final double forward = multicurves.getSimplyCompoundForwardRate(cap.getIndex(), t0, t1, deltaF);
    final double alpha0 = _model.alpha(parameters, 0.0, cap.getFixingTime(), tp, t0);
    final double alpha1 = _model.alpha(parameters, 0.0, cap.getFixingTime(), tp, t1);
    final double kappa = (Math.log((1 + deltaF * k) / (1.0 + deltaF * forward)) - (alpha1 * alpha1 - alpha0 * alpha0) / 2.0) / (alpha1 - alpha0);
    final double n0 = NORMAL.getCDF(omega * (-kappa - alpha0));
    final double n1 = NORMAL.getCDF(omega * (-kappa - alpha1));
    //    double pv = deltaP / deltaF * dfPay * omega * ((1.0 + deltaF * forward) * n0 - (1.0 + deltaF * k) * n1) * cap.getNotional();
    // Backward sweep
    final double pvBar = 1.0;
    //    double kappaBar = 0.0; // kappa is the optimal exercise boundary
    final double forwardBar = deltaP / deltaF * dfPay * omega * deltaF * n0 * cap.getNotional() * pvBar;
    final double dfPayBar = deltaP / deltaF * omega * ((1.0 + deltaF * forward) * n0 - (1.0 + deltaF * k) * n1) * cap.getNotional() * pvBar;

    final Map<String, List<DoublesPair>> mapDsc = new HashMap<>();
    final List<DoublesPair> listDiscounting = new ArrayList<>();
    listDiscounting.add(DoublesPair.of(cap.getPaymentTime(), -cap.getPaymentTime() * dfPay * dfPayBar));
    mapDsc.put(multicurves.getName(ccy), listDiscounting);

    final Map<String, List<ForwardSensitivity>> mapFwd = new HashMap<>();
    final List<ForwardSensitivity> listForward = new ArrayList<>();
    listForward.add(new SimplyCompoundedForwardSensitivity(cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor(), forwardBar));
    mapFwd.put(multicurves.getName(cap.getIndex()), listForward);

    return MultipleCurrencyMulticurveSensitivity.of(ccy, MulticurveSensitivity.of(mapDsc, mapFwd));
  }

  private  CapFloorIborHullWhiteMethod();

}