/**
   * Create a credit curve from an array of dates and hazard rates
   *
   * @param dtStart Curve epoch date
   * @param strName Credit Curve Name
   * @param strCurrency Currency
   * @param adblDate Array of dates
   * @param adblHazardRate Array of hazard rates
   * @param dblRecovery Recovery
   * @return CreditCurve instance
   */
  public static final org.drip.analytics.definition.ExplicitBootCreditCurve CreateCreditCurve(
      final org.drip.analytics.date.JulianDate dtStart,
      final java.lang.String strName,
      final java.lang.String strCurrency,
      final double[] adblDate,
      final double[] adblHazardRate,
      final double dblRecovery) {
    if (null == dtStart
        || null == adblHazardRate
        || null == adblDate
        || adblHazardRate.length != adblDate.length
        || !org.drip.quant.common.NumberUtil.IsValid(dblRecovery)) return null;

    try {
      double[] adblRecovery = new double[1];
      double[] adblRecoveryDate = new double[1];
      adblRecovery[0] = dblRecovery;

      adblRecoveryDate[0] = dtStart.julian();

      return new org.drip.state.curve.ForwardHazardCreditCurve(
          dtStart.julian(),
          org.drip.state.identifier.CreditLabel.Standard(strName),
          strCurrency,
          adblHazardRate,
          adblDate,
          adblRecovery,
          adblRecoveryDate,
          java.lang.Double.NaN);
    } catch (java.lang.Exception e) {
      e.printStackTrace();
    }

    return null;
  }
  /**
   * Construct an DerivedFXBasis instance from the currency pair, FX Spot, and FX basis parameters
   *
   * @param cp Currency Pair
   * @param dtSpot Spot Date
   * @param dblFXSpot FX Spot
   * @param adblDate Array of dates
   * @param adblFXBasis Array of FX Basis
   * @param bIsFXBasisBootstrapped True if the inputs are for bootstrapped FX basis
   * @throws java.lang.Exception Thrown if the FXBasis instance cannot be created
   */
  public DerivedFXBasis(
      final org.drip.product.params.CurrencyPair cp,
      final org.drip.analytics.date.JulianDate dtSpot,
      final double dblFXSpot,
      final double[] adblDate,
      final double[] adblFXBasis,
      final boolean bIsFXBasisBootstrapped)
      throws java.lang.Exception {
    if (null == cp
        || null == dtSpot
        || !org.drip.quant.common.NumberUtil.IsValid(dblFXSpot)
        || null == adblDate
        || 0 == adblDate.length
        || null == adblFXBasis
        || 0 == adblFXBasis.length
        || adblDate.length != adblFXBasis.length)
      throw new java.lang.Exception("DerivedFXBasis ctr: Invalid Params");

    _dblSpotDate = dtSpot.julian();

    _cp = cp;
    _dblFXSpot = dblFXSpot;
    _adblDate = new double[adblDate.length];
    _adblFXBasis = new double[adblFXBasis.length];
    _bIsFXBasisBootstrapped = bIsFXBasisBootstrapped;

    for (int i = 0; i < adblFXBasis.length; ++i) {
      if (!org.drip.quant.common.NumberUtil.IsValid(adblDate[i]) || adblDate[i] <= _dblSpotDate)
        throw new java.lang.Exception(
            "DerivedFXBasis ctr: Invalid params: Node date "
                + org.drip.analytics.date.DateUtil.FromJulian(adblDate[i])
                + " before spot "
                + dtSpot);

      _adblDate[i] = adblDate[i];
      _adblFXBasis[i] = adblFXBasis[i];
    }
  }
  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;
  }
  /**
   * Construct NonlinearDiscountFactorDiscountCurve instance from an array of dates and forward
   * rates
   *
   * @param dtStart Epoch Date
   * @param strCurrency Currency
   * @param collatParams Collateralization Parameters
   * @param adblDate Array of Dates
   * @param adblRate Array of Forward Rates
   * @throws java.lang.Exception Thrown if the curve cannot be created
   */
  public NonlinearDiscountFactorDiscountCurve(
      final org.drip.analytics.date.JulianDate dtStart,
      final java.lang.String strCurrency,
      final org.drip.param.valuation.CollateralizationParams collatParams,
      final double[] adblDate,
      final double[] adblRate)
      throws java.lang.Exception {
    super(dtStart.julian(), strCurrency, collatParams);

    if (null == adblDate
        || 0 == adblDate.length
        || null == adblRate
        || adblDate.length != adblRate.length
        || null == dtStart)
      throw new java.lang.Exception("NonlinearDiscountFactorDiscountCurve ctr: Invalid inputs");

    _dblEpochDate = dtStart.julian();

    org.drip.spline.params.SegmentCustomBuilderControl sbp =
        new org.drip.spline.params.SegmentCustomBuilderControl(
            org.drip.spline.stretch.MultiSegmentSequenceBuilder.BASIS_SPLINE_POLYNOMIAL,
            new org.drip.spline.basis.PolynomialFunctionSetParams(2),
            org.drip.spline.params.SegmentInelasticDesignControl.Create(0, 2),
            null,
            null);

    int iNumSegment = adblDate.length;
    _adblDate = new double[iNumSegment];
    double[] adblDF = new double[iNumSegment];
    org.drip.spline.params.SegmentCustomBuilderControl[] aSBP =
        new org.drip.spline.params.SegmentCustomBuilderControl[adblDate.length - 1];

    for (int i = 0; i < iNumSegment; ++i) {
      _adblDate[i] = adblDate[i];

      if (0 == i)
        adblDF[0] = java.lang.Math.exp(adblRate[0] * (_dblEpochDate - _adblDate[0]) / 365.25);
      else {
        aSBP[i - 1] = sbp;

        adblDF[i] =
            java.lang.Math.exp(adblRate[i] * (_adblDate[i - 1] - _adblDate[i]) / 365.25)
                * adblDF[i - 1];
      }
    }

    _dblLeftFlatForwardRate =
        -365.25 * java.lang.Math.log(adblDF[0]) / (_adblDate[0] - _dblEpochDate);

    _dblRightFlatForwardRate =
        -365.25
            * java.lang.Math.log(adblDF[iNumSegment - 1])
            / (_adblDate[iNumSegment - 1] - _dblEpochDate);

    _msr =
        org.drip.spline.stretch.MultiSegmentSequenceBuilder.CreateCalibratedStretchEstimator(
            "POLY_SPLINE_DF_REGIME",
            adblDate,
            adblDF,
            aSBP,
            null,
            org.drip.spline.stretch.BoundarySettings.NaturalStandard(),
            org.drip.spline.stretch.MultiSegmentSequence.CALIBRATE);
  }