/**
   * Print the Contents of the 2D Array Triplet
   *
   * @param strLeftLabel Left Label
   * @param strMiddleLabel Middle Label
   * @param strRightLabel Right Label
   * @param aadblLeft The Left 2D array
   * @param aadblMiddle The Middle 2D array
   * @param aadblRight The Right 2D array
   * @param bBailOnNaN Bail on encountering an NaN
   * @return TRUE => Print Successful
   */
  public static final boolean Print2DArrayTriplet(
      final java.lang.String strLeftLabel,
      final java.lang.String strMiddleLabel,
      final java.lang.String strRightLabel,
      final double[][] aadblLeft,
      final double[][] aadblMiddle,
      final double[][] aadblRight,
      final boolean bBailOnNaN) {
    if (null == aadblLeft || null == aadblMiddle || null == aadblRight) return false;

    int iSize = aadblLeft.length;

    if (0 == iSize || iSize != aadblMiddle.length || iSize != aadblRight.length) return false;

    for (int i = 0; i < iSize; ++i) {
      for (int j = 0; j < iSize; ++j) {
        if (!org.drip.quant.common.NumberUtil.IsValid(aadblLeft[i][j])
            && !org.drip.quant.common.NumberUtil.IsValid(aadblLeft[i][j])
            && !org.drip.quant.common.NumberUtil.IsValid(aadblRight[i][j])
            && bBailOnNaN) return false;

        System.out.println(
            strLeftLabel
                + "["
                + i
                + "]["
                + j
                + "] = "
                + org.drip.quant.common.FormatUtil.FormatDouble(aadblLeft[i][j], 1, 6, 1.)
                + "  |  "
                + strMiddleLabel
                + "["
                + i
                + "]["
                + j
                + "] = "
                + org.drip.quant.common.FormatUtil.FormatDouble(aadblMiddle[i][j], 1, 6, 1.)
                + "  |  "
                + strRightLabel
                + "["
                + i
                + "]["
                + j
                + "] = "
                + org.drip.quant.common.FormatUtil.FormatDouble(aadblRight[i][j], 1, 6, 1.));
      }
    }

    return true;
  }
  /**
   * Print the contents of the 2D array
   *
   * @param strName Label Name
   * @param aadblA The 2D array
   * @param bBailOnNaN Bail on encountering an NaN
   * @return TRUE => Print Successful
   */
  public static final boolean Print2DArray(
      final java.lang.String strName, final double[][] aadblA, final boolean bBailOnNaN) {
    if (null == aadblA || 0 == aadblA.length) return false;

    int iSize = aadblA.length;

    for (int i = 0; i < iSize; ++i) {
      for (int j = 0; j < iSize; ++j) {
        if (!org.drip.quant.common.NumberUtil.IsValid(aadblA[i][j]) && bBailOnNaN) return false;

        System.out.println(
            strName
                + "["
                + i
                + "]["
                + j
                + "] = "
                + org.drip.quant.common.FormatUtil.FormatDouble(aadblA[i][j], 1, 6, 1.));
      }
    }

    return true;
  }
  @Override
  public org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double> value(
      final org.drip.param.valuation.ValuationParams valParams,
      final org.drip.param.pricer.PricerParams pricerParams,
      final org.drip.param.definition.ComponentMarketParams mktParams,
      final org.drip.param.valuation.QuotingParams quotingParams) {
    if (null == valParams || null == mktParams) return null;

    org.drip.analytics.rates.DiscountCurve dc = mktParams.getDiscountCurve();

    if (null == dc) return null;

    long lStart = System.nanoTime();

    double dblFixing01 = 0.;
    double dblAccrued01 = 0.;
    double dblDirtyDV01 = 0.;
    boolean bFirstPeriod = true;
    double dblDirtyFloatingPV = 0.;
    double dblCashPayDF = java.lang.Double.NaN;
    double dblResetDate = java.lang.Double.NaN;
    double dblResetRate = java.lang.Double.NaN;

    java.lang.String strFRI = _fri.fullyQualifiedName();

    org.drip.analytics.rates.ForwardRateEstimator fc = mktParams.getForwardCurve();

    if (null != fc && !_fri.match(fc.index())) fc = null;

    for (org.drip.analytics.period.CashflowPeriod period : _lsCouponPeriod) {
      double dblFloatingRate = 0.;
      double dblDirtyPeriodDV01 = java.lang.Double.NaN;

      double dblPeriodPayDate = period.getPayDate();

      if (dblPeriodPayDate < valParams.valueDate()) continue;

      if (null == fc) fc = dc.forwardRateEstimator(dblPeriodPayDate, _fri);

      try {
        if (bFirstPeriod) {
          bFirstPeriod = false;

          if (null == mktParams.getFixings()
              || null
                  == mktParams
                      .getFixings()
                      .get(new org.drip.analytics.date.JulianDate(period.getResetDate()))
              || null
                  == mktParams
                      .getFixings()
                      .get(new org.drip.analytics.date.JulianDate(period.getResetDate()))
                      .get(strFRI))
            dblResetRate =
                dblFloatingRate =
                    null == fc ? dc.libor(_fri.tenor()) : fc.forward(period.getPayDate());
          else
            dblResetRate =
                dblFloatingRate =
                    mktParams
                        .getFixings()
                        .get(new org.drip.analytics.date.JulianDate(period.getResetDate()))
                        .get(strFRI);

          dblFixing01 =
              period.getAccrualDCF(valParams.valueDate())
                  * 0.0001
                  * getNotional(period.getAccrualStartDate(), valParams.valueDate());

          if (period.getStartDate() < valParams.valueDate()) dblAccrued01 = dblFixing01;

          dblResetDate = period.getResetDate();
        } else
          dblFloatingRate =
              null == fc
                  ? dc.libor(period.getStartDate(), period.getEndDate())
                  : fc.forward(period.getPayDate());

        dblDirtyPeriodDV01 =
            0.0001
                * period.getCouponDCF()
                * mktParams.getDiscountCurve().df(dblPeriodPayDate)
                * getNotional(period.getAccrualStartDate(), period.getEndDate());
      } catch (java.lang.Exception e) {
        e.printStackTrace();

        return null;
      }

      if (s_bBlog) {
        try {
          System.out.println(
              new org.drip.analytics.date.JulianDate(period.getResetDate())
                  + " ["
                  + new org.drip.analytics.date.JulianDate(period.getStartDate())
                  + "->"
                  + new org.drip.analytics.date.JulianDate(period.getEndDate())
                  + "] => "
                  + org.drip.quant.common.FormatUtil.FormatDouble(dblFloatingRate, 1, 4, 100.));
        } catch (java.lang.Exception e) {
          e.printStackTrace();
        }
      }

      dblDirtyDV01 += dblDirtyPeriodDV01;
      dblDirtyFloatingPV += dblDirtyPeriodDV01 * 10000. * dblFloatingRate;
    }

    try {
      double dblCashSettle = valParams.cashPayDate();

      if (null != _settleParams)
        dblCashSettle = _settleParams.cashSettleDate(valParams.valueDate());

      dblCashPayDF = mktParams.getDiscountCurve().df(dblCashSettle);
    } catch (java.lang.Exception e) {
      e.printStackTrace();

      return null;
    }

    dblDirtyDV01 /= dblCashPayDF;
    dblDirtyFloatingPV /= dblCashPayDF;
    double dblCleanDV01 = dblDirtyDV01 - dblAccrued01;
    double dblCleanFloatingPV = dblDirtyFloatingPV - dblAccrued01 * (dblResetRate + _dblSpread);

    org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double> mapResult =
        new org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double>();

    mapResult.put("ResetDate", dblResetDate);

    mapResult.put("ResetRate", dblResetRate);

    mapResult.put("Accrued01", dblAccrued01 * _dblNotional);

    mapResult.put("Fixing01", dblFixing01 * _dblNotional);

    mapResult.put(
        "FloatAccrued", dblAccrued01 * 10000. * (dblResetRate + _dblSpread) * _dblNotional);

    mapResult.put("DV01", dblCleanDV01 * _dblNotional);

    mapResult.put("CleanDV01", dblCleanDV01 * _dblNotional);

    mapResult.put("DirtyDV01", dblDirtyDV01 * _dblNotional);

    mapResult.put("CleanFloatingPV", dblCleanFloatingPV * _dblNotional);

    mapResult.put("DirtyFloatingPV", dblDirtyFloatingPV * _dblNotional);

    mapResult.put("PV", dblCleanFloatingPV * _dblNotional);

    mapResult.put("CleanPV", dblCleanFloatingPV * _dblNotional);

    mapResult.put("DirtyPV", dblDirtyFloatingPV * _dblNotional);

    mapResult.put("Upfront", dblCleanFloatingPV * _dblNotional);

    mapResult.put("FairPremium", dblCleanFloatingPV / dblCleanDV01);

    mapResult.put("Rate", dblCleanFloatingPV / dblCleanDV01);

    mapResult.put("ParRate", dblCleanFloatingPV / dblCleanDV01);

    double dblValueNotional = java.lang.Double.NaN;

    try {
      dblValueNotional = getNotional(valParams.valueDate());
    } catch (java.lang.Exception e) {
      e.printStackTrace();
    }

    if (org.drip.quant.common.NumberUtil.IsValid(dblValueNotional)) {
      double dblPrice = 100. * (1. + (dblCleanFloatingPV / _dblNotional / dblValueNotional));

      mapResult.put("Price", dblPrice);

      mapResult.put("CleanPrice", dblPrice);
    }

    mapResult.put("CalcTime", (System.nanoTime() - lStart) * 1.e-09);

    return mapResult;
  }
  private static final void CDSAPISample() throws Exception {
    JulianDate dtStart = JulianDate.Today();

    /*
     * Flat Discount Curve
     */

    DiscountCurve dc = DiscountCurveBuilder.CreateFromFlatRate(dtStart, "USD", 0.05);

    /*
     * Flat Credit Curve
     */

    CreditCurve cc = CreditCurveBuilder.FromFlatHazard(dtStart.getJulian(), "CC", "USD", 0.02, 0.4);

    /*
     * Component Market Parameters built from the Discount and the Credit Curves
     */

    ComponentMarketParams cmp = ComponentMarketParamsBuilder.MakeCreditCMP(dc, cc);

    /*
     * Create an SNAC CDS
     */

    CreditDefaultSwap cds = CDSBuilder.CreateSNAC(dtStart, "5Y", 0.1, "CC");

    /*
     * Valuation Parameters
     */

    ValuationParams valParams =
        ValuationParams.CreateValParams(dtStart, 0, "", Convention.DR_ACTUAL);

    /*
     * Standard Credit Pricer Parameters (check javadoc for details)
     */

    PricerParams pricerParams = PricerParams.MakeStdPricerParams();

    System.out.println(
        "Acc Start       Acc End     Pay Date    Index   Spread   Cpn DCF    Pay01    Surv01");

    System.out.println(
        "---------      ---------    ---------   ------  ------   -------- --------- --------");

    /*
     * CDS Coupon Cash Flow
     */

    for (CashflowPeriodCurveFactors p : cds.getCouponFlow(valParams, pricerParams, cmp))
      System.out.println(
          JulianDate.fromJulian(p.getAccrualStartDate())
              + FIELD_SEPARATOR
              + JulianDate.fromJulian(p.getAccrualEndDate())
              + FIELD_SEPARATOR
              + JulianDate.fromJulian(p.getPayDate())
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(p.getIndexRate(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(p.getSpread(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(p.getCouponDCF(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dc.df(p.getPayDate()), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(cc.getSurvival(p.getPayDate()), 1, 4, 1.));

    System.out.println(
        "Loss Start     Loss End      Pay Date      Cpn    Notl     Rec    EffDF    StartSurv  EndSurv");

    System.out.println(
        "----------     --------      --------      ---    ----     ---    -----    ---------  -------");

    /*
     * CDS Loss Cash Flow
     */

    for (LossPeriodCurveFactors dp : cds.getLossFlow(valParams, pricerParams, cmp))
      System.out.println(
          JulianDate.fromJulian(dp.getStartDate())
              + FIELD_SEPARATOR
              + JulianDate.fromJulian(dp.getEndDate())
              + FIELD_SEPARATOR
              + JulianDate.fromJulian(dp.getPayDate())
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.getCouponDCF(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.effectiveNotional(), 1, 0, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.effectiveRecovery(), 1, 2, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.effectiveDF(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.startSurvival(), 1, 4, 1.)
              + FIELD_SEPARATOR
              + FormatUtil.FormatDouble(dp.endSurvival(), 1, 4, 1.));
  }
  private static final Map<String, ForwardCurve> xM6MBasisSample(
      final JulianDate dtSpot,
      final String strCurrency,
      final DiscountCurve dc,
      final int iTenorInMonths,
      final String[] astrxM6MFwdTenor,
      final double[] adblxM6MBasisSwapQuote)
      throws Exception {
    System.out.println("------------------------------------------------------------");

    System.out.println(" SPL =>              n=4               |         |         |");

    System.out.println("---------------------------------------|  LOG DF |  LIBOR  |");

    System.out.println(" MSR =>  RECALC  |  REFEREN |  DERIVED |         |         |");

    System.out.println("------------------------------------------------------------");

    /*
     * Construct the 6M-xM float-float basis swap.
     */

    FloatFloatComponent[] aFFC =
        MakexM6MBasisSwap(dtSpot, strCurrency, astrxM6MFwdTenor, iTenorInMonths);

    String strBasisTenor = iTenorInMonths + "M";

    ValuationParams valParams = new ValuationParams(dtSpot, dtSpot, strCurrency);

    /*
     * Calculate the starting forward rate off of the discount curve.
     */

    double dblStartingFwd = dc.forward(dtSpot.julian(), dtSpot.addTenor(strBasisTenor).julian());

    /*
     * Set the discount curve based component market parameters.
     */

    CurveSurfaceQuoteSet mktParams =
        MarketParamsBuilder.Create(dc, null, null, null, null, null, null);

    Map<String, ForwardCurve> mapForward = new HashMap<String, ForwardCurve>();

    /*
     * Construct the shape preserving forward curve off of Quartic Polynomial Basis Spline.
     */

    ForwardCurve fcxMQuartic =
        ScenarioForwardCurveBuilder.ShapePreservingForwardCurve(
            "QUARTIC_FWD" + strBasisTenor,
            ForwardLabel.Create(strCurrency, strBasisTenor),
            valParams,
            null,
            mktParams,
            null,
            MultiSegmentSequenceBuilder.BASIS_SPLINE_POLYNOMIAL,
            new PolynomialFunctionSetParams(5),
            aFFC,
            "ReferenceParBasisSpread",
            adblxM6MBasisSwapQuote,
            dblStartingFwd);

    mapForward.put(" QUARTIC_FWD" + strBasisTenor, fcxMQuartic);

    /*
     * Set the discount curve + quartic polynomial forward curve based component market parameters.
     */

    CurveSurfaceQuoteSet mktParamsQuarticFwd =
        MarketParamsBuilder.Create(dc, fcxMQuartic, null, null, null, null, null, null);

    int i = 0;
    int iFreq = 12 / iTenorInMonths;

    /*
     * Compute the following forward curve metrics for each of cubic polynomial forward, quartic
     * 	polynomial forward, and KLK Hyperbolic tension forward curves:
     * 	- Reference Basis Par Spread
     * 	- Derived Basis Par Spread
     *
     * Further compare these with a) the forward rate off of the discount curve, b) the LIBOR rate, and
     * 	c) Input Basis Swap Quote.
     */

    for (String strMaturityTenor : astrxM6MFwdTenor) {
      double dblFwdEndDate = dtSpot.addTenor(strMaturityTenor).julian();

      double dblFwdStartDate =
          dtSpot.addTenor(strMaturityTenor).subtractTenor(strBasisTenor).julian();

      FloatFloatComponent ffc = aFFC[i++];

      CaseInsensitiveTreeMap<Double> mapQuarticValue =
          ffc.value(valParams, null, mktParamsQuarticFwd, null);

      System.out.println(
          " "
              + strMaturityTenor
              + " =>  "
              + FormatUtil.FormatDouble(fcxMQuartic.forward(strMaturityTenor), 2, 2, 100.)
              + "  |  "
              + FormatUtil.FormatDouble(mapQuarticValue.get("ReferenceParBasisSpread"), 2, 2, 1.)
              + "  |  "
              + FormatUtil.FormatDouble(mapQuarticValue.get("DerivedParBasisSpread"), 2, 2, 1.)
              + "  |  "
              + FormatUtil.FormatDouble(
                  iFreq * java.lang.Math.log(dc.df(dblFwdStartDate) / dc.df(dblFwdEndDate)),
                  1,
                  2,
                  100.)
              + "  |  "
              + FormatUtil.FormatDouble(dc.libor(dblFwdStartDate, dblFwdEndDate), 1, 2, 100.)
              + "  |  ");
    }

    return mapForward;
  }
  public static final void main(final String[] astrArgs) throws Exception {
    /*
     * Initialize the Credit Analytics Library
     */

    CreditAnalytics.Init("");

    JulianDate dtToday = DateUtil.Today();

    String[] astrTenor =
        new String[] {
          "1Y", "2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "11Y", "12Y", "15Y", "20Y",
          "25Y", "30Y"
        };

    double[] adblBasis =
        new double[] {
          0.00186, //  1Y
          0.00127, //  2Y
          0.00097, //  3Y
          0.00080, //  4Y
          0.00067, //  5Y
          0.00058, //  6Y
          0.00051, //  7Y
          0.00046, //  8Y
          0.00042, //  9Y
          0.00038, // 10Y
          0.00035, // 11Y
          0.00033, // 12Y
          0.00028, // 15Y
          0.00022, // 20Y
          0.00020, // 25Y
          0.00018 // 30Y
        };

    BasisCurve bcCubicPolynomial =
        ScenarioBasisCurveBuilder.CubicPolynomialBasisCurve(
            "USD3M6MBasis_CubicPolynomial",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis);

    BasisCurve bcQuinticPolynomial =
        ScenarioBasisCurveBuilder.QuarticPolynomialBasisCurve(
            "USD3M6MBasis_QuinticPolynomial",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis);

    BasisCurve bcKaklisPandelis =
        ScenarioBasisCurveBuilder.KaklisPandelisBasisCurve(
            "USD3M6MBasis_KaklisPandelis",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis);

    BasisCurve bcKLKHyperbolic =
        ScenarioBasisCurveBuilder.KLKHyperbolicBasisCurve(
            "USD3M6MBasis_KLKHyperbolic",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis,
            1.);

    BasisCurve bcKLKRationalLinear =
        ScenarioBasisCurveBuilder.KLKRationalLinearBasisCurve(
            "USD3M6MBasis_KLKRationalLinear",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis,
            0.1);

    BasisCurve bcKLKRationalQuadratic =
        ScenarioBasisCurveBuilder.KLKRationalLinearBasisCurve(
            "USD3M6MBasis_KLKRationalQuadratic",
            dtToday,
            ForwardLabel.Create("USD", "6M"),
            ForwardLabel.Create("USD", "3M"),
            false,
            new CollateralizationParams("OVERNIGHT", "USD"),
            astrTenor,
            adblBasis,
            2.);

    System.out.println("\tPrinting the Basis Node Values in Order (Left -> Right):");

    System.out.println("\t\tCalculated Cubic Polynomial Basis (%)");

    System.out.println("\t\tCalculated Quintic Polynomial Basis (%)");

    System.out.println("\t\tCalculated Kaklis Pandelis Basis (%)");

    System.out.println("\t\tCalculated KLK Hyperbolic Basis (%)");

    System.out.println("\t\tCalculated KLK Rational Linear Basis (%)");

    System.out.println("\t\tCalculated KLK Rational Quadratic Basis (%)");

    System.out.println("\t\tInput Quote (bp)");

    System.out.println("\t-------------------------------------------------------------");

    System.out.println("\t-------------------------------------------------------------");

    for (int i = 0; i < adblBasis.length; ++i)
      System.out.println(
          "\t"
              + astrTenor[i]
              + " => "
              + FormatUtil.FormatDouble(bcCubicPolynomial.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(bcQuinticPolynomial.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(bcKaklisPandelis.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(bcKLKHyperbolic.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(bcKLKRationalLinear.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(bcKLKRationalQuadratic.basis(astrTenor[i]), 1, 2, 10000.)
              + " | "
              + FormatUtil.FormatDouble(adblBasis[i], 1, 2, 10000.));

    System.out.println(
        "\n\t|----------------------------------------------------------------------------|");

    System.out.println(
        "\t|  DATE    =>  CUBIC | QUINTIC  | KAKPAND | KLKHYPER | KLKRATLNR | KLKRATQUA |");

    System.out.println(
        "\t|----------------------------------------------------------------------------|\n");

    for (int i = 3; i < 30; ++i) {
      JulianDate dt = dtToday.addTenor(i + "Y");

      System.out.println(
          "\t"
              + dt
              + " => "
              + FormatUtil.FormatDouble(bcCubicPolynomial.basis(dt), 1, 2, 10000.)
              + "  |  "
              + FormatUtil.FormatDouble(bcQuinticPolynomial.basis(dt), 1, 2, 10000.)
              + "   |  "
              + FormatUtil.FormatDouble(bcKaklisPandelis.basis(dt), 1, 2, 10000.)
              + "  |  "
              + FormatUtil.FormatDouble(bcKLKHyperbolic.basis(dt), 1, 2, 10000.)
              + "   |  "
              + FormatUtil.FormatDouble(bcKLKRationalLinear.basis(dt), 1, 2, 10000.)
              + "    |  "
              + FormatUtil.FormatDouble(bcKLKRationalQuadratic.basis(dt), 1, 2, 10000.)
              + "    |  ");
    }

    System.out.println(
        "\n\t|----------------------------------------------------------------------------|");
  }