class CapFloorIborLMMDDMethod {

  public MultipleCurrencyAmount presentValue(final CapFloorIbor cap, final LiborMarketModelDisplacedDiffusionProviderInterface lmmData) {
    ArgumentChecker.notNull(cap, "The cap/floor shoud not be null");
    ArgumentChecker.notNull(lmmData, "LMM provider");
    Currency ccy = cap.getCurrency();
    MulticurveProviderInterface multicurves = lmmData.getMulticurveProvider();
    LiborMarketModelDisplacedDiffusionParameters parameters = lmmData.getLMMParameters();
    int index = lmmData.getLMMParameters().getTimeIndex(cap.getFixingPeriodStartTime());
    double volatility = 0;
    for (int loopfact = 0; loopfact < lmmData.getLMMParameters().getNbFactor(); loopfact++) {
      volatility += parameters.getVolatility()[index][loopfact] * parameters.getVolatility()[index][loopfact];
    }
    volatility = Math.sqrt(volatility);
    double timeDependentFactor = Math.sqrt((Math.exp(2 * parameters.getMeanReversion() * cap.getFixingTime()) - 1.0) / (2.0 * parameters.getMeanReversion()));
    volatility *= timeDependentFactor;
    double displacement = parameters.getDisplacement()[index];
    double forward = multicurves.getSimplyCompoundForwardRate(cap.getIndex(), cap.getFixingPeriodStartTime(), cap.getFixingPeriodEndTime(), cap.getFixingAccrualFactor());
    double beta = (1.0 + cap.getFixingAccrualFactor() * forward) * multicurves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime())
        / multicurves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime());
    double strikeAdjusted = (cap.getStrike() - (beta - 1) / cap.getFixingAccrualFactor()) / beta;
    EuropeanVanillaOption option = new EuropeanVanillaOption(strikeAdjusted + displacement, 1.0, cap.isCap()); // Time is in timeDependentFactor
    double forwardDsc = (multicurves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime()) / multicurves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime()) - 1.0) / cap.getFixingAccrualFactor();
    final double df = multicurves.getDiscountFactor(ccy, cap.getPaymentTime());
    final BlackFunctionData dataBlack = new BlackFunctionData(forwardDsc + displacement, df, volatility);
    final Function1D<BlackFunctionData, Double> func = BLACK_FUNCTION.getPriceFunction(option);
    final double price = beta * func.evaluate(dataBlack) * cap.getNotional() * cap.getPaymentYearFraction();
    return MultipleCurrencyAmount.of(cap.getCurrency(), price);
  }

  private  CapFloorIborLMMDDMethod();

}