class BlackFormulaRepository {

  public static double price(
      double forward,
      double strike,
      double timeToExpiry,
      double lognormalVol,
      boolean isCall) {

    ArgChecker.isTrue(forward >= 0d, "negative/NaN forward; have {}", forward);
    ArgChecker.isTrue(strike >= 0d, "negative/NaN strike; have {}", strike);
    ArgChecker.isTrue(timeToExpiry >= 0d, "negative/NaN timeToExpiry; have {}", timeToExpiry);
    ArgChecker.isTrue(lognormalVol >= 0d, "negative/NaN lognormalVol; have {}", lognormalVol);

    double sigmaRootT = lognormalVol * Math.sqrt(timeToExpiry);
    if (Double.isNaN(sigmaRootT)) {
      log.info("lognormalVol * Math.sqrt(timeToExpiry) ambiguous");
      sigmaRootT = 1d;
    }
    int sign = isCall ? 1 : -1;
    boolean bFwd = (forward > LARGE);
    boolean bStr = (strike > LARGE);
    boolean bSigRt = (sigmaRootT > LARGE);
    double d1 = 0d;
    double d2 = 0d;

    if (bFwd && bStr) {
      log.info("(large value)/(large value) ambiguous");
      return isCall ? (forward >= strike ? forward : 0d) : (strike >= forward ? strike : 0d);
    }
    if (sigmaRootT < SMALL) {
      return Math.max(sign * (forward - strike), 0d);
    }
    if (Math.abs(forward - strike) < SMALL || bSigRt) {
      d1 = 0.5 * sigmaRootT;
      d2 = -0.5 * sigmaRootT;
    } else {
      d1 = Math.log(forward / strike) / sigmaRootT + 0.5 * sigmaRootT;
      d2 = d1 - sigmaRootT;
    }

    double nF = NORMAL.getCDF(sign * d1);
    double nS = NORMAL.getCDF(sign * d2);
    double first = nF == 0d ? 0d : forward * nF;
    double second = nS == 0d ? 0d : strike * nS;

    double res = sign * (first - second);
    return Math.max(0., res);
  }

  private  BlackFormulaRepository();

}