class BondCapitalIndexedSecurityDiscountingMethod {

  public double dirtyPriceFromRealYield(final BondCapitalIndexedSecurity<?> bond, final double yield) {
    Validate.isTrue(bond.getNominal().getNumberOfPayments() == 1, "Yield: more than one nominal repayment.");
    final int nbCoupon = bond.getCoupon().getNumberOfPayments();
    final YieldConvention yieldConvention = bond.getYieldConvention();
    if (yieldConvention.equals(US_IL_REAL)) {
      // Coupon period rate to next coupon and simple rate from next coupon to settlement.
      double pvAtFirstCoupon;
      if (Math.abs(yield) > 1.0E-8) {
        final double factorOnPeriod = 1 + yield / bond.getCouponPerYear();
        final double vn = Math.pow(factorOnPeriod, 1 - nbCoupon);
        pvAtFirstCoupon = ((CouponInflationGearing) bond.getCoupon().getNthPayment(0)).getFactor() / yield * (factorOnPeriod - vn) + vn;
      } else {
        pvAtFirstCoupon = ((CouponInflationGearing) bond.getCoupon().getNthPayment(0)).getFactor() / bond.getCouponPerYear() * nbCoupon + 1;
      }
      return pvAtFirstCoupon / (1 + bond.getAccrualFactorToNextCoupon() * yield / bond.getCouponPerYear());
    }

    if (yieldConvention.equals(INDEX_LINKED_FLOAT)) {
      final double realRate = ((CouponInflationGearing) bond.getCoupon().getNthPayment(1)).getFactor();
      final double firstYearFraction = bond.getCoupon().getNthPayment(0).getPaymentYearFraction();
      double firstCouponEndFixingTime = 0.0;
      double firstCouponPayementTime = 0.0;
      if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponInterpolationGearing) {
        firstCouponEndFixingTime = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(1)).getReferenceEndTime()[1];
        firstCouponPayementTime = ((CouponInflationZeroCouponInterpolationGearing) bond.getCoupon().getNthPayment(1)).getPaymentTime();
      } else if (bond.getCoupon().getNthPayment(1) instanceof CouponInflationZeroCouponMonthlyGearing) {
        firstCouponEndFixingTime = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(1)).getReferenceEndTime();
        firstCouponPayementTime = ((CouponInflationZeroCouponMonthlyGearing) bond.getCoupon().getNthPayment(1)).getPaymentTime();
      }
      final double lag = firstCouponPayementTime - firstCouponEndFixingTime;
      final double v = 1 / (1 + yield / bond.getCouponPerYear());
      final double rpibase = bond.getIndexStartValue();
      final double rpiLast = bond.getLastIndexKnownFixing();
      final double indexRatio = rpiLast / rpibase;
      final int nbMonth = (int) Math.max(Math.round((bond.getLastKnownFixingTime() - bond.getCoupon().getNthPayment(0).getPaymentTime() + lag) * 12), 0.0);
      final double u = Math.pow(1 / (1 + .03), .5);
      final double a = indexRatio * Math.pow(u, 2.0 * nbMonth / 12.0);
      final double firstCashFlow = firstYearFraction * realRate * indexRatio;
      if (bond.getCoupon().getNumberOfPayments() == 1) {
        return Math.pow(u * v, bond.getAccrualFactorToNextCoupon()) * (firstCashFlow + 1) * a / u;
      } else {
        final double secondYearFraction = bond.getCoupon().getNthPayment(1).getPaymentYearFraction();
        final double secondCashFlow = secondYearFraction * realRate * indexRatio;
        final double vn = Math.pow(v, nbCoupon - 1);
        final double pvAtFirstCoupon = firstCashFlow + secondCashFlow * u * v + a * realRate * v * v * (1 - vn / v) / (1 - v) + a * vn;
        return pvAtFirstCoupon * Math.pow(u * v, bond.getRatioPeriodToNextCoupon());
      }
    }
    if (yieldConvention.equals(UK_IL_BOND)) {
      final double firstYearFraction = bond.getCoupon().getNthPayment(0).getPaymentYearFraction();
      final double realRate = ((CouponInflationGearing) bond.getCoupon().getNthPayment(1)).getFactor();
      // Real rate adjusted by the number of coupons, i.e. annual rate / 2 for UK bonds
      final double firstCashFlow = firstYearFraction * realRate;
      final double v = 1 / (1 + yield / bond.getCouponPerYear());
      if (bond.getCoupon().getNumberOfPayments() == 1) {
        return Math.pow(v, bond.getAccrualFactorToNextCoupon()) * (firstCashFlow + 1);
      } else {
        final double secondYearFraction = bond.getCoupon().getNthPayment(1).getPaymentYearFraction();
        final double secondCashFlow = secondYearFraction * realRate;
        final double vn = Math.pow(v, nbCoupon - 1);
        final double pvAtFirstCoupon = firstCashFlow + secondCashFlow * v + realRate * v * v * (1 - vn / v) / (1 - v) + vn;
        return pvAtFirstCoupon * Math.pow(v, bond.getAccrualFactorToNextCoupon());
      }
    }
    throw new UnsupportedOperationException("The convention " + bond.getYieldConvention().getName() + " is not supported.");
  }

}