class BlackBarrierPriceFormulaRepository {

  public double price(
      double spot,
      double strike,
      double timeToExpiry,
      double costOfCarry,
      double rate,
      double lognormalVol,
      boolean isCall,
      SimpleConstantContinuousBarrier barrier) {

    ArgChecker.notNull(barrier, "barrier");
    boolean isKnockIn = barrier.getKnockType().isKnockIn();
    boolean isDown = barrier.getBarrierType().isDown();
    double h = barrier.getBarrierLevel();
    ArgChecker.isFalse(isDown && spot <= barrier.getBarrierLevel(),
        "The Data is not consistent with an alive barrier (DOWN and spot<=barrier).");
    ArgChecker.isFalse(!isDown && spot >= barrier.getBarrierLevel(),
        "The Data is not consistent with an alive barrier (UP and spot>=barrier).");
    int phi = isCall ? 1 : -1;
    double eta = isDown ? 1 : -1;
    double df1 = Math.exp(timeToExpiry * (costOfCarry - rate));
    double df2 = Math.exp(-rate * timeToExpiry);
    double sigmaSq = lognormalVol * lognormalVol;
    double sigmaT = lognormalVol * Math.sqrt(timeToExpiry);
    if (nearZero(Math.min(timeToExpiry, sigmaSq), SMALL)) {
      if (isKnockIn) {
        return 0d;
      }
      double dscFwd = df1 * spot;
      double dscStr = df2 * strike;
      return isCall ? (dscFwd >= dscStr ? dscFwd - dscStr : 0d) : (dscStr >= dscFwd ? dscStr - dscFwd : 0d);
    }
    double mu = (costOfCarry - 0.5 * sigmaSq) / sigmaSq;
    double m1 = sigmaT * (1 + mu);
    double x1 = Math.log(spot / strike) / sigmaT + m1;
    double x2 = Math.log(spot / h) / sigmaT + m1;
    double y1 = Math.log(h * h / spot / strike) / sigmaT + m1;
    double y2 = Math.log(h / spot) / sigmaT + m1;
    double xA = getA(spot, strike, df1, df2, x1, sigmaT, phi);
    double xB = getA(spot, strike, df1, df2, x2, sigmaT, phi);
    double xC = getC(spot, strike, df1, df2, y1, sigmaT, h, mu, phi, eta);
    double xD = getC(spot, strike, df1, df2, y2, sigmaT, h, mu, phi, eta);
    if (isKnockIn) { // KnockIn
      if (isDown) {
        if (isCall) {
          return strike > h ? xC : xA - xB + xD;
        }
        return strike > h ? xB - xC + xD : xA;
      }
      if (isCall) {
        return strike > h ? xA : xB - xC + xD;
      }
      return strike > h ? xA - xB + xD : xC;
    }
    // KnockOut
    if (isDown) {
      if (isCall) {
        return strike > h ? xA - xC : xB - xD;
      }
      return strike > h ? xA - xB + xC - xD : 0d;
    }
    if (isCall) {
      return strike > h ? 0d : xA - xB + xC - xD;
    }
    return strike > h ? xB - xD : xA - xC;
  }

}