// Kahl and Jackel (2005) Heston price, using an integration range of [0,1].
        // From "Not-so-Complex Algorithms in the Heston model"
        public double HestonPriceKahlJackel(HParam param, OpSet settings, double[] X, double[] W)
        {
            int    N      = X.Length;
            double kappa  = param.kappa;
            double theta  = param.theta;
            double sigma  = param.sigma;
            double v0     = param.v0;
            double rho    = param.rho;
            double lambda = param.lambda;

            double S       = settings.S;
            double K       = settings.K;
            double T       = settings.T;
            double r       = settings.r;
            double q       = settings.q;
            string PutCall = settings.PutCall;

            // Forward price
            double F = S * Math.Exp((r - q) * T);

            // Required quantities for integrand
            double Cinf = Math.Sqrt(1 - rho * rho) / sigma * (v0 + kappa * theta * T);

            // Required quantities for f1
            double ImC1 = (Math.Exp((rho * sigma - kappa) * T) * theta * kappa + theta * kappa * ((kappa - rho * sigma) * T - 1.0)) / 2.0 / Math.Pow(kappa - rho * sigma, 2);
            double ImD1 = (1.0 - Math.Exp((rho * sigma - kappa) * T)) / 2.0 / (kappa - rho * sigma);

            // Required quantities for f2
            double ImC2 = -(Math.Exp(-kappa * T) * theta * kappa + theta * kappa * (kappa * T - 1.0)) / 2.0 / kappa / kappa;
            double ImD2 = -(1.0 - Math.Exp(-kappa * T / 2.0)) / 2.0 / kappa;

            double pi = Math.PI;

            double[]    y  = new double[N];
            double[]    z  = new double[N];
            HestonPrice HP = new HestonPrice();

            for (int u = 0; u <= N - 1; u++)
            {
                // Transformation of the abscissa from [-1,1] to [0,1]
                double x = 0.5 * X[u] + 0.5;
                if (x == 0.0)
                {
                    // Integrand at left abscissa 0
                    y[u] = 0.5 * (F - K);
                }
                else if (x == 1.0)
                {
                    // Integrand at right abscissa 1
                    double f1 = Math.Log(F / K) + ImC1 + ImD1 * v0;
                    double f2 = Math.Log(F / K) + ImC2 + ImD2 * v0;
                    y[u] = 0.5 * (F - K) + (F * f1 - K * f1) / (pi * Cinf);
                }
                else
                {
                    // Integrand at remaining abscissas
                    double f1 = HP.HestonProb(-Math.Log(x) / Cinf, param, settings, 1);
                    double f2 = HP.HestonProb(-Math.Log(x) / Cinf, param, settings, 2);
                    y[u] = 0.5 * (F - K) + (F * f1 - K * f2) / (x * pi * Cinf);
                }
                // Multiply by the weights
                z[u] = W[u] * y[u];
            }
            // The Call price
            double HCall = Math.Exp(-r * T) * 0.5 * z.Sum();

            // The put price by put call parity
            if (PutCall == "C")
            {
                return(HCall);
            }
            else
            {
                return(HCall - S * Math.Exp(-q * T) + K * Math.Exp(-r * T));
            }
        }
        static void Main(string[] args)
        {
            // 32-point Gauss-Laguerre Abscissas and weights
            double[] xGLa = new Double[32];
            double[] wGLa = new Double[32];
            using (TextReader reader = File.OpenText("../../GaussLaguerre32.txt"))
            {
                for (int k = 0; k <= 31; k++)
                {
                    string   text = reader.ReadLine();
                    string[] bits = text.Split(' ');
                    xGLa[k] = double.Parse(bits[0]);
                    wGLa[k] = double.Parse(bits[1]);
                }
            }
            // 32-point Gauss-Legendre Abscissas and weights
            double[] xGLe = new Double[32];
            double[] wGLe = new Double[32];
            using (TextReader reader = File.OpenText("../../GaussLegendre32.txt"))
            {
                for (int k = 0; k <= 31; k++)
                {
                    string   text = reader.ReadLine();
                    string[] bits = text.Split(' ');
                    xGLe[k] = double.Parse(bits[0]);
                    wGLe[k] = double.Parse(bits[1]);
                }
            }
            // 32-point Gauss-Lobatto Abscissas and weights
            double[] xGLo = new Double[32];
            double[] wGLo = new Double[32];
            using (TextReader reader = File.OpenText("../../GaussLobatto32.txt"))
            {
                for (int k = 0; k <= 31; k++)
                {
                    string   text = reader.ReadLine();
                    string[] bits = text.Split(' ');
                    xGLo[k] = double.Parse(bits[0]);
                    wGLo[k] = double.Parse(bits[1]);
                }
            }

            // Heston parameters
            HParam param = new HParam();

            param.kappa  = 1.0;
            param.theta  = 0.06;
            param.sigma  = 0.5;
            param.v0     = 0.06;
            param.rho    = -0.8;
            param.lambda = 0.0;

            // Option price settings
            OpSet settings = new OpSet();

            settings.S       = 10.0;
            settings.K       = 7.0;
            settings.T       = 1.0 / 12.0;
            settings.r       = 0.06;
            settings.q       = 0.04;
            settings.trap    = 1;
            settings.PutCall = "C";

            // Lower and upper integration limits
            double a = 0.0;         // Lower Limit for Gauss Legendre
            double b = 100.0;       // Upper Limit for Gauss Legendre
            double A = 1e-5;        // Lower Limit for Gauss Lobatto
            double B = 100.0;       // Upper Limit for Gauss Lobatto

            // Initialize the price vectors
            HestonPrice HP       = new HestonPrice();
            KahlJackel  KJ       = new KahlJackel();
            double      PriceGLa = HP.HestonPriceGaussLaguerre(param, settings, xGLa, wGLa);       // Heston Gauss Laguerre
            double      PriceGLe = HP.HestonPriceGaussLegendre(param, settings, xGLe, wGLe, a, b); // Heston Gauss Legendre
            double      PriceGLo = HP.HestonPriceGaussLegendre(param, settings, xGLo, wGLo, A, B); // Heston Gauss Lobatto
            double      PriceKJ  = KJ.HestonPriceKahlJackel(param, settings, xGLo, wGLo);          // Kahl Jackel Gauss Lobatto

            Console.WriteLine("Kahl Jackel Integration scheme");
            Console.WriteLine("Method                       Price");
            Console.WriteLine("-------------------------------------");
            Console.WriteLine("Heston Gauss Laguerre       {0:F5}", PriceGLa);
            Console.WriteLine("Heston Gauss Legendre       {0:F5}", PriceGLe);
            Console.WriteLine("Heston Gauss Lobatto        {0:F5}", PriceGLo);
            Console.WriteLine("Kahl Jackel Gauss Lobatto   {0:F5}", PriceKJ);
            Console.WriteLine("-------------------------------------");
        }