public override void calculate()
        {
            if (arguments_.barrierType == DoubleBarrier.Type.KIKO ||
                arguments_.barrierType == DoubleBarrier.Type.KOKI)
            {
                AmericanExercise ex = arguments_.exercise as AmericanExercise;
                Utils.QL_REQUIRE(ex != null, () => "KIKO/KOKI options must have American exercise");
                Utils.QL_REQUIRE(ex.dates()[0] <=
                                 process_.blackVolatility().currentLink().referenceDate(),
                                 () => "American option with window exercise not handled yet");
            }
            else
            {
                EuropeanExercise ex = arguments_.exercise as EuropeanExercise;
                Utils.QL_REQUIRE(ex != null, () => "non-European exercise given");
            }
            CashOrNothingPayoff payoff = arguments_.payoff as CashOrNothingPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "a cash-or-nothing payoff must be given");

            double spot = process_.stateVariable().currentLink().value();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");

            double variance =
                process_.blackVolatility().currentLink().blackVariance(
                    arguments_.exercise.lastDate(),
                    payoff.strike());
            double barrier_lo = arguments_.barrier_lo.Value;
            double barrier_hi = arguments_.barrier_hi.Value;

            DoubleBarrier.Type barrierType = arguments_.barrierType;
            Utils.QL_REQUIRE(barrier_lo > 0.0,
                             () => "positive low barrier value required");
            Utils.QL_REQUIRE(barrier_hi > 0.0,
                             () => "positive high barrier value required");
            Utils.QL_REQUIRE(barrier_lo < barrier_hi,
                             () => "barrier_lo must be < barrier_hi");
            Utils.QL_REQUIRE(barrierType == DoubleBarrier.Type.KnockIn ||
                             barrierType == DoubleBarrier.Type.KnockOut ||
                             barrierType == DoubleBarrier.Type.KIKO ||
                             barrierType == DoubleBarrier.Type.KOKI,
                             () => "Unsupported barrier type");

            // degenerate cases
            switch (barrierType)
            {
            case DoubleBarrier.Type.KnockOut:
                if (spot <= barrier_lo || spot >= barrier_hi)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KnockIn:
                if (spot <= barrier_lo || spot >= barrier_hi)
                {
                    // knocked in - pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KIKO:
                if (spot >= barrier_hi)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                else if (spot <= barrier_lo)
                {
                    // knocked in, pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KOKI:
                if (spot <= barrier_lo)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                else if (spot >= barrier_hi)
                {
                    // knocked in, pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;
            }

            AnalyticDoubleBarrierBinaryEngineHelper helper = new AnalyticDoubleBarrierBinaryEngineHelper(process_,
                                                                                                         payoff, arguments_);

            switch (barrierType)
            {
            case DoubleBarrier.Type.KnockOut:
            case DoubleBarrier.Type.KnockIn:
                results_.value = helper.payoffAtExpiry(spot, variance, barrierType);
                break;

            case DoubleBarrier.Type.KIKO:
            case DoubleBarrier.Type.KOKI:
                results_.value = helper.payoffKIKO(spot, variance, barrierType);
                break;

            default:
                results_.value = null;
                break;
            }
        }
        public override void calculate()
        {
            AmericanExercise ex = arguments_.exercise as AmericanExercise;

            Utils.QL_REQUIRE(ex != null, () => "non-American exercise given");
            Utils.QL_REQUIRE(ex.payoffAtExpiry(), () => "payoff must be at expiry");
            Utils.QL_REQUIRE(ex.dates()[0] <= process_.blackVolatility().link.referenceDate(), () =>
                             "American option with window exercise not handled yet");

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given");

            double spot = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");

            double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike());
            double?barrier  = arguments_.barrier;

            Utils.QL_REQUIRE(barrier > 0.0, () => "positive barrier value required");
            Barrier.Type barrierType = arguments_.barrierType;

            // KO degenerate cases
            if ((barrierType == Barrier.Type.DownOut && spot <= barrier) ||
                (barrierType == Barrier.Type.UpOut && spot >= barrier))
            {
                // knocked out, no value
                results_.value       = 0;
                results_.delta       = 0;
                results_.gamma       = 0;
                results_.vega        = 0;
                results_.theta       = 0;
                results_.rho         = 0;
                results_.dividendRho = 0;
                return;
            }

            // KI degenerate cases
            if ((barrierType == Barrier.Type.DownIn && spot <= barrier) ||
                (barrierType == Barrier.Type.UpIn && spot >= barrier))
            {
                // knocked in - is a digital european
                Exercise exercise = new EuropeanExercise(arguments_.exercise.lastDate());

                IPricingEngine engine = new AnalyticEuropeanEngine(process_);

                VanillaOption opt = new VanillaOption(payoff, exercise);
                opt.setPricingEngine(engine);
                results_.value       = opt.NPV();
                results_.delta       = opt.delta();
                results_.gamma       = opt.gamma();
                results_.vega        = opt.vega();
                results_.theta       = opt.theta();
                results_.rho         = opt.rho();
                results_.dividendRho = opt.dividendRho();
                return;
            }

            double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate());

            AnalyticBinaryBarrierEngine_helper helper = new AnalyticBinaryBarrierEngine_helper(
                process_, payoff, ex, arguments_);

            results_.value = helper.payoffAtExpiry(spot, variance, riskFreeDiscount);
        }