class BlackScholesFormulaRepository {

  public static double gamma(
      double spot,
      double strike,
      double timeToExpiry,
      double lognormalVol,
      double interestRate,
      double costOfCarry) {

    ArgChecker.isTrue(spot >= 0d, "negative/NaN spot; have {}", spot);
    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);
    ArgChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN");
    ArgChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN");

    double coef = 0d;
    if ((interestRate > LARGE && costOfCarry > LARGE) || (-interestRate > LARGE && -costOfCarry > LARGE) ||
        Math.abs(costOfCarry - interestRate) < SMALL) {
      coef = 1d; //ref value is returned
    } else {
      double rate = costOfCarry - interestRate;
      if (rate > LARGE) {
        return costOfCarry > LARGE ? 0d : Double.POSITIVE_INFINITY;
      }
      if (-rate > LARGE) {
        return 0d;
      }
      coef = Math.exp(rate * timeToExpiry);
    }

    double rootT = Math.sqrt(timeToExpiry);
    double sigmaRootT = lognormalVol * rootT;
    if (Double.isNaN(sigmaRootT)) {
      sigmaRootT = 1d; //ref value is returned
    }
    if (spot > LARGE * strike || spot < SMALL * strike || sigmaRootT > LARGE) {
      return 0d;
    }

    double factor = Math.exp(costOfCarry * timeToExpiry);
    if (Double.isNaN(factor)) {
      factor = 1d; //ref value is returned
    }

    double d1 = 0d;
    if (Math.abs(spot - strike) < SMALL || (spot > LARGE && strike > LARGE)) {
      double coefD1 = (Math.abs(costOfCarry) < SMALL && lognormalVol < SMALL) ?
          Math.signum(costOfCarry) + 0.5 * lognormalVol :
          (costOfCarry / lognormalVol + 0.5 * lognormalVol);
      double tmp = coefD1 * rootT;
      d1 = Double.isNaN(tmp) ? 0d : tmp;
    } else {
      if (sigmaRootT < SMALL) {
        double scnd = (Math.abs(costOfCarry) > LARGE && rootT < SMALL) ? Math.signum(costOfCarry) : costOfCarry * rootT;
        double tmp = (Math.log(spot / strike) / rootT + scnd) / lognormalVol;
        d1 = Double.isNaN(tmp) ? 0d : tmp;
      } else {
        double tmp = costOfCarry * rootT / lognormalVol;
        double sig = (costOfCarry >= 0d) ? 1d : -1d;
        double scnd =
            Double.isNaN(tmp) ? ((lognormalVol < LARGE && lognormalVol > SMALL) ? sig / lognormalVol : sig * rootT) : tmp;
        d1 = Math.log(spot / strike) / sigmaRootT + scnd + 0.5 * sigmaRootT;
      }
    }
    double norm = NORMAL.getPDF(d1);

    double res = norm < SMALL ? 0d : coef * norm / spot / sigmaRootT;
    return Double.isNaN(res) ? Double.POSITIVE_INFINITY : res;
  }

  private  BlackScholesFormulaRepository();

}