class DiscountingCmsPeriodPricer {

  public PointSensitivityBuilder presentValueSensitivity(
      CmsPeriod cmsPeriod,
      RatesProvider provider) {

    Currency ccy = cmsPeriod.getCurrency();
    LocalDate valuationDate = provider.getValuationDate();
    if (valuationDate.isAfter(cmsPeriod.getPaymentDate())) {
      return PointSensitivityBuilder.none();
    }
    LocalDate fixingDate = cmsPeriod.getFixingDate();
    double dfPayment = provider.discountFactor(ccy, cmsPeriod.getPaymentDate());
    if (!fixingDate.isAfter(valuationDate)) { // Using fixing
      OptionalDouble fixedRate = provider.timeSeries(cmsPeriod.getIndex()).get(fixingDate);
      if (fixedRate.isPresent()) {
        double payoff = 0d;
        switch (cmsPeriod.getCmsPeriodType()) {
          case CAPLET:
            payoff = Math.max(fixedRate.getAsDouble() - cmsPeriod.getStrike(), 0d);
            break;
          case FLOORLET:
            payoff = Math.max(cmsPeriod.getStrike() - fixedRate.getAsDouble(), 0d);
            break;
          case COUPON:
            payoff = fixedRate.getAsDouble();
            break;
          default:
            throw new IllegalArgumentException("unsupported CMS type");
        }
        return provider.discountFactors(ccy).zeroRatePointSensitivity(
            cmsPeriod.getPaymentDate()).multipliedBy(payoff * cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
      } else if (fixingDate.isBefore(valuationDate)) {
        throw new IllegalArgumentException(Messages.format(
            "Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.getIndex(), fixingDate));
      }
    }
    if (!cmsPeriod.getCmsPeriodType().equals(CmsPeriodType.COUPON)) {
      throw new IllegalArgumentException("Unable to price cap or floor in this pricer");
    }
    // Using forward
    ResolvedSwap swap = cmsPeriod.getUnderlyingSwap();
    ZeroRateSensitivity dfPaymentdr = provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.getPaymentDate());
    double forward = swapPricer.parRate(swap, provider);
    PointSensitivityBuilder forwardSensi = swapPricer.parRateSensitivity(swap, provider);
    return forwardSensi.multipliedBy(dfPayment).combinedWith(dfPaymentdr.multipliedBy(forward))
        .multipliedBy(cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
  }

  public  DiscountingCmsPeriodPricer(
      DiscountingSwapProductPricer swapPricer);

}