class ForexOptionDigitalBlackSmileMethod {

  public MultipleCurrencyMulticurveSensitivity presentValueCurveSensitivity(final ForexOptionDigital optionForex, final BlackForexSmileProviderInterface smileMulticurves) {
    ArgumentChecker.notNull(optionForex, "Forex option");
    ArgumentChecker.notNull(smileMulticurves, "Smile");
    ArgumentChecker.isTrue(smileMulticurves.checkCurrencies(optionForex.getCurrency1(), optionForex.getCurrency2()), "Option currencies not compatible with smile data");
    final MulticurveProviderInterface multicurves = smileMulticurves.getMulticurveProvider();
    final double payTime = optionForex.getUnderlyingForex().getPaymentTime();
    final double expiry = optionForex.getExpirationTime();
    // Forward sweep
    final Currency domesticCcy;
    final Currency foreignCcy;
    final double strike;
    final double amount;
    final double omega;
    if (optionForex.payDomestic()) {
      domesticCcy = optionForex.getUnderlyingForex().getCurrency2();
      foreignCcy = optionForex.getUnderlyingForex().getCurrency1();
      strike = optionForex.getStrike();
      amount = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency2().getAmount());
      omega = optionForex.isCall() ? 1.0 : -1.0;
    } else {
      strike = 1.0 / optionForex.getStrike();
      domesticCcy = optionForex.getUnderlyingForex().getCurrency1();
      foreignCcy = optionForex.getUnderlyingForex().getCurrency2();
      amount = Math.abs(optionForex.getUnderlyingForex().getPaymentCurrency1().getAmount());
      omega = optionForex.isCall() ? -1.0 : 1.0;
    }
    final double dfDomestic = multicurves.getDiscountFactor(domesticCcy, payTime);
    final double dfForeign = multicurves.getDiscountFactor(foreignCcy, payTime);
    final double spot = multicurves.getFxRate(foreignCcy, domesticCcy);
    final double forward = spot * dfForeign / dfDomestic;
    final double volatility = smileMulticurves.getVolatility(foreignCcy, domesticCcy, optionForex.getExpirationTime(), strike, forward);
    final double sigmaRootT = volatility * Math.sqrt(expiry);
    final double dM = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT;
    final double pv = amount * dfDomestic * NORMAL.getCDF(omega * dM) * (optionForex.isLong() ? 1.0 : -1.0);
    // Backward sweep
    final double pvBar = 1.0;
    final double dMBar = amount * dfDomestic * NORMAL.getPDF(omega * dM) * (optionForex.isLong() ? 1.0 : -1.0) * omega * pvBar;
    final double forwardBar = 1 / (forward * sigmaRootT) * dMBar;
    final double dfForeignBar = spot / dfDomestic * forwardBar;
    final double dfDomesticBar = -spot / (dfDomestic * dfDomestic) * dfForeign * forwardBar + pv / dfDomestic * pvBar;
    final double rForeignBar = -payTime * dfForeign * dfForeignBar;
    final double rDomesticBar = -payTime * dfDomestic * dfDomesticBar;
    // Sensitivity object
    final Map<String, List<DoublesPair>> resultMap = new HashMap<>();
    final List<DoublesPair> listForeign = new ArrayList<>();
    listForeign.add(DoublesPair.of(payTime, rForeignBar));
    resultMap.put(multicurves.getName(foreignCcy), listForeign);
    final List<DoublesPair> listDomestic = new ArrayList<>();
    listDomestic.add(DoublesPair.of(payTime, rDomesticBar));
    resultMap.put(multicurves.getName(domesticCcy), listDomestic);
    final MulticurveSensitivity result = MulticurveSensitivity.ofYieldDiscounting(resultMap);
    return MultipleCurrencyMulticurveSensitivity.of(domesticCcy, result);
  }

  private  ForexOptionDigitalBlackSmileMethod();

}