/// <summary>
        /// Modify the pv and cash taking the date of default into account.
        /// </summary>
        /// <remarks>
        /// The default value considers the buy/sell sign or whether it's a forward deal, but it doesn't consider whether the call option has been exercised or not.
        /// This is partly to respect the method signature of the base class, but partly the call exercise feature can be taken care of by modifying the actual
        /// underlier default date - if the name is in default but the call option has been exercised, the default has no consequence, and the "effective"
        /// default date can be moved to inifinity (as far as default value is concerned).
        /// </remarks>
        public override void GetDefaultValue(double baseDate, double valueDate, Vector defaultDate, RecoveryRate recoveryRate, Vector pv, Vector cash)
        {
            double principal      = CFFixedInterestListValuation.GetPrincipal(fCashflowList, valueDate);
            var    deal           = (CallableBondForward)Deal;
            double settlementDate = deal.Settlement_Date;
            double t           = CalcUtils.DaysToYears(valueDate - baseDate);
            double buySellSign = deal.Buy_Sell == BuySell.Buy ? 1.0 : -1.0;

            using (var cache = Vector.CacheLike(pv))
            {
                // Approximation: recover only principal, neglecting accrued interest
                Vector recovery = cache.Get(buySellSign * principal * recoveryRate.Get(t));

                if (valueDate <= settlementDate)
                {
                    // Set the pv to (recovery - settlementAmount) * df when defaultDate <= valueDate <= settlementDate.
                    // Set cash to (recovery - settlementAmount) when defaultDate <= valueDate = settlementDate (cash is always zero before settlementDate).
                    double tSettle          = CalcUtils.DaysToYears(settlementDate - baseDate);
                    double settlementAmount = buySellSign * fSettlementAmount;
                    Vector hasDefaulted     = cache.Get(defaultDate <= valueDate);

                    pv.AssignConditional(hasDefaulted, fRepoRate.Get(t, tSettle) * (recovery - settlementAmount), pv);

                    if (cash != null && valueDate == settlementDate)
                    {
                        cash.AssignConditional(hasDefaulted, pv, cash);
                    }
                }
                else
                {
                    // after settlement date
                    recovery.MultiplyBy(defaultDate >= valueDate); // set to zero if already defaulted
                    Vector notDefaulted = cache.Get(defaultDate > valueDate);
                    pv.AssignConditional(notDefaulted, pv, recovery);
                    if (cash != null)
                    {
                        cash.AssignConditional(notDefaulted, cash, recovery);
                    }
                }
            }
        }
        /// <summary>
        /// Prepare for valuation anything that is not dependent upon the scenario.
        /// </summary>
        public override void PreCloneInitialize(PriceFactorList factors, BaseTimeGrid baseTimes, RequiredResults requiredResults)
        {
            base.PreCloneInitialize(factors, baseTimes, requiredResults);

            CallableBondForward deal = (CallableBondForward)Deal;

            double           firstCallDate   = deal.First_Call_Date;
            double           lastCallDate    = deal.Last_Call_Date;
            double           baseDate        = factors.BaseDate;
            double           issueDate       = deal.Issue_Date;
            double           settlementDate  = deal.Settlement_Date;
            double           priceDate       = Math.Max(baseDate, settlementDate + 1.0); // bond cashflows before priceDate do not contribute to bond price
            double           maturityDate    = deal.Bond_Maturity_Date;
            double           couponInterval  = deal.Coupon_Interval;
            double           notional        = deal.Notional;
            IHolidayCalendar holidayCalendar = deal.GetHolidayCalendar();

            DateGenerationParams dateGenerationParams = new DateGenerationParams
            {
                EffectiveDate         = issueDate,
                MaturityDate          = maturityDate,
                AccrualDayCount       = deal.Accrual_Day_Count,
                FirstCouponDate       = deal.First_Coupon_Date,
                PenultimateCouponDate = deal.Penultimate_Coupon_Date,
                Amortisation          = deal.Amortisation,
                CouponPeriod          = couponInterval,
                Principal             = notional,
                PrincipalExchange     = PrincipalExchange.Start_Maturity,
                AccrualCalendar       = holidayCalendar
            };

            CashflowListDetail detail = CashflowGeneration.GenerateCashflowListDetail(dateGenerationParams);

            // Collect reset dates as we loop.
            var resetDates = new DateList(detail.Coupon_Details.Count);

            // Create cashflow list
            fCashflowList             = new CFFixedInterestList();
            fCashflowList.Compounding = YesNo.No;

            foreach (CouponDetail couponDetail in detail.Coupon_Details)
            {
                if (couponDetail.Payment_Date < priceDate)
                {
                    continue;
                }

                foreach (AccrualDetail accrualDetail in couponDetail.Accrual_Details)
                {
                    resetDates.Add(accrualDetail.Accrual_Start_Date);

                    if (couponDetail.Payment_Date < priceDate)
                    {
                        continue;
                    }

                    var cashflow = new CFFixedInterest
                    {
                        Payment_Date          = couponDetail.Payment_Date,
                        Notional              = accrualDetail.Notional,
                        Accrual_Start_Date    = accrualDetail.Accrual_Start_Date,
                        Accrual_End_Date      = accrualDetail.Accrual_End_Date,
                        Accrual_Year_Fraction = accrualDetail.Accrual_Year_Fraction,
                        Rate = deal.Coupon_Rate * Percentage.PercentagePoint,
                        Accrual_Day_Count = deal.Accrual_Day_Count,
                        Discounted        = YesNo.No
                    };

                    fCashflowList.Items.Add(cashflow);
                }
            }

            IRBaseDealSkin.ApplyRateSchedule(fCashflowList.Items, deal.Coupon_Rate_Schedule, Percentage.PercentagePoint, holidayCalendar, DateAdjustmentMethod.Modified_Following);

            // Calculate fixed interest cashflows.
            fCashflowList.CalculateInterest(baseDate);

            fFixedCashflowList = PrincipalCashflows(priceDate, issueDate, maturityDate, PrincipalExchange.Start_Maturity, notional, deal.Amortisation, 1.0);

            fSettlementAmount = 0.0;
            fAccrued          = 0.0;
            bool payDatesRequired = requiredResults.CashRequired();

            if (settlementDate >= baseDate)
            {
                double settlementPrincipal = CFFixedInterestListValuation.GetPrincipal(fCashflowList, settlementDate);
                fSettlementAmount = settlementPrincipal * deal.Price * Percentage.PercentagePoint;

                for (int i = 0; i < fCashflowList.Items.Count; ++i)
                {
                    CFFixedInterest cashflow = fCashflowList[i];

                    if (cashflow.Accrual_Start_Date >= settlementDate)
                    {
                        break;
                    }

                    if (settlementDate < cashflow.Accrual_End_Date)
                    {
                        fAccrued += cashflow.Interest() * (settlementDate - cashflow.Accrual_Start_Date) / (cashflow.Accrual_End_Date - cashflow.Accrual_Start_Date);
                    }
                }

                if (deal.Price_Is_Clean == YesNo.Yes)
                {
                    fSettlementAmount += fAccrued; // add accrued interest
                }
                fT.AddPayDate(settlementDate, payDatesRequired);
            }

            // Add the floating and fixed cashflow dates to the time grid.
            fT.AddPayDates <CFFixedInterest>(fCashflowList, payDatesRequired);
            fT.AddPayDates <CFFixed>(fFixedCashflowList, payDatesRequired);

            // We only need an option pricer if callable on or after the settlement date.
            fSwaptionPricer = null;
            if (lastCallDate >= settlementDate)
            {
                // Snap call dates to grid of reset dates and
                // ensure that first call date is on or after settlement date
                int iLast = resetDates.IndexOfNextDate(lastCallDate);
                lastCallDate = resetDates[iLast];
                int iFirst = resetDates.IndexOfNextDate(firstCallDate);

                while ((iFirst < resetDates.Count - 1) && (resetDates[iFirst] < settlementDate))
                {
                    // move first exercise date forward
                    iFirst++;
                }

                firstCallDate = resetDates[iFirst];
                int      paySign      = deal.Call_Put == OptionType.Put ? +1 : -1;
                RateList exerciseFees = new RateList();

                foreach (Rate price in deal.Call_Prices)
                {
                    Rate fee = new Rate();
                    fee.Value = paySign * (Percentage.OverPercentagePoint - price.Value);
                    fee.Date  = price.Date;
                    exerciseFees.Add(fee);
                }

                var amortisation = AllocateAmortisationToPaymentDates <CFFixedInterest>(deal.Amortisation, fCashflowList.Items);

                fSwaptionPricer = new SwaptionPricer(issueDate, maturityDate, couponInterval, couponInterval,
                                                     deal.Accrual_Day_Count, holidayCalendar, DayCount.ACT_365, holidayCalendar, firstCallDate, lastCallDate, baseDate,
                                                     paySign, paySign, 0.0, null, notional, amortisation, deal.Coupon_Rate, null, deal.Coupon_Rate_Schedule, exerciseFees,
                                                     null, OptionStyle2.Bermudan, Max_Nodes, Step_Size, fT, true, requiredResults.CashRequired());
            }

            if (NeedSurvivalProb())
            {
                fRecoveryList = new CFRecoveryList();
                fRecoveryList.PopulateRecoveryCashflowList(baseDate, settlementDate, fCashflowList);
            }
        }