public void testH1HWPricingEngine()
        {
            /*
             * Example taken from Lech Aleksander Grzelak,
             * Equity and Foreign Exchange Hybrid Models for Pricing Long-Maturity
             * Financial Derivatives,
             * http://repository.tudelft.nl/assets/uuid:a8e1a007-bd89-481a-aee3-0e22f15ade6b/PhDThesis_main.pdf
             */
            Date today = new Date(15, Month.July, 2012);

            Settings.Instance.setEvaluationDate(today);
            Date       exerciseDate = new Date(13, Month.July, 2022);
            DayCounter dc           = new Actual365Fixed();

            Exercise exercise = new EuropeanExercise(exerciseDate);

            Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100.0));

            double r       = 0.02;
            double q       = 0.00;
            double v0      = 0.05;
            double theta   = 0.05;
            double kappa_v = 0.3;

            double[] sigma_v = { 0.3, 0.6 };
            double   rho_sv  = -0.30;
            double   rho_sr  = 0.6;
            double   kappa_r = 0.01;
            double   sigma_r = 0.01;

            Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, r, dc));
            Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, q, dc));

            Handle <BlackVolTermStructure> flatVolTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, 0.20, dc));
            GeneralizedBlackScholesProcess bsProcess = new GeneralizedBlackScholesProcess(s0, qTS, rTS, flatVolTS);

            HullWhiteProcess hwProcess      = new HullWhiteProcess(rTS, kappa_r, sigma_r);
            HullWhite        hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), kappa_r, sigma_r);

            double tol = 0.0001;

            double[]   strikes  = { 40, 80, 100, 120, 180 };
            double[][] expected =
            {
                new double[]  { 0.267503, 0.235742, 0.228223, 0.223461, 0.217855 },
                new double[]  { 0.263626, 0.211625, 0.199907, 0.193502, 0.190025 }
            };

            for (int j = 0; j < sigma_v.Length; ++j)
            {
                HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v0, kappa_v, theta, sigma_v[j], rho_sv);
                HestonModel   hestonModel   = new HestonModel(hestonProcess);

                for (int i = 0; i < strikes.Length; ++i)
                {
                    StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, strikes[i]);

                    VanillaOption option = new VanillaOption(payoff, exercise);

                    IPricingEngine analyticH1HWEngine = new AnalyticH1HWEngine(hestonModel, hullWhiteModel, rho_sr, 144);
                    option.setPricingEngine(analyticH1HWEngine);
                    double impliedH1HW = option.impliedVolatility(option.NPV(), bsProcess);

                    if (Math.Abs(expected[j][i] - impliedH1HW) > tol)
                    {
                        QAssert.Fail("Failed to reproduce H1HW implied volatility"
                                     + "\n   expected       : " + expected[j][i]
                                     + "\n   calculated     : " + impliedH1HW
                                     + "\n   tol            : " + tol
                                     + "\n   strike         : " + strikes[i]
                                     + "\n   sigma          : " + sigma_v[j]);
                    }
                }
            }
        }