/**
   * The bucked CS01 (or credit DV01) by shifting each implied par-spread in turn. This takes an
   * extraneous yield curve and a credit curve and a set of bucket CDSs (CDSs with maturities equal
   * to the bucket points). Par-spreads at the bucket maturities are implied from the credit curve.
   * These spreads form pseudo market spreads to bootstraps a new credit (hazard) curve - the target
   * CDS is then priced with this credit curve. This is then repeated with each spreads bumped in
   * turn. The result is the vector of differences (bumped minus base price) divided by the bump
   * amount.
   *
   * @param cds analytic description of a CDS traded at a certain time
   * @param cdsCoupon The <b>fraction</b> spread of the CDS
   * @param bucketCDSs these are the reference instruments that correspond to maturity buckets
   * @param yieldCurve The yield (or discount) curve
   * @param creditCurve the credit curve
   * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4
   * @return The credit DV01
   */
  public double[] bucketedCS01FromCreditCurve(
      CdsAnalytic cds,
      double cdsCoupon,
      CdsAnalytic[] bucketCDSs,
      IsdaCompliantYieldCurve yieldCurve,
      IsdaCompliantCreditCurve creditCurve,
      double fracBumpAmount) {

    ArgChecker.notNull(cds, "cds");
    ArgChecker.noNulls(bucketCDSs, "bucketCDSs");
    ArgChecker.notNull(creditCurve, "creditCurve");
    ArgChecker.notNull(yieldCurve, "yieldCurve");
    ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small");
    int n = bucketCDSs.length;

    double[] impSpreads = new double[n];
    double[] t = new double[n];
    for (int i = 0; i < n; i++) {
      impSpreads[i] = _pricer.parSpread(bucketCDSs[i], yieldCurve, creditCurve);
      t[i] = bucketCDSs[i].getProtectionEnd();
      if (i > 0) {
        ArgChecker.isTrue(t[i] > t[i - 1], "buckets must be assending");
      }
    }
    int index = Arrays.binarySearch(t, cds.getProtectionEnd());
    if (index < 0) {
      index = -1 - index;
    }
    index = Math.min(index, n - 1);

    IsdaCompliantCreditCurve baseCurve =
        _curveBuilder.calibrateCreditCurve(bucketCDSs, impSpreads, yieldCurve);
    double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon);
    double[] res = new double[n];
    for (int i = 0; i <= index; i++) { // don't bother calculating where there is no sensitivity
      double[] bumpedSpreads = makeBumpedSpreads(impSpreads, fracBumpAmount, ShiftType.ABSOLUTE, i);
      IsdaCompliantCreditCurve bumpedCurve =
          _curveBuilder.calibrateCreditCurve(bucketCDSs, bumpedSpreads, yieldCurve);
      double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon);
      res[i] = (price - basePrice) / fracBumpAmount;
    }
    return res;
  }
  public double parallelCS01FromCreditCurve(
      CdsAnalytic cds,
      double cdsCoupon,
      CdsAnalytic[] pillarCDSs,
      IsdaCompliantYieldCurve yieldCurve,
      IsdaCompliantCreditCurve creditCurve,
      double fracBumpAmount) {

    ArgChecker.notNull(cds, "cds");
    ArgChecker.noNulls(pillarCDSs, "pillarCDSs");
    ArgChecker.notNull(creditCurve, "creditCurve");
    ArgChecker.notNull(yieldCurve, "yieldCurve");
    ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small");
    int n = pillarCDSs.length;

    double[] impSpreads = new double[n];
    double[] t = new double[n];
    for (int i = 0; i < n; i++) {
      impSpreads[i] = _pricer.parSpread(pillarCDSs[i], yieldCurve, creditCurve);
      t[i] = pillarCDSs[i].getProtectionEnd();
      if (i > 0) {
        ArgChecker.isTrue(t[i] > t[i - 1], "pillars must be assending");
      }
    }

    IsdaCompliantCreditCurve baseCurve =
        _curveBuilder.calibrateCreditCurve(pillarCDSs, impSpreads, yieldCurve);
    double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon);
    double[] bumpedSpreads = makeBumpedSpreads(impSpreads, fracBumpAmount, ShiftType.ABSOLUTE);
    IsdaCompliantCreditCurve bumpedCurve =
        _curveBuilder.calibrateCreditCurve(pillarCDSs, bumpedSpreads, yieldCurve);
    double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon);
    double res = (price - basePrice) / fracBumpAmount;

    return res;
  }