/**
   * Retrieve the basket product's coupon amount at the given date
   *
   * @param dblDate Double JulianDate
   * @param bmp Basket Market Parameters
   * @return Coupon Amount
   * @throws java.lang.Exception Thrown if coupon cannot be calculated
   */
  public double getCoupon(
      final double dblDate, final org.drip.param.definition.BasketMarketParams bmp)
      throws java.lang.Exception {
    double dblNotional = getNotional(dblDate);

    if (null == bmp || 0. == dblNotional || !org.drip.quant.common.NumberUtil.IsValid(dblNotional))
      throw new java.lang.Exception("BasketProduct::getCoupon => Cannot extract basket notional");

    org.drip.product.definition.Component[] aComp = getComponents();

    double dblCoupon = 0.;
    int iNumComp = aComp.length;

    for (int i = 0; i < iNumComp; ++i)
      dblCoupon += aComp[i].getCoupon(dblDate, bmp.getComponentMarketParams(aComp[i]));

    return dblCoupon / dblNotional;
  }
  /**
   * Generate a full list of the basket product measures for the full input set of market parameters
   *
   * @param valParams ValuationParams
   * @param pricerParams PricerParams
   * @param bmp BasketMarketParams
   * @param quotingParams Quoting Parameters
   * @return Map of measure name and value
   */
  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.BasketMarketParams bmp,
      final org.drip.param.valuation.QuotingParams quotingParams) {
    long lStart = System.nanoTime();

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

    org.drip.product.definition.Component[] aComp = getComponents();

    double[] adblWeight = getWeights();

    int iNumComp = aComp.length;

    for (int i = 0; i < iNumComp; ++i) {
      org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double> mapCompOP =
          aComp[i].value(
              valParams, pricerParams, bmp.getComponentMarketParams(aComp[i]), quotingParams);

      if (null == mapCompOP || 0 == mapCompOP.size()) continue;

      java.util.Set<java.util.Map.Entry<java.lang.String, java.lang.Double>> mapCompOPES =
          mapCompOP.entrySet();

      if (null == mapCompOPES) continue;

      for (java.util.Map.Entry<java.lang.String, java.lang.Double> meCompOP : mapCompOPES) {
        if (null == meCompOP) continue;

        java.lang.String strKey = meCompOP.getKey();

        if (null == strKey || strKey.isEmpty()) continue;

        java.lang.Double dblCompValue = mapCompOP.get(strKey);

        java.lang.Double dblBasketValue = mapBasketOP.get(strKey);

        if (MEASURE_AGGREGATION_TYPE_CUMULATIVE == measureAggregationType(strKey))
          mapBasketOP.put(
              strKey,
              (null == dblCompValue ? 0. : dblCompValue)
                  + (null == dblBasketValue ? 0. : dblBasketValue));
        else if (MEASURE_AGGREGATION_TYPE_WEIGHTED_CUMULATIVE == measureAggregationType(strKey)
            && null != adblWeight)
          mapBasketOP.put(
              strKey,
              (null == dblCompValue ? 0. : adblWeight[i] * dblCompValue)
                  + (null == dblBasketValue ? 0. : dblBasketValue));
        else if (MEASURE_AGGREGATION_TYPE_UNIT_ACCUMULATE == measureAggregationType(strKey))
          mapBasketOP.put(
              aComp[i].getComponentName() + "[" + strKey + "]",
              (null == dblCompValue ? 0. : dblCompValue));
      }
    }

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

    return mapBasketOP;
  }
  private ComponentFactorTenorDeltaGammaMeasureMap accumulateComponentWiseTenorDeltaGammaMeasures(
      final org.drip.param.valuation.ValuationParams valParams,
      final org.drip.param.pricer.PricerParams pricerParams,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<
              org.drip.param.definition.BasketMarketParams>
          mapComponentBMP,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<
              org.drip.param.definition.BasketMarketParams>
          mapTenorUpBMP,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<
              org.drip.param.definition.BasketMarketParams>
          mapTenorDownBMP,
      final org.drip.param.valuation.QuotingParams quotingParams,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double> mapBaseMeasures) {
    if (null == mapComponentBMP || 0 == mapComponentBMP.size()) return null;

    java.util.Set<
            java.util.Map.Entry<java.lang.String, org.drip.param.definition.BasketMarketParams>>
        mapESComponentBMP = mapComponentBMP.entrySet();

    if (null == mapESComponentBMP || 0 == mapESComponentBMP.size()) return null;

    org.drip.analytics.support.CaseInsensitiveTreeMap<TenorDeltaGammaMeasureMap>
        mapComponentTenorDGMM =
            new org.drip.analytics.support.CaseInsensitiveTreeMap<TenorDeltaGammaMeasureMap>();

    for (java.util.Map.Entry<java.lang.String, org.drip.param.definition.BasketMarketParams>
        meComponentBMP : mapESComponentBMP) {
      if (null == meComponentBMP) continue;

      java.lang.String strComponentName = meComponentBMP.getKey();

      if (null == strComponentName || strComponentName.isEmpty()) continue;

      org.drip.param.definition.BasketMarketParams bmpComponent = meComponentBMP.getValue();

      if (null != bmpComponent)
        mapComponentTenorDGMM.put(
            strComponentName,
            accumulateTenorDeltaGammaMeasures(
                valParams,
                pricerParams,
                mapTenorUpBMP,
                mapTenorDownBMP,
                quotingParams,
                mapBaseMeasures,
                new ComponentCurve(
                    strComponentName, bmpComponent.getCreditCurve(strComponentName))));
    }

    if (0 == mapComponentTenorDGMM.size()) return null;

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

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

    for (java.util.Map.Entry<java.lang.String, TenorDeltaGammaMeasureMap> meCompTenorDGMM :
        mapComponentTenorDGMM.entrySet()) {
      if (null == meCompTenorDGMM) continue;

      TenorDeltaGammaMeasureMap dgmmCompTenorDeltaGamma = meCompTenorDGMM.getValue();

      if (null != dgmmCompTenorDeltaGamma) {
        java.lang.String strKey = meCompTenorDGMM.getKey();

        mmmCompRatesDelta.put(strKey, dgmmCompTenorDeltaGamma._mmDelta);

        mmmCompRatesGamma.put(strKey, dgmmCompTenorDeltaGamma._mmGamma);
      }
    }

    return new ComponentFactorTenorDeltaGammaMeasureMap(mmmCompRatesDelta, mmmCompRatesGamma);
  }
  private TenorDeltaGammaMeasureMap accumulateTenorDeltaGammaMeasures(
      final org.drip.param.valuation.ValuationParams valParams,
      final org.drip.param.pricer.PricerParams pricerParams,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<
              org.drip.param.definition.BasketMarketParams>
          mapTenorUpBMP,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<
              org.drip.param.definition.BasketMarketParams>
          mapTenorDownBMP,
      final org.drip.param.valuation.QuotingParams quotingParams,
      final org.drip.analytics.support.CaseInsensitiveTreeMap<java.lang.Double> mapBaseMeasures,
      final ComponentCurve compCurve) {
    if (null == mapTenorUpBMP || 0 == mapTenorUpBMP.size()) return null;

    java.util.Set<
            java.util.Map.Entry<java.lang.String, org.drip.param.definition.BasketMarketParams>>
        mapESTenorUpBMP = mapTenorUpBMP.entrySet();

    if (null == mapESTenorUpBMP || 0 == mapESTenorUpBMP.size()) return null;

    org.drip.analytics.support.CaseInsensitiveTreeMap<FlatDeltaGammaMeasureMap> mapTenorDGMM =
        new org.drip.analytics.support.CaseInsensitiveTreeMap<FlatDeltaGammaMeasureMap>();

    for (java.util.Map.Entry<java.lang.String, org.drip.param.definition.BasketMarketParams>
        meTenorUpBMP : mapESTenorUpBMP) {
      if (null == meTenorUpBMP) continue;

      java.lang.String strTenorKey = meTenorUpBMP.getKey();

      if (null == strTenorKey || strTenorKey.isEmpty()) continue;

      org.drip.param.definition.BasketMarketParams bmpTenorUp = meTenorUpBMP.getValue();

      org.drip.param.definition.BasketMarketParams bmpTenorDown = mapTenorDownBMP.get(strTenorKey);

      org.drip.analytics.definition.CreditCurve ccVirginUp = null;
      org.drip.analytics.definition.CreditCurve ccVirginDown = null;

      if (null != bmpTenorUp
          && null != compCurve
          && null != compCurve._cc
          && null != compCurve._strName
          && !compCurve._strName.isEmpty()) {
        ccVirginUp = bmpTenorUp.getCreditCurve(compCurve._strName);

        bmpTenorUp.addCreditCurve(compCurve._strName, compCurve._cc);

        if (null != bmpTenorDown) {
          ccVirginDown = bmpTenorDown.getCreditCurve(compCurve._strName);

          bmpTenorDown.addCreditCurve(compCurve._strName, compCurve._cc);
        }
      }

      mapTenorDGMM.put(
          strTenorKey,
          accumulateDeltaGammaMeasures(
              valParams, pricerParams, bmpTenorUp, bmpTenorDown, quotingParams, mapBaseMeasures));

      if (null != bmpTenorUp
          && null != compCurve
          && null != compCurve._strName
          && !compCurve._strName.isEmpty()
          && null != ccVirginUp) bmpTenorUp.addCreditCurve(compCurve._strName, ccVirginUp);

      if (null != bmpTenorDown
          && null != compCurve
          && null != compCurve._strName
          && !compCurve._strName.isEmpty()
          && null != ccVirginDown) bmpTenorDown.addCreditCurve(compCurve._strName, ccVirginDown);
    }

    if (0 == mapTenorDGMM.size()) return null;

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

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

    for (java.util.Map.Entry<java.lang.String, FlatDeltaGammaMeasureMap> meTenorDGMM :
        mapTenorDGMM.entrySet()) {
      if (null == meTenorDGMM) continue;

      FlatDeltaGammaMeasureMap dgmmTenorDelta = meTenorDGMM.getValue();

      if (null != dgmmTenorDelta) {
        java.lang.String strKey = meTenorDGMM.getKey();

        mmDelta.put(strKey, dgmmTenorDelta._mapDelta);

        mmGamma.put(strKey, dgmmTenorDelta._mapGamma);
      }
    }

    return new TenorDeltaGammaMeasureMap(mmDelta, mmGamma);
  }