class BlackFormulaRepository {

  public static double driftlessTheta(double forward, double strike, double timeToExpiry, double lognormalVol) {
    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 rootT = Math.sqrt(timeToExpiry);
    double sigmaRootT = lognormalVol * rootT;
    if (Double.isNaN(sigmaRootT)) {
      log.info("lognormalVol * Math.sqrt(timeToExpiry) ambiguous");
      sigmaRootT = 1d;
    }

    boolean bFwd = (forward > LARGE);
    boolean bStr = (strike > LARGE);
    boolean bSigRt = (sigmaRootT > LARGE);
    double d1 = 0d;

    if (bSigRt) {
      return 0d;
    }
    if (sigmaRootT < SMALL) {
      if (Math.abs(forward - strike) >= SMALL && !(bFwd && bStr)) {
        return 0d;
      }
      log.info("log(1)/0 ambiguous");
      if (rootT < SMALL) {
        return forward < SMALL ?
            -NORMAL.getPDF(0d) * lognormalVol / 2. :
            (lognormalVol < SMALL ?
                -forward * NORMAL.getPDF(0d) / 2. :
                -forward * NORMAL.getPDF(0d) * lognormalVol / 2. / rootT);
      }
      if (lognormalVol < SMALL) {
        return bFwd ? -NORMAL.getPDF(0d) / 2. / rootT : -forward * NORMAL.getPDF(0d) * lognormalVol / 2. / rootT;
      }
    }
    if (Math.abs(forward - strike) < SMALL | (bFwd && bStr)) {
      d1 = 0.5 * sigmaRootT;
    } else {
      d1 = Math.log(forward / strike) / sigmaRootT + 0.5 * sigmaRootT;
    }

    double nVal = NORMAL.getPDF(d1);
    return nVal == 0d ? 0d : -forward * nVal * lognormalVol / 2. / rootT;
  }

  private  BlackFormulaRepository();

}