public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.settlementMethod != Settlement.Method.ParYieldCurve, () =>
                             "cash-settled (ParYieldCurve) swaptions not priced by Jamshidian engine");

            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () =>
                             "cannot use the Jamshidian decomposition on exotic swaptions");

            Utils.QL_REQUIRE(arguments_.swap.spread.IsEqual(0.0), () =>
                             "non zero spread (" + arguments_.swap.spread + ") not allowed");

            Date       referenceDate;
            DayCounter dayCounter;

            ITermStructureConsistentModel tsmodel = (ITermStructureConsistentModel)base.model_.link;

            try
            {
                if (tsmodel != null)
                {
                    referenceDate = tsmodel.termStructure().link.referenceDate();
                    dayCounter    = tsmodel.termStructure().link.dayCounter();
                }
                else
                {
                    referenceDate = termStructure_.link.referenceDate();
                    dayCounter    = termStructure_.link.dayCounter();
                }
            }
            catch
            {
                referenceDate = termStructure_.link.referenceDate();
                dayCounter    = termStructure_.link.dayCounter();
            }

            List <double> amounts = new InitializedList <double>(arguments_.fixedCoupons.Count);

            for (int i = 0; i < amounts.Count; i++)
            {
                amounts[i] = arguments_.fixedCoupons[i];
            }
            amounts[amounts.Count - 1] = amounts.Last() + arguments_.nominal;

            double maturity = dayCounter.yearFraction(referenceDate,
                                                      arguments_.exercise.date(0));

            List <double> fixedPayTimes = new InitializedList <double>(arguments_.fixedPayDates.Count);

            for (int i = 0; i < fixedPayTimes.Count; i++)
            {
                fixedPayTimes[i] =
                    dayCounter.yearFraction(referenceDate,
                                            arguments_.fixedPayDates[i]);
            }

            rStarFinder finder = new rStarFinder(model_, arguments_.nominal, maturity,
                                                 fixedPayTimes, amounts);
            Brent  s1d       = new Brent();
            double minStrike = -10.0;
            double maxStrike = 10.0;

            s1d.setMaxEvaluations(10000);
            s1d.setLowerBound(minStrike);
            s1d.setUpperBound(maxStrike);
            double rStar = s1d.solve(finder, 1e-8, 0.05, minStrike, maxStrike);

            Option.Type w = arguments_.type == VanillaSwap.Type.Payer ?
                            Option.Type.Put : Option.Type.Call;
            int size = arguments_.fixedCoupons.Count;

            double value = 0.0;

            for (int i = 0; i < size; i++)
            {
                double fixedPayTime =
                    dayCounter.yearFraction(referenceDate,
                                            arguments_.fixedPayDates[i]);
                double strike = model_.link.discountBond(maturity,
                                                         fixedPayTime,
                                                         rStar);
                double dboValue = model_.link.discountBondOption(
                    w, strike, maturity,
                    fixedPayTime);
                value += amounts[i] * dboValue;
            }
            results_.value = value;
        }
        public FdmHestonVarianceMesher(int size,
                                       HestonProcess process,
                                       double maturity,
                                       int tAvgSteps  = 10,
                                       double epsilon = 0.0001)
            : base(size)
        {
            List <double> vGrid = new InitializedList <double>(size, 0.0);
            List <double> pGrid = new InitializedList <double>(size, 0.0);

            double df = 4.0 * process.theta() * process.kappa() /
                        Math.Pow(process.sigma(), 2);

            try
            {
                List <pair_double> grid = new List <pair_double>();

                for (int l = 1; l <= tAvgSteps; ++l)
                {
                    double t   = (maturity * l) / tAvgSteps;
                    double ncp = 4 * process.kappa() * Math.Exp(-process.kappa() * t)
                                 / (Math.Pow(process.sigma(), 2)
                                    * (1 - Math.Exp(-process.kappa() * t))) * process.v0();
                    double k = Math.Pow(process.sigma(), 2)
                               * (1 - Math.Exp(-process.kappa() * t)) / (4 * process.kappa());

                    double qMin = 0.0; // v_min = 0.0;
                    double qMax = Math.Max(process.v0(),
                                           k * new InverseNonCentralCumulativeChiSquareDistribution(
                                               df, ncp, 100, 1e-8).value(1 - epsilon));

                    double minVStep = (qMax - qMin) / (50 * size);
                    double ps, p = 0.0;

                    double vTmp = qMin;
                    grid.Add(new pair_double(qMin, epsilon));

                    for (int i = 1; i < size; ++i)
                    {
                        ps = (1 - epsilon - p) / (size - i);
                        p += ps;
                        double tmp = k * new InverseNonCentralCumulativeChiSquareDistribution(
                            df, ncp, 100, 1e-8).value(p);

                        double vx = Math.Max(vTmp + minVStep, tmp);
                        p    = new NonCentralCumulativeChiSquareDistribution(df, ncp).value(vx / k);
                        vTmp = vx;
                        grid.Add(new pair_double(vx, p));
                    }
                }
                Utils.QL_REQUIRE(grid.Count == size * tAvgSteps,
                                 () => "something wrong with the grid size");

                grid.Sort();

                List <Pair <double, double> > tp = new List <Pair <double, double> >(grid);

                for (int i = 0; i < size; ++i)
                {
                    int b = (i * tp.Count) / size;
                    int e = ((i + 1) * tp.Count) / size;
                    for (int j = b; j < e; ++j)
                    {
                        vGrid[i] += tp[j].first / (e - b);
                        pGrid[i] += tp[j].second / (e - b);
                    }
                }
            }
            catch (Exception)
            {
                // use default mesh
                double vol = process.sigma() *
                             Math.Sqrt(process.theta() / (2 * process.kappa()));

                double mean       = process.theta();
                double upperBound = Math.Max(process.v0() + 4 * vol, mean + 4 * vol);
                double lowerBound
                    = Math.Max(0.0, Math.Min(process.v0() - 4 * vol, mean - 4 * vol));

                for (int i = 0; i < size; ++i)
                {
                    pGrid[i] = i / (size - 1.0);
                    vGrid[i] = lowerBound + i * (upperBound - lowerBound) / (size - 1.0);
                }
            }

            double skewHint = ((process.kappa() != 0.0)
                    ? Math.Max(1.0, process.sigma() / process.kappa()) : 1.0);

            pGrid.Sort();
            volaEstimate_ = new GaussLobattoIntegral(100000, 1e-4).value(
                new interpolated_volatility(pGrid, vGrid).value,
                pGrid.First(),
                pGrid.Last()) * Math.Pow(skewHint, 1.5);

            double v0 = process.v0();

            for (int i = 1; i < vGrid.Count; ++i)
            {
                if (vGrid[i - 1] <= v0 && vGrid[i] >= v0)
                {
                    if (Math.Abs(vGrid[i - 1] - v0) < Math.Abs(vGrid[i] - v0))
                    {
                        vGrid[i - 1] = v0;
                    }
                    else
                    {
                        vGrid[i] = v0;
                    }
                }
            }
            locations_ = vGrid.Select(x => x).ToList();
            for (int i = 0; i < size - 1; ++i)
            {
                dminus_[i + 1] = dplus_[i] = vGrid[i + 1] - vGrid[i];
            }
            dplus_[dplus_.Count - 1] = null;
            dminus_[0] = null;
        }