class SwapCleanDiscountingCalculator {

  public Double parRate(SwapFixedIborDefinition swapDefinition, DayCount fixedLegDayCount,
      DayCount iborLegDayCount, Calendar calendar, ZonedDateTime valuationDate,
      ZonedDateTimeDoubleTimeSeries indexTimeSeries, MulticurveProviderDiscount multicurves) {
    ArgumentChecker.notNull(swapDefinition, "swapDefinition");
    ArgumentChecker.notNull(fixedLegDayCount, "fixedLegDayCount");
    ArgumentChecker.notNull(iborLegDayCount, "iborLegDayCount");
    ArgumentChecker.notNull(calendar, "calendar");
    ArgumentChecker.notNull(valuationDate, "valuationDate");
    ArgumentChecker.notNull(indexTimeSeries, "indexTimeSeries");
    ArgumentChecker.notNull(multicurves, "multicurves");
    checkNotionalAndFixedRate(swapDefinition);

    Annuity<? extends Coupon> iborLeg = swapDefinition.getIborLeg().toDerivative(valuationDate, indexTimeSeries);
    double dirtyIborLegPV = iborLeg.accept(PVDC, multicurves).getAmount(iborLeg.getCurrency());
    double iborLegAccruedInterest = getAccrued(iborLegDayCount, calendar, valuationDate,
        swapDefinition.getIborLeg(), indexTimeSeries);
    double cleanFloatingPV = (dirtyIborLegPV - iborLegAccruedInterest) *
        Math.signum(iborLeg.getNthPayment(0).getNotional());
    
    AnnuityCouponFixed fixedLeg = swapDefinition.getFixedLeg().toDerivative(valuationDate);
    double accruedAmount = getAccrued(fixedLegDayCount, calendar, valuationDate, swapDefinition.getFixedLeg(),
        indexTimeSeries) * Math.signum(fixedLeg.getNthPayment(0).getNotional());
    double dirtyAnnuity = METHOD_SWAP.presentValueBasisPoint(new SwapFixedCoupon<>(fixedLeg, iborLeg),
        multicurves);
    double cleanAnnuity = dirtyAnnuity - accruedAmount;

    return cleanFloatingPV / cleanAnnuity;
  }

}