packages feed

hasquant-0.8.0.0: cbits/qlPricingEngineAux.cpp

#include "qlCheckedFdEngine.h"
// this file intentionally does not contain any references to wrappers, only vanilla QuantLib is used here
#include <ql/pricingengines/all.hpp>
#include <ql/pricingengines/vanilla/binomialengine.hpp>
#include <ql/pricingengines/barrier/binomialbarrierengine.hpp>
#include <ql/experimental/barrieroption/binomialdoublebarrierengine.hpp>
#include <ql/experimental/barrieroption/mcdoublebarrierengine.hpp>
#include <ql/experimental/callablebonds/blackcallablebondengine.hpp>
#include <ql/experimental/callablebonds/treecallablebondengine.hpp>
#include <ql/pricingengines/bond/binomialconvertibleengine.hpp>
#include <ql/experimental/lattices/extendedbinomialtree.hpp>
#include <ql/experimental/math/zigguratrng.hpp>
#include <ql/methods/finitedifferences/expliciteuler.hpp>
#include <ql/methods/finitedifferences/impliciteuler.hpp>
#include <ql/pricingengines/vanilla/fdblackscholesvanillaengine.hpp>
#include <ql/instruments/dividendschedule.hpp>
#include <ql/methods/montecarlo/pathgenerator.hpp>
#include <ql/methods/montecarlo/multipathgenerator.hpp>
#include <ql/experimental/exoticoptions/mchimalayaengine.hpp>
#include <ql/experimental/exoticoptions/mcpagodaengine.hpp>
#include <ql/experimental/exoticoptions/mceverestengine.hpp>
#include <ql/pricingengines/forward/mcforwardeuropeanbsengine.hpp>
#include <ql/pricingengines/forward/mcforwardeuropeanhestonengine.hpp>
#include <ql/pricingengines/basket/mceuropeanbasketengine.hpp>
#include <ql/pricingengines/basket/mcamericanbasketengine.hpp>
#include <ql/pricingengines/asian/mc_discr_arith_av_price_heston.hpp>
#include <ql/math/statistics/incrementalstatistics.hpp>

namespace hasquant {
#include "qlEnumObjects.h"
}

using QuantLib::ext::shared_ptr;
#include "qlStdCompat.h"
#include "qlPricingEngineAux.h"
using namespace QuantLib;

// ---------------------------------------------------------------------------------------------
// Trait dispatchers.
//
// Each engine is selected by one or two runtime traits. Dispatchers call a generic lambda with
// an empty `Tag<T>` per axis, so each constructor is written once and recovers types through
// `typename decltype(r)::type`.
//
// Use an explicit result type: a decltype probe can prevent clang from emitting the selected
// engine's vtable.
template <class T> struct Tag { using type = T; };

template <class Ret, class F>
Ret dispatchTree(int tree, F&& make) {
  switch (tree) {
  case hasquant::JarrowRudd: return make(Tag<JarrowRudd>());
  case hasquant::CoxRossRubinstein: return make(Tag<CoxRossRubinstein>());
  case hasquant::AdditiveEQPBinomialTree: return make(Tag<AdditiveEQPBinomialTree>());
  case hasquant::Trigeorgis: return make(Tag<Trigeorgis>());
  case hasquant::Tian: return make(Tag<Tian>());
  case hasquant::LeisenReimer: return make(Tag<LeisenReimer>());
  case hasquant::Joshi4: return make(Tag<Joshi4>());
  case hasquant::ExtendedJarrowRudd: return make(Tag<ExtendedJarrowRudd>());
  case hasquant::ExtendedCoxRossRubinstein: return make(Tag<ExtendedCoxRossRubinstein>());
  case hasquant::ExtendedAdditiveEQPBinomialTree: return make(Tag<ExtendedAdditiveEQPBinomialTree>());
  case hasquant::ExtendedTrigeorgis: return make(Tag<ExtendedTrigeorgis>());
  case hasquant::ExtendedTian: return make(Tag<ExtendedTian>());
  case hasquant::ExtendedLeisenReimer: return make(Tag<ExtendedLeisenReimer>());
  case hasquant::ExtendedJoshi4: return make(Tag<ExtendedJoshi4>());
  }
  QL_FAIL("Unknown Binomial Tree "<< tree);
}

template <class Ret, class F>
Ret dispatchRng(int rngtrait, F&& make) {
  switch (rngtrait) {
  case hasquant::PseudoRandom: return make(Tag<PseudoRandom>());
  case hasquant::PoissonPseudoRandom: return make(Tag<PoissonPseudoRandom>());
  case hasquant::LowDiscrepancy: return make(Tag<LowDiscrepancy>());
  case hasquant::Ziggurat: return make(Tag<Ziggurat>());
  }
  QL_FAIL("Unknown RNG "<< rngtrait);
}

template <class Ret, class F>
Ret dispatchStat(int stattrait, F&& make) {
  switch (stattrait) {
  case hasquant::Statistics: return make(Tag<QuantLib::Statistics>());
  case hasquant::GaussianStatistics: return make(Tag<QuantLib::GaussianStatistics>());
  case hasquant::GeneralStatistics: return make(Tag<QuantLib::GeneralStatistics>());
  case hasquant::IncrementalStatistics: return make(Tag<QuantLib::IncrementalStatistics>());
  }
  QL_FAIL("Unknown Statistics "<< stattrait);
}

// The shared RNG x Statistics dispatch used by every templated Monte Carlo engine in this file.
template <class Ret, class F>
Ret dispatchRngStat(int rngtrait, int stattrait, F&& make) {
  return dispatchRng<Ret>(rngtrait, [&](auto r) {
    return dispatchStat<Ret>(stattrait, [&](auto st) { return make(r, st); });
  });
}
// ---------------------------------------------------------------------------------------------

PricingEngine* qlBinomialVanillaEngineAux(int tree, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps) {
  return dispatchTree<PricingEngine*>(tree, [&](auto t) {
    return new BinomialVanillaEngine<typename decltype(t)::type>(process, timeSteps);
  });
}

PricingEngine* qlBinomialConvertibleEngineAux(int tree, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, const Handle<Quote>& cs, DividendSchedule d) {
  return dispatchTree<PricingEngine*>(tree, [&](auto t) {
    return new BinomialConvertibleEngine<typename decltype(t)::type>(process, timeSteps, cs, d);
  });
}

PricingEngine* qlBinomialBarrierEngineAux(int tree, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned maxTimeSteps) {
  return dispatchTree<PricingEngine*>(tree, [&](auto t) {
    return new BinomialBarrierEngine<typename decltype(t)::type, DiscretizedBarrierOption>(process, timeSteps, maxTimeSteps);
  });
}

PricingEngine* qlBinomialDoubleBarrierEngineAux(int tree, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps) {
  return dispatchTree<PricingEngine*>(tree, [&](auto t) {
    return new BinomialDoubleBarrierEngine<typename decltype(t)::type>(process, timeSteps);
  });
}

PricingEngine* qlMCDoubleBarrierEngineAux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDoubleBarrierEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCVarianceSwapEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCVarianceSwapEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCHestonHullWhiteEngine1Aux(int rngtrait, int stattrait, const shared_ptr<HybridHestonHullWhiteProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int antitheticVariate, int controlVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCHestonHullWhiteEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, antitheticVariate, controlVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCAmericanEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int antitheticVariate, int controlVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed, unsigned polynomOrder, LsmBasisSystem::PolynomialType polynomType, unsigned nCalibrationSamples, optional<bool> antitheticVariateCalibration, unsigned seedCalibration) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCAmericanEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, antitheticVariate, controlVariate, requiredSamples, requiredTolerance, maxSamples, seed, polynomOrder, polynomType, nCalibrationSamples, antitheticVariateCalibration, seedCalibration);
  });
}

PricingEngine* qlMCBarrierEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, int isBiased, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCBarrierEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, isBiased, seed);
  });
}

PricingEngine* qlMCDigitalEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> x0, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDigitalEngine<typename decltype(r)::type, typename decltype(st)::type>(x0, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCForwardEuropeanBSEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCForwardEuropeanBSEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCForwardEuropeanHestonEngine1Aux(int rngtrait, int stattrait, const shared_ptr<HestonProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed, int controlVariate) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCForwardEuropeanHestonEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed, controlVariate);
  });
}

PricingEngine* qlMCDiscreteArithmeticAPEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, int brownianBridge, int antitheticVariate, int controlVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDiscreteArithmeticAPEngine<typename decltype(r)::type, typename decltype(st)::type>(process, brownianBridge, antitheticVariate, controlVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCDiscreteArithmeticASEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDiscreteArithmeticASEngine<typename decltype(r)::type, typename decltype(st)::type>(process, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCDiscreteGeometricAPEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDiscreteGeometricAPEngine<typename decltype(r)::type, typename decltype(st)::type>(process, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCDiscreteArithmeticAPHestonEngine1Aux(int rngtrait, int stattrait, const shared_ptr<HestonProcess> process, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed, unsigned timeSteps, unsigned timeStepsPerYear, int controlVariate) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDiscreteArithmeticAPHestonEngine<typename decltype(r)::type, typename decltype(st)::type>(process, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed, timeSteps, timeStepsPerYear, controlVariate);
  });
}

PricingEngine* qlMCDiscreteGeometricAPHestonEngine1Aux(int rngtrait, int stattrait, const shared_ptr<HestonProcess> process, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed, unsigned timeSteps, unsigned timeStepsPerYear) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCDiscreteGeometricAPHestonEngine<typename decltype(r)::type, typename decltype(st)::type>(process, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed, timeSteps, timeStepsPerYear);
  });
}

PricingEngine* qlMCEuropeanEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCEuropeanEngine<typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCEuropeanGJRGARCHEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GJRGARCHProcess> x0, unsigned timeSteps, unsigned timeStepsPerYear, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCEuropeanGJRGARCHEngine<typename decltype(r)::type, typename decltype(st)::type>(x0, timeSteps, timeStepsPerYear, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCEuropeanHestonEngine1Aux(int rngtrait, int stattrait, const shared_ptr<HestonProcess> x0, unsigned timeSteps, unsigned timeStepsPerYear, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCEuropeanHestonEngine<typename decltype(r)::type, typename decltype(st)::type>(x0, timeSteps, timeStepsPerYear, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCHullWhiteCapFloorEngine1Aux(int rngtrait, int stattrait, shared_ptr<HullWhite> model, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCHullWhiteCapFloorEngine<typename decltype(r)::type, typename decltype(st)::type>(model, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCHimalayaEngine1Aux(int rngtrait, int stattrait, const shared_ptr<StochasticProcessArray> processes, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCHimalayaEngine<typename decltype(r)::type, typename decltype(st)::type>(processes, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCPagodaEngine1Aux(int rngtrait, int stattrait, const shared_ptr<StochasticProcessArray> processes, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCPagodaEngine<typename decltype(r)::type, typename decltype(st)::type>(processes, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCEverestEngine1Aux(int rngtrait, int stattrait, const shared_ptr<StochasticProcessArray> processes, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCEverestEngine<typename decltype(r)::type, typename decltype(st)::type>(processes, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCEuropeanBasketEngine1Aux(int rngtrait, int stattrait, const shared_ptr<StochasticProcessArray> processes, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCEuropeanBasketEngine<typename decltype(r)::type, typename decltype(st)::type>(processes, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCPerformanceEngine1Aux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCPerformanceEngine<typename decltype(r)::type, typename decltype(st)::type>(process, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

// MCLookbackEngine<I,RNG,S> is templated over the lookback option type I as well as RNG/S -- I is
// not a runtime "trait" (it fixes the whole option/engine-base pair, not a policy struct chosen
// among siblings), so each of the four already-bound lookback option types gets its own dispatcher
// over the RNG x Statistics axes only, mirroring the four separate analytic lookback engine shims.
PricingEngine* qlMCLookbackFixedEngineAux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCLookbackEngine<ContinuousFixedLookbackOption, typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCLookbackFloatingEngineAux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCLookbackEngine<ContinuousFloatingLookbackOption, typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCLookbackPartialFixedEngineAux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCLookbackEngine<ContinuousPartialFixedLookbackOption, typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

PricingEngine* qlMCLookbackPartialFloatingEngineAux(int rngtrait, int stattrait, const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed) {
  return dispatchRngStat<PricingEngine*>(rngtrait, stattrait, [&](auto r, auto st) {
    return new MCLookbackEngine<ContinuousPartialFloatingLookbackOption, typename decltype(r)::type, typename decltype(st)::type>(process, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed);
  });
}

// Upstream MCAmericanBasketEngine has only an RNG template parameter; it has no statistics axis.
PricingEngine* qlMCAmericanBasketEngine1Aux(int rngtrait, const shared_ptr<StochasticProcessArray> processes, unsigned timeSteps, unsigned timeStepsPerYear, int brownianBridge, int antitheticVariate, unsigned requiredSamples, double requiredTolerance, unsigned maxSamples, unsigned seed, unsigned nCalibrationSamples, unsigned polynomialOrder, LsmBasisSystem::PolynomialType polynomialType) {
  return dispatchRng<PricingEngine*>(rngtrait, [&](auto r) {
    return new MCAmericanBasketEngine<typename decltype(r)::type>(processes, timeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed, nCalibrationSamples, polynomialOrder, polynomialType);
  });
}

PricingEngine* qlFdBlackScholesVanillaEngineAux(const shared_ptr<GeneralizedBlackScholesProcess> process, unsigned tGrid, unsigned xGrid, unsigned dampingSteps, const FdmSchemeDesc &fdScheme, bool localVol, double illegalLocalVolOverwrite, int cashDividendModel) {return new hasquant::CheckedFdEngine<FdBlackScholesVanillaEngine>(process, tGrid, xGrid, dampingSteps, fdScheme, localVol, illegalLocalVolOverwrite, (FdBlackScholesVanillaEngine::CashDividendModel)cashDividendModel);}

class PolymorphicPathGenerator {
private:
  template <class Rsg> struct SequenceSource {
    using sample_type = typename Rsg::sample_type;
    shared_ptr<Rsg> source;
    unsigned dimension() const {return source->dimension();}
    const Sample<std::vector<Real>>& nextSequence() const {return source->nextSequence();}
    const Sample<std::vector<Real>>& lastSequence() const {return source->lastSequence();}
  };
public:
  PolymorphicPathGenerator(int rngtrait, const shared_ptr<StochasticProcess> p, const TimeGrid &t, unsigned seed, unsigned dim, bool brownianBridge) {
    init(rngtrait, p, t, seed, dim, brownianBridge, SobolRsg::Jaeckel);
  }
  PolymorphicPathGenerator(SobolRsg::DirectionIntegers dir, const shared_ptr<StochasticProcess> p, const TimeGrid &t, unsigned seed, unsigned dim, bool brownianBridge) {
    init(hasquant::LowDiscrepancy, p, t, seed, dim, brownianBridge, dir);
  }
  const Sample<MultiPath>& next() const {const auto& s = _next(); drawn_ = true; return s;}
  const Sample<MultiPath>& antithetic() const {return _antithetic();}
  const Sample<std::vector<Real>>& sequence() const {
    QL_REQUIRE(drawn_, "no path has been drawn");
    return _sequence();
  }
private:
  template <class Rsg> void install(const shared_ptr<StochasticProcess>& p, const TimeGrid& t, Rsg rsg, bool brownianBridge) {
    auto source = shared_ptr<Rsg>(new Rsg(std::move(rsg)));
    using Generator = MultiPathGenerator<SequenceSource<Rsg>>;
    auto gen = shared_ptr<Generator>(new Generator(p, t, SequenceSource<Rsg>{source}, brownianBridge));
    _next = [gen]() -> const Sample<MultiPath>& {return gen->next();};
    _antithetic = [gen]() -> const Sample<MultiPath>& {return gen->antithetic();};
    _sequence = [source]() -> const Sample<std::vector<Real>>& {return source->lastSequence();};
  }
  void init(int rngtrait, const shared_ptr<StochasticProcess> p, const TimeGrid &t, unsigned seed, unsigned dim, bool brownianBridge, SobolRsg::DirectionIntegers dir) {
    switch (rngtrait) {
    case hasquant::PseudoRandom:
      install(p, t, PseudoRandom::rsg_type(PseudoRandom::ursg_type(dim, PseudoRandom::urng_type(seed))), brownianBridge);
      break;
    case hasquant::PoissonPseudoRandom:
      install(p, t, PoissonPseudoRandom::rsg_type(PoissonPseudoRandom::ursg_type(dim, PoissonPseudoRandom::urng_type(seed))), brownianBridge);
      break;
    case hasquant::LowDiscrepancy:
      install(p, t, LowDiscrepancy::rsg_type(SobolRsg(dim, seed, dir)), brownianBridge);
      break;
    case hasquant::Ziggurat:
      install(p, t, Ziggurat::rsg_type(dim, ZigguratRng(seed)), brownianBridge);
      break;
    default:
      QL_FAIL("Unknown RNG "<< rngtrait);
    }
  }
  std::function<const Sample<MultiPath>& ()> _next;
  std::function<const Sample<MultiPath>& ()> _antithetic;
  std::function<const Sample<std::vector<Real>>& ()> _sequence;
  mutable bool drawn_ = false;
};

PolymorphicPathGenerator* qlPathGeneratorAux(int rngtrait, const shared_ptr<StochasticProcess> p, const TimeGrid &grid, unsigned seed, unsigned dim, bool brownianBridge) {
  return new PolymorphicPathGenerator(rngtrait, p, grid, seed, dim, brownianBridge);
}

PolymorphicPathGenerator* qlSobolPathGeneratorAux(SobolRsg::DirectionIntegers dir, const shared_ptr<StochasticProcess> p, const TimeGrid &grid, unsigned seed, unsigned dim, bool brownianBridge) {
  return new PolymorphicPathGenerator(dir, p, grid, seed, dim, brownianBridge);
}

// Expose the gaussian sequence generator so Haskell can evolve paths without a hot-loop callback.
// Match PolymorphicPathGenerator's RNG and Sobol dispatch for draw-for-draw comparisons.
class PolymorphicGaussianRsg {
private:
  using PseudoRandomRsg = PseudoRandom::rsg_type;
  using SobolRsgType = LowDiscrepancy::rsg_type;
  using PoissonRsg = PoissonPseudoRandom::rsg_type;
  using ZigguratRsg = Ziggurat::rsg_type;
public:
  PolymorphicGaussianRsg(int rngtrait, unsigned dim, unsigned seed) {
    init(rngtrait, dim, seed, SobolRsg::Jaeckel);
  }
  PolymorphicGaussianRsg(SobolRsg::DirectionIntegers dir, unsigned dim, unsigned seed) {
    init(hasquant::LowDiscrepancy, dim, seed, dir);
  }
  const Sample<std::vector<Real> >& nextSequence() const {return _next();}
  const Sample<std::vector<Real> >& lastSequence() const {return _last();}
  unsigned dimension() const {return _dimension();}
private:
  void init(int rngtrait, unsigned dim, unsigned seed, SobolRsg::DirectionIntegers dir) {
    switch (rngtrait) {
    case hasquant::PseudoRandom:
      _pseudoRandom.reset(new PseudoRandomRsg(PseudoRandom::ursg_type(dim, PseudoRandom::urng_type(seed))));
      bind(_pseudoRandom.get());
      break;
    case hasquant::PoissonPseudoRandom:
      _poisson.reset(new PoissonRsg(PoissonPseudoRandom::ursg_type(dim, PoissonPseudoRandom::urng_type(seed))));
      bind(_poisson.get());
      break;
    case hasquant::LowDiscrepancy:
      _sobol.reset(new SobolRsgType(SobolRsg(dim, seed, dir)));
      bind(_sobol.get());
      break;
    case hasquant::Ziggurat:
      _ziggurat.reset(new ZigguratRsg(dim, ZigguratRng(seed)));
      bind(_ziggurat.get());
      break;
    default:
      QL_FAIL("Unknown RNG "<< rngtrait);
    }
  }
  template <class Rsg> void bind(Rsg* r) {
    _next = [r]() -> const Sample<std::vector<Real> >& {return r->nextSequence();};
    _last = [r]() -> const Sample<std::vector<Real> >& {return r->lastSequence();};
    _dimension = [r]() {return (unsigned)r->dimension();};
  }
  std::unique_ptr<PseudoRandomRsg> _pseudoRandom;
  std::unique_ptr<SobolRsgType> _sobol;
  std::unique_ptr<PoissonRsg> _poisson;
  std::unique_ptr<ZigguratRsg> _ziggurat;
  std::function<const Sample<std::vector<Real> >& ()> _next;
  std::function<const Sample<std::vector<Real> >& ()> _last;
  std::function<unsigned ()> _dimension;
};

PolymorphicGaussianRsg* qlGaussianRsgAux(int rngtrait, unsigned dimension, unsigned seed) {
  return new PolymorphicGaussianRsg(rngtrait, dimension, seed);
}
PolymorphicGaussianRsg* qlSobolGaussianRsgAux(SobolRsg::DirectionIntegers dir, unsigned dimension, unsigned seed) {
  return new PolymorphicGaussianRsg(dir, dimension, seed);
}
void qlFreePolymorphicGaussianRsgAux(PolymorphicGaussianRsg* g) {delete g;}
const Sample<std::vector<Real> >& qlGaussianRsgNextSequenceAux(PolymorphicGaussianRsg* g) {return g->nextSequence();}
const Sample<std::vector<Real> >& qlGaussianRsgLastSequenceAux(PolymorphicGaussianRsg* g) {return g->lastSequence();}
unsigned qlGaussianRsgDimensionAux(PolymorphicGaussianRsg* g) {return g->dimension();}

void qlFreePolymorphicPathGeneratorAux(PolymorphicPathGenerator *p) {delete p;}
const Sample<MultiPath>& qlPathGeneratorNextAux(PolymorphicPathGenerator *p) {return p->next();}
const Sample<MultiPath>& qlPathGeneratorAntitheticAux(PolymorphicPathGenerator *p) {return p->antithetic();}

/* vim: set ft=cpp ff=unix ts=8 sts=2 sw=2 et: */

const Sample<std::vector<Real>>& qlPathGeneratorSequenceAux(PolymorphicPathGenerator* g) {return g->sequence();}