/// <summary>
        /// Implied volatility from option price. (Inversion of the two step quadrature formula.)
        /// </summary>
        /// <param name="maturity">option maturity</param>
        /// <param name="strike">option strike</param>
        /// <param name="price">option price </param>
        /// <param name="q">option type : 1 for call, -1 for put</param>
        /// <returns></returns>
        public double ImpliedVol(double maturity, double strike, double price, double q)
        {
            //Proxy using Lehman Formula
            double proxyFwd, proxyDk;

            affineDivUtils.LehmanProxy(maturity, spot, out proxyFwd, out proxyDk);
            double lehmanTargetVol = BlackScholesOption.ImpliedVol(price, proxyFwd, strike + proxyDk, maturity, q);

            var pricer = new BsDivPrice(maturity, strike, spot, affineDivUtils, quadPoints, quadWeights);
            Func <double, double> volToLehmanVolErr =
                v => BlackScholesOption.ImpliedVol(pricer.Price(v, q), proxyFwd, strike + proxyDk, maturity, q) - lehmanTargetVol;

            //Solve
            double v1   = lehmanTargetVol;
            double err1 = volToLehmanVolErr(v1);

            double v2   = lehmanTargetVol - err1;
            double err2 = volToLehmanVolErr(v2);
            double v3   = v1 - err1 * (v2 - v1) / (err2 - err1);

            if (Math.Abs(v3 - v2) < v2 * 1.0e-13)
            {
                return(v3);
            }
            double err3 = volToLehmanVolErr(v3);
            double v4   = RootUtils.TrinomRoot(v1, v2, v3, err1, err2, err3);

            if (Math.Abs(v4 - v3) < v3 * 1.0e-13)
            {
                return(v4);
            }
            double err4 = volToLehmanVolErr(v4);
            double v5   = RootUtils.TrinomRoot(v2, v3, v4, err2, err3, err4);

            return(v5);
        }
        public double[] CalibrateVol(double[] maturities, double[] targetPrices, double[] strikes, double[] optionTypes)
        {
            Contract.Requires(EnumerableUtils.IsSorted(maturities));
            Contract.Requires(maturities.Length == strikes.Length &&
                              strikes.Length == targetPrices.Length &&
                              targetPrices.Length == optionTypes.Length);

            double[] variances = new double[maturities.Length + 1];
            double[] varPillars = new[] { 0.0 }.Concat(maturities).ToArray();

            double[] calibVols = new double[maturities.Length];
            for (int step = 0; step < maturities.Length; step++)
            {
                var maturity    = maturities[step];
                var strike      = strikes[step];
                var targetPrice = targetPrices[step];
                var q           = optionTypes[step];

                //Proxy using Lehman Formula
                double proxyFwd, proxyDk;
                affineDivUtils.LehmanProxy(maturity, spot, out proxyFwd, out proxyDk);
                double lehmanTargetVol = BlackScholesOption.ImpliedVol(targetPrice, proxyFwd, strike + proxyDk, maturity, q);

                var pricer = new BsDivPrice(maturities[step], strikes[step], spot, affineDivUtils, quadPoints, quadWeights);
                Func <double, double> volToLehmanVolErr = v =>
                {
                    variances[1 + step] = v * v * maturity;
                    var varFunc = RrFunctions.LinearInterpolation(varPillars, variances);

                    Func <double, double> volFunc = t => Math.Sqrt(varFunc.Eval(t) / t);
                    var price = pricer.Price(volFunc, q);
                    return(BlackScholesOption.ImpliedVol(price, proxyFwd, strike + proxyDk, maturity, q) - lehmanTargetVol);
                };//TODO use a cache


                //Bracket & Solve
                double v1 = lehmanTargetVol;
                double v2;
                if (step == 0)
                {
                    v2 = lehmanTargetVol - volToLehmanVolErr(lehmanTargetVol);
                }
                else
                {
                    var volIfZeroVolOnStep = Math.Sqrt(calibVols[step - 1] * calibVols[step - 1] * maturities[step - 1] / maturities[step]);
                    var minError           = volToLehmanVolErr(volIfZeroVolOnStep);
                    if (minError > 0.0) //saturation case
                    {
                        calibVols[step]     = volIfZeroVolOnStep;
                        variances[1 + step] = volIfZeroVolOnStep * volIfZeroVolOnStep * maturity;
                        continue;
                    }
                    v2 = volIfZeroVolOnStep;
                }

                if (!RootUtils.Bracket(volToLehmanVolErr, v1, v2, out v1, out v2))
                {
                    throw new Exception("Failed to inverse vol");
                }
                var impliedVol = RootUtils.Brenth(volToLehmanVolErr, v1, v2, 1.0e-10, 2.0 * DoubleUtils.MachineEpsilon, 10);

                calibVols[step]     = impliedVol;
                variances[1 + step] = impliedVol * impliedVol * maturity;
            }

            return(calibVols);
        }