void FdSimpleKlugeExtOUVPPEngine::calculate() const {

        boost::shared_ptr<SwingExercise> swingExercise(
            boost::dynamic_pointer_cast<SwingExercise>(arguments_.exercise));

        QL_REQUIRE(swingExercise, "Swing exercise supported only");

        const Size nStates = (arguments_.nStarts == Null<Size>())
            ? 2*arguments_.tMinUp + arguments_.tMinDown
            : (arguments_.nStarts+1)*(2*arguments_.tMinUp+arguments_.tMinDown);

        // 1. Layout
        std::vector<Size> dim;
        dim.push_back(xGrid_);
        dim.push_back(yGrid_);
        dim.push_back(gGrid_);
        dim.push_back(nStates);
        const boost::shared_ptr<FdmLinearOpLayout> layout(
            new FdmLinearOpLayout(dim));

        const std::vector<Time> exerciseTimes
            = swingExercise->exerciseTimes(rTS_->dayCounter(),
                                           rTS_->referenceDate());

        // 2. mesher set-up
        const Time maturity = exerciseTimes.back();
        const boost::shared_ptr<ExtOUWithJumpsProcess> klugeProcess
            = process_->getKlugeProcess();

        const boost::shared_ptr<StochasticProcess1D> klugeOUProcess
            = klugeProcess->getExtendedOrnsteinUhlenbeckProcess();

        const boost::shared_ptr<Fdm1dMesher> xMesher(
            new FdmSimpleProcess1dMesher(xGrid_, klugeOUProcess, maturity));

        const boost::shared_ptr<Fdm1dMesher> yMesher(
            new ExponentialJump1dMesher(yGrid_,
                                        klugeProcess->beta(),
                                        klugeProcess->jumpIntensity(),
                                        klugeProcess->eta(), 1e-3));

        const boost::shared_ptr<Fdm1dMesher> gMesher(
            new FdmSimpleProcess1dMesher(gGrid_,
                                         process_->getExtOUProcess(),maturity));

        const boost::shared_ptr<Fdm1dMesher> exerciseMesher(
            new Uniform1dMesher(0.0, nStates-1.0, nStates));

        std::vector<boost::shared_ptr<Fdm1dMesher> > meshers;
        meshers.push_back(xMesher);
        meshers.push_back(yMesher);
        meshers.push_back(gMesher);
        meshers.push_back(exerciseMesher);
        const boost::shared_ptr<FdmMesher> mesher (
            new FdmMesherComposite(layout, meshers));

        // 3. Calculator
        const boost::shared_ptr<FdmInnerValueCalculator> zeroInnerValue(
            new FdmZeroInnerValue());

        const boost::shared_ptr<Payoff> zeroStrikeCall(
            new PlainVanillaPayoff(Option::Call, 0.0));

        const boost::shared_ptr<FdmInnerValueCalculator> gasPrice(
            new FdmExpExtOUInnerValueCalculator(zeroStrikeCall,
                                                mesher, gasShape_, 2));

        const boost::shared_ptr<FdmInnerValueCalculator> powerPrice(
            new FdmExtOUJumpModelInnerValue(zeroStrikeCall,mesher,powerShape_));

        const boost::shared_ptr<FdmInnerValueCalculator> sparkSpread(
            new FdmSparkSpreadInnerValue(
                boost::dynamic_pointer_cast<BasketPayoff>(arguments_.payoff),
                gasPrice, powerPrice));

        // 4. Step conditions
        std::list<std::vector<Time> > stoppingTimes;
        std::list<boost::shared_ptr<StepCondition<Array> > > stepConditions;

        // 4.1 Bermudan step conditions
        stoppingTimes.push_back(exerciseTimes);
        stepConditions.push_back(boost::shared_ptr<StepCondition<Array> >(
            new FdmVPPStepCondition(arguments_.heatRate,
                                    arguments_.pMin, arguments_.pMax,
                                    arguments_.tMinUp, arguments_.tMinDown,
                                    arguments_.nStarts,
                                    arguments_.startUpFuel,
                                    arguments_.startUpFixCost,
                                    carbonPrice_, 3,
                                    mesher, gasPrice, sparkSpread)));
        
        boost::shared_ptr<FdmStepConditionComposite> conditions(
                new FdmStepConditionComposite(stoppingTimes, stepConditions));

        // 5. Boundary conditions
        const std::vector<boost::shared_ptr<FdmDirichletBoundary> > boundaries;

        // 6. set-up solver
        FdmSolverDesc solverDesc = { mesher, boundaries, conditions,
                                     zeroInnerValue, maturity, tGrid_, 0 };

        const boost::shared_ptr<FdmKlugeExtOUSolver<4> > solver(
            new FdmKlugeExtOUSolver<4>(Handle<KlugeExtOUProcess>(process_),
                                       rTS_, solverDesc, schemeDesc_));

        std::vector<Real> x(4);
        x[0] = process_->initialValues()[0];
        x[1] = process_->initialValues()[1];
        x[2] = process_->initialValues()[2];
        
        const Real tol = 1e-8;
        Array results(2*arguments_.tMinUp + arguments_.tMinDown);
        for (Size i=0; i < results.size(); ++i) {

            x[3] = std::max(tol, std::min(
                (Real) (nStates-results.size()+i), nStates-1-tol));
            results[i] = solver->valueAt(x);
        }
        results_.value = *std::max_element(results.begin(), results.end());
    }
    void FdSimpleExtOUJumpSwingEngine::calculate() const {

        boost::shared_ptr<SwingExercise> swingExercise(
            boost::dynamic_pointer_cast<SwingExercise>(arguments_.exercise));

        QL_REQUIRE(swingExercise, "Swing exercise supported only");

        // 1. Layout
        std::vector<Size> dim;
        dim.push_back(xGrid_);
        dim.push_back(yGrid_);
        dim.push_back(arguments_.maxExerciseRights+1);
        const boost::shared_ptr<FdmLinearOpLayout> layout(
                                            new FdmLinearOpLayout(dim));

        // 2. Mesher
        const std::vector<Time> exerciseTimes
            = swingExercise->exerciseTimes(rTS_->dayCounter(),
                                           rTS_->referenceDate());

        const Time maturity = exerciseTimes.back();
        const boost::shared_ptr<StochasticProcess1D> ouProcess(
                              process_->getExtendedOrnsteinUhlenbeckProcess());
        const boost::shared_ptr<Fdm1dMesher> xMesher(
                     new FdmSimpleProcess1dMesher(xGrid_, ouProcess,maturity));

        const boost::shared_ptr<Fdm1dMesher> yMesher(
                        new ExponentialJump1dMesher(yGrid_,
                                                    process_->beta(),
                                                    process_->jumpIntensity(),
                                                    process_->eta()));
        const boost::shared_ptr<Fdm1dMesher> exerciseMesher(
                       new Uniform1dMesher(0, arguments_.maxExerciseRights,
                                              arguments_.maxExerciseRights+1));

        std::vector<boost::shared_ptr<Fdm1dMesher> > meshers;
        meshers.push_back(xMesher);
        meshers.push_back(yMesher);
        meshers.push_back(exerciseMesher);
        const boost::shared_ptr<FdmMesher> mesher (
                                   new FdmMesherComposite(layout, meshers));

        // 3. Calculator
        boost::shared_ptr<FdmInnerValueCalculator> calculator(
                                                    new FdmZeroInnerValue());
        // 4. Step conditions
        std::list<boost::shared_ptr<StepCondition<Array> > > stepConditions;
        std::list<std::vector<Time> > stoppingTimes;

        // 4.1 Bermudan step conditions
        stoppingTimes.push_back(exerciseTimes);

        const boost::shared_ptr<StrikedTypePayoff> payoff =
            boost::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
        boost::shared_ptr<FdmInnerValueCalculator> exerciseCalculator(
                      new FdmExtOUJumpModelInnerValue(payoff, mesher, shape_));

        stepConditions.push_back(boost::shared_ptr<StepCondition<Array> >(
                new FdmSimpleSwingCondition(exerciseTimes, mesher,
                                            exerciseCalculator, 2)));

        boost::shared_ptr<FdmStepConditionComposite> conditions(
                new FdmStepConditionComposite(stoppingTimes, stepConditions));


        // 5. Boundary conditions
        const std::vector<boost::shared_ptr<FdmDirichletBoundary> > boundaries;

        // 6. set-up solver
        FdmSolverDesc solverDesc = { mesher, boundaries, conditions,
                                     calculator, maturity, tGrid_, 0 };

        const boost::shared_ptr<FdmSimple3dExtOUJumpSolver> solver(
            new FdmSimple3dExtOUJumpSolver(
                                    Handle<ExtOUWithJumpsProcess>(process_),
                                    rTS_, solverDesc, schemeDesc_));

        const Real x = process_->initialValues()[0];
        const Real y = process_->initialValues()[1];

        std::vector< std::pair<Real, Real> > exerciseValues;
        for (Size i=arguments_.minExerciseRights;
             i <= arguments_.maxExerciseRights; ++i) {
            const Real z = Real(i);
            exerciseValues.push_back(std::pair<Real, Real>(
                                       solver->valueAt(x, y, z), z));
        }
        const Real z = std::max_element(exerciseValues.begin(),
                                        exerciseValues.end())->second;

        results_.value = solver->valueAt(x, y, z);
    }
    void FdSimpleKlugeExtOUVPPEngine::calculate() const {

        ext::shared_ptr<SwingExercise> swingExercise(
            ext::dynamic_pointer_cast<SwingExercise>(arguments_.exercise));

        QL_REQUIRE(swingExercise, "Swing exercise supported only");

        const FdmVPPStepConditionFactory stepConditionFactory(arguments_);

        // 1. Exercise definition
        const std::vector<Time> exerciseTimes
            = swingExercise->exerciseTimes(rTS_->dayCounter(),
                                           rTS_->referenceDate());

        // 2. mesher set-up
        const Time maturity = exerciseTimes.back();
        const ext::shared_ptr<ExtOUWithJumpsProcess> klugeProcess
            = process_->getKlugeProcess();

        const ext::shared_ptr<StochasticProcess1D> klugeOUProcess
            = klugeProcess->getExtendedOrnsteinUhlenbeckProcess();

        const ext::shared_ptr<Fdm1dMesher> xMesher(
            new FdmSimpleProcess1dMesher(xGrid_, klugeOUProcess, maturity));

        const ext::shared_ptr<Fdm1dMesher> yMesher(
            new ExponentialJump1dMesher(yGrid_,
                                        klugeProcess->beta(),
                                        klugeProcess->jumpIntensity(),
                                        klugeProcess->eta(), 1e-3));

        const ext::shared_ptr<Fdm1dMesher> gMesher(
            new FdmSimpleProcess1dMesher(gGrid_,
                                         process_->getExtOUProcess(),maturity));

        const ext::shared_ptr<Fdm1dMesher> exerciseMesher(
            stepConditionFactory.stateMesher());

        const ext::shared_ptr<FdmMesher> mesher (
            new FdmMesherComposite(xMesher, yMesher, gMesher, exerciseMesher));

        // 3. Calculator
        const ext::shared_ptr<FdmInnerValueCalculator> zeroInnerValue(
            new FdmZeroInnerValue());

        const ext::shared_ptr<Payoff> zeroStrikeCall(
            new PlainVanillaPayoff(Option::Call, 0.0));

        const ext::shared_ptr<FdmInnerValueCalculator> fuelPrice(
            new FdmExpExtOUInnerValueCalculator(zeroStrikeCall,
                                                mesher, fuelShape_, 2));

        const ext::shared_ptr<FdmInnerValueCalculator> powerPrice(
            new FdmExtOUJumpModelInnerValue(zeroStrikeCall,mesher,powerShape_));

        const ext::shared_ptr<FdmInnerValueCalculator> sparkSpread(
            new FdmSparkSpreadInnerValue(
                ext::dynamic_pointer_cast<BasketPayoff>(arguments_.payoff),
                fuelPrice, powerPrice));

        // 4. Step conditions
        std::list<std::vector<Time> > stoppingTimes;
        std::list<ext::shared_ptr<StepCondition<Array> > > stepConditions;

        // 4.1 Bermudan step conditions
        stoppingTimes.push_back(exerciseTimes);
        const FdmVPPStepConditionMesher mesh = { 3u, mesher };
        
        const ext::shared_ptr<FdmVPPStepCondition> stepCondition(
            stepConditionFactory.build(mesh, fuelCostAddon_,
                                       fuelPrice, sparkSpread));

        stepConditions.push_back(stepCondition);

        const ext::shared_ptr<FdmStepConditionComposite> conditions(
            new FdmStepConditionComposite(stoppingTimes, stepConditions));

        // 5. Boundary conditions
        const FdmBoundaryConditionSet boundaries;

        // 6. set-up solver
        FdmSolverDesc solverDesc = { mesher, boundaries, conditions,
                                     zeroInnerValue, maturity, tGrid_, 0 };

        const ext::shared_ptr<FdmKlugeExtOUSolver<4> > solver(
            new FdmKlugeExtOUSolver<4>(Handle<KlugeExtOUProcess>(process_),
                                       rTS_, solverDesc, schemeDesc_));

        std::vector<Real> x(4);
        x[0] = process_->initialValues()[0];
        x[1] = process_->initialValues()[1];
        x[2] = process_->initialValues()[2];
        
        const Real tol = 1e-8;
        const Real maxExerciseValue = exerciseMesher->locations().back();
        const Real minExerciseValue = exerciseMesher->locations().front();

        Array results(exerciseMesher->size());
        for (Size i=0; i < results.size(); ++i) {

            x[3] = std::max(minExerciseValue + tol,
                            std::min(exerciseMesher->location(i),
                                     maxExerciseValue - tol));
            results[i] = solver->valueAt(x);
        }
        results_.value = stepCondition->maxValue(results);
    }