#include "ql/qldefines.hpp"
#include <boost/config.hpp>
#ifdef BOOST_MSVC
#  include <ql/auto_link.hpp>
#endif

/*CVAIRS*/
#include <ql/instruments/vanillaswap.hpp>
#include <ql/instruments/makevanillaswap.hpp>
#include <ql/pricingengines/swap/discountingswapengine.hpp>
#include <ql/pricingengines/swap/cvaswapengine.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/ratehelpers.hpp>
#include <ql/termstructures/credit/interpolatedhazardratecurve.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual360.hpp>
/*end CVAIRS*/

/*Bonds*/
#include <ql/instruments/bonds/zerocouponbond.hpp>
#include <ql/instruments/bonds/floatingratebond.hpp>
#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/bondhelpers.hpp>
#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/indexes/ibor/usdlibor.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/utilities/dataparsers.hpp>
/*end Bonds */

/*Convertible Bonds */
#include <ql/experimental/convertiblebonds/convertiblebond.hpp>
#include <ql/experimental/convertiblebonds/binomialconvertibleengine.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/utilities/dataformatters.hpp>
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))
/* end Convertible Bonds */

/*BermudanSwaptions*/
#include <ql/instruments/swaption.hpp>
#include <ql/pricingengines/swap/discountingswapengine.hpp>
#include <ql/pricingengines/swaption/treeswaptionengine.hpp>
#include <ql/pricingengines/swaption/jamshidianswaptionengine.hpp>
#include <ql/pricingengines/swaption/g2swaptionengine.hpp>
#include <ql/pricingengines/swaption/fdhullwhiteswaptionengine.hpp>
#include <ql/pricingengines/swaption/fdg2swaptionengine.hpp>
#include <ql/models/shortrate/calibrationhelpers/swaptionhelper.hpp>
#include <ql/models/shortrate/onefactormodels/blackkarasinski.hpp>
#include <ql/math/optimization/levenbergmarquardt.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/cashflows/coupon.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/utilities/dataformatters.hpp>
/*end BermudanSwaptions*/

/*Callable Bonds */
#include <ql/experimental/callablebonds/callablebond.hpp>
#include <ql/experimental/callablebonds/treecallablebondengine.hpp>
#include <ql/models/shortrate/onefactormodels/hullwhite.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actualactual.hpp>

#include <vector>
#include <cmath>
/* end of Callable Bonds */

#include <iostream>
#include <iomanip>
#include <string>
#include "json.hpp"

//using namespace emscripten;
using namespace QuantLib;
using json = nlohmann::json;

//using namespace std;
//using namespace std::string_literals;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    ThreadKey sessionId() { return {}; }

}
#endif

ext::shared_ptr<YieldTermStructure>
        flatRate(const Date& today,
            const ext::shared_ptr<Quote>& forward,
            const DayCounter& dc,
            const Compounding& compounding,
            const Frequency& frequency) {
        return ext::shared_ptr<YieldTermStructure>(
            new FlatForward(today,
                Handle<Quote>(forward),
                dc,
                compounding,
                frequency));
}

                ext::shared_ptr<YieldTermStructure>
        flatRate(const Date& today,
                Rate forward,
                const DayCounter& dc,
                const Compounding &compounding,
                const Frequency &frequency) {
        return flatRate(today,
                ext::shared_ptr<Quote>(new SimpleQuote(forward)),
                dc,
                compounding,
                frequency);
}

std::string calculateCallableBonds(std::string data){

        try {
                json jData = json::parse(data);

                Date today = Date(DateParser::parseISO( jData["request"]["referenceDate"].get<std::string>()));
                Settings::instance().evaluationDate() = today;

                Rate bbCurveRate = jData["request"]["bbCurveRate"].get<float>();

                DayCounter bbDayCounter = ActualActual(ActualActual::Bond);
                InterestRate bbIR(bbCurveRate,bbDayCounter,Compounded,Semiannual);

                Handle<YieldTermStructure> termStructure(flatRate(today,
                                                                bbIR.rate(),
                                                                bbIR.dayCounter(),
                                                                bbIR.compounding(),
                                                                bbIR.frequency()));

                CallabilitySchedule callSchedule;
                Real callPrice = jData["request"]["callPrice"].get<int>();
                Size numberOfCallDates = jData["request"]["numberOfCallDates"].get<int>();
                Date callDate = Date(DateParser::parseISO( jData["request"]["callDate"].get<std::string>()));

                for (Size i=0; i< numberOfCallDates; i++) {
                Calendar nullCalendar = NullCalendar();

                Bond::Price myPrice(callPrice, Bond::Price::Clean);
                callSchedule.push_back(
                        ext::make_shared<Callability>(
                                        myPrice,
                                        Callability::Call,
                                        callDate ));
                callDate = nullCalendar.advance(callDate, 3, Months);
                }

                Date dated = Date(DateParser::parseISO(jData["request"]["callableBond"]["issueDate"].get<std::string>()));
                Date issue = dated;
                Date maturity = Date(DateParser::parseISO( jData["request"]["callableBond"]["maturityDate"].get<std::string>()));

                Natural settlementDays = jData["request"]["callableBond"]["settlementDays"].get<int>();
                Calendar bondCalendar = UnitedStates(UnitedStates::GovernmentBond);
                Real coupon = jData["request"]["callableBond"]["coupon"].get<float>();

                Frequency frequency = Quarterly;

                Real redemption = jData["request"]["callableBond"]["redemption"].get<float>();

                Real faceAmount = jData["request"]["callableBond"]["faceAmount"].get<float>();

                DayCounter bondDayCounter = ActualActual(ActualActual::Bond);

                // PFC1 shows no indication dates are being adjusted
                // for weekends/holidays for vanilla bonds
                BusinessDayConvention accrualConvention = Unadjusted;
                BusinessDayConvention paymentConvention = Unadjusted;

                Schedule sch(dated, maturity, Period(frequency), bondCalendar,
                        accrualConvention, accrualConvention,
                        DateGeneration::Backward, false);

                Size maxIterations = jData["request"]["maxIterations"];

                //Real accuracy = 1e-8;
                Real accuracy = jData["request"]["accuracy"];

                //Integer gridIntervals = 40;
                Integer gridIntervals = jData["request"]["gridIntervals"];

                //Real reversionParameter = .03;
                Real reversionParameter = jData["request"]["reversionParameter"].get<float>();

                // output price/yield results for varying volatility parameter

                Real sigma = QL_EPSILON;

                for (long unsigned int i = 0; i < jData["request"]["sigma"].size(); i++){
                        switch (i){
                                case 0:{
                                        ext::shared_ptr<ShortRateModel> hw0(new HullWhite(termStructure,reversionParameter,sigma));
                                        ext::shared_ptr<PricingEngine> engine0(new TreeCallableFixedRateBondEngine(hw0,gridIntervals));
                                        CallableFixedRateBond callableBond(settlementDays,faceAmount, sch,std::vector<Rate>(1, coupon),bondDayCounter, paymentConvention,redemption,issue,callSchedule);
                                        callableBond.setPricingEngine(engine0);
                                        jData["response"][std::to_string(i)]["sigma"] = 0.0;//jData["request"]["sigma"][0];
                                        jData["response"][std::to_string(i)]["cleanPrice"] =  callableBond.cleanPrice();
                                        jData["response"][std::to_string(i)]["yield"] =  100. * callableBond.yield(bondDayCounter,Compounded,frequency,accuracy,maxIterations);
                                        break;
                                }
                                case 1:{
                                        sigma = jData["request"]["sigma"][i];
                                        ext::shared_ptr<ShortRateModel> hw1(new HullWhite(termStructure,reversionParameter,sigma));
                                        ext::shared_ptr<PricingEngine> engine1(new TreeCallableFixedRateBondEngine(hw1,gridIntervals));
                                        CallableFixedRateBond callableBond(settlementDays,faceAmount, sch,std::vector<Rate>(1, coupon),bondDayCounter, paymentConvention,redemption,issue,callSchedule);
                                        callableBond.setPricingEngine(engine1);
                                        jData["response"][std::to_string(i)]["sigma"] = 0.01;//jData["request"]["sigma"][i];
                                        jData["response"][std::to_string(i)]["cleanPrice"] =  callableBond.cleanPrice();
                                        jData["response"][std::to_string(i)]["yield"] =  100. * callableBond.yield(bondDayCounter,Compounded,frequency,accuracy,maxIterations);
                                        break;
                                }
                                case 2: {
                                        sigma = jData["request"]["sigma"][i];
                                        ext::shared_ptr<ShortRateModel> hw2(new HullWhite(termStructure, reversionParameter, sigma));
                                        ext::shared_ptr<PricingEngine> engine2(new TreeCallableFixedRateBondEngine(hw2,gridIntervals));
                                        CallableFixedRateBond callableBond(settlementDays,faceAmount, sch,std::vector<Rate>(1, coupon),bondDayCounter, paymentConvention,redemption,issue,callSchedule);
                                        callableBond.setPricingEngine(engine2);
                                        jData["response"][std::to_string(i)]["sigma"] = 0.03;//jData["request"]["sigma"][2];
                                        jData["response"][std::to_string(i)]["cleanPrice"] =  callableBond.cleanPrice();
                                        jData["response"][std::to_string(i)]["yield"] =  100. * callableBond.yield(bondDayCounter,Compounded,frequency,accuracy,maxIterations);
                                        break;
                                }
                                case 3: {
                                        sigma = jData["request"]["sigma"][i];
                                        ext::shared_ptr<ShortRateModel> hw3(new HullWhite(termStructure, reversionParameter, sigma));
                                        ext::shared_ptr<PricingEngine> engine3(new TreeCallableFixedRateBondEngine(hw3,gridIntervals));
                                        CallableFixedRateBond callableBond(settlementDays,faceAmount, sch,std::vector<Rate>(1, coupon),bondDayCounter, paymentConvention,redemption,issue,callSchedule);
                                        callableBond.setPricingEngine(engine3);
                                        jData["response"][std::to_string(i)]["sigma"] = 0.06;//jData["request"]["sigma"][3];
                                        jData["response"][std::to_string(i)]["cleanPrice"] =  callableBond.cleanPrice();
                                        jData["response"][std::to_string(i)]["yield"] =  100. * callableBond.yield(bondDayCounter,Compounded,frequency,accuracy,maxIterations);
                                        break;
                                }
                                case 4:{
                                        sigma = jData["request"]["sigma"][i];
                                        ext::shared_ptr<ShortRateModel> hw4(new HullWhite(termStructure, reversionParameter, sigma));
                                        ext::shared_ptr<PricingEngine> engine4(new TreeCallableFixedRateBondEngine(hw4,gridIntervals));
                                        CallableFixedRateBond callableBond(settlementDays,faceAmount, sch,std::vector<Rate>(1, coupon),bondDayCounter, paymentConvention,redemption,issue,callSchedule);
                                        callableBond.setPricingEngine(engine4);
                                        jData["response"][std::to_string(i)]["sigma"] = 0.012;//jData["request"]["sigma"][4];
                                        jData["response"][std::to_string(i)]["cleanPrice"] =  callableBond.cleanPrice();
                                        jData["response"][std::to_string(i)]["yield"] =  100. * callableBond.yield(bondDayCounter,Compounded,frequency,accuracy,maxIterations);
                                        break;
                                }
                                default: break;
                        }
                }

                std::cout << "JDATA" << std::setw(4) << jData["response"] << std::endl;
                std::string result = jData.dump();
                return result;

        } catch (std::exception& e) {
                std::cerr << e.what() << std::endl;
                return "1";
        } catch (...) {
                std::cerr << "unknown error" << std::endl;
                return "1";
        }
};

std::string calculateRegularBond(std::string data){

    try {

        json incomingJsonData = json::parse(data);

        std::cout << std::endl;

        /*********************
         ***  MARKET DATA  ***
         *********************/

        Calendar calendar = TARGET();

        Date settlementDate = DateParser::parseISO( incomingJsonData["request"]["settlementDate"].get<std::string>());
        // must be a business day
        settlementDate = calendar.adjust(settlementDate);

        Integer fixingDays = incomingJsonData["request"]["fixingDays"];
        Natural settlementDays = incomingJsonData["request"]["settlementDays"];

        Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);
        // nothing to do with Date::todaysDate
        Settings::instance().evaluationDate() = todaysDate;

        Rate zc3mQuote = incomingJsonData["request"]["zeroCoupnRates"]["zc3mQuote"];
        Rate zc6mQuote = incomingJsonData["request"]["zeroCoupnRates"]["zc6mQuote"];
        Rate zc1yQuote = incomingJsonData["request"]["zeroCoupnRates"]["zc1yQuote"];

        ext::shared_ptr<Quote> zc3mRate(new SimpleQuote(zc3mQuote));
        ext::shared_ptr<Quote> zc6mRate(new SimpleQuote(zc6mQuote));
        ext::shared_ptr<Quote> zc1yRate(new SimpleQuote(zc1yQuote));

        DayCounter zcBondsDayCounter = Actual365Fixed();

        ext::shared_ptr<RateHelper> zc3m(new DepositRateHelper(
                Handle<Quote>(zc3mRate),
                3*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter));
        ext::shared_ptr<RateHelper> zc6m(new DepositRateHelper(
                Handle<Quote>(zc6mRate),
                6*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter));
        ext::shared_ptr<RateHelper> zc1y(new DepositRateHelper(
                Handle<Quote>(zc1yRate),
                1*Years, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter));

        // setup bonds
        Real redemption = 100.0;

        const Size numberOfBonds = 5;

        Date issueDates[] = {
                Date (DateParser::parseISO( incomingJsonData["request"]["issueDate"][0].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["issueDate"][1].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["issueDate"][2].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["issueDate"][3].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["issueDate"][4].get<std::string>()))
        };

        Date maturities[] = {
                Date (DateParser::parseISO( incomingJsonData["request"]["maturities"][0].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["maturities"][1].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["maturities"][2].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["maturities"][3].get<std::string>())),
                Date (DateParser::parseISO( incomingJsonData["request"]["maturities"][4].get<std::string>()))
        };

        Real couponRates[] = {
                incomingJsonData["request"]["couponRates"][0],
                incomingJsonData["request"]["couponRates"][1],
                incomingJsonData["request"]["couponRates"][2],
                incomingJsonData["request"]["couponRates"][3],
                incomingJsonData["request"]["couponRates"][4]
        };

        Real marketQuotes[] = {
                incomingJsonData["request"]["marketQuotes"][0],
                incomingJsonData["request"]["marketQuotes"][1],
                incomingJsonData["request"]["marketQuotes"][2],
                incomingJsonData["request"]["marketQuotes"][3],
                incomingJsonData["request"]["marketQuotes"][4]
        };

        std::vector< ext::shared_ptr<SimpleQuote> > quote;
        for (double marketQuote : marketQuotes) {
            ext::shared_ptr<SimpleQuote> cp(new SimpleQuote(marketQuote));
            quote.push_back(cp);
        }

        RelinkableHandle<Quote> quoteHandle[numberOfBonds];
        for (Size i=0; i<numberOfBonds; i++) {
            quoteHandle[i].linkTo(quote[i]);
        }

        // Definition of the rate helpers
        std::vector<ext::shared_ptr<BondHelper> > bondsHelpers;

        for (Size i=0; i<numberOfBonds; i++) {

                Schedule schedule(issueDates[i], maturities[i], Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond),
                        Unadjusted, Unadjusted, DateGeneration::Backward, false);

                ext::shared_ptr<FixedRateBondHelper> bondHelper(new FixedRateBondHelper(
                        quoteHandle[i],
                        settlementDays,
                        100.0,
                        schedule,
                        std::vector<Rate>(1,couponRates[i]),
                        ActualActual(ActualActual::Bond),
                        Unadjusted,
                        redemption,
                        issueDates[i]));

            // the above could also be done by creating a
            // FixedRateBond instance and writing:
            //
            // ext::shared_ptr<BondHelper> bondHelper(
            //         new BondHelper(quoteHandle[i], bond));
            //
            // This would also work for bonds that still don't have a
            // specialized helper, such as floating-rate bonds.

            bondsHelpers.push_back(bondHelper);
        }

        /*********************
        **  CURVE BUILDING **
        *********************/

        // Any DayCounter would be fine.
        // ActualActual::ISDA ensures that 30 years is 30.0
        DayCounter termStructureDayCounter =
        ActualActual(ActualActual::ISDA);

        // A depo-bond curve
        std::vector<ext::shared_ptr<RateHelper> > bondInstruments;

        // Adding the ZC bonds to the curve for the short end
        bondInstruments.push_back(zc3m);
        bondInstruments.push_back(zc6m);
        bondInstruments.push_back(zc1y);

        // Adding the Fixed rate bonds to the curve for the long end
        for (Size i=0; i<numberOfBonds; i++) {
        bondInstruments.push_back(bondsHelpers[i]);
        }

        ext::shared_ptr<YieldTermStructure> bondDiscountingTermStructure(
                new PiecewiseYieldCurve<Discount,LogLinear>(
                        settlementDate, bondInstruments,
                        termStructureDayCounter));

        // Building of the Libor forecasting curve
        // deposits
        Rate d1wQuote = incomingJsonData["request"]["liborRates"]["d1wQuote"];
        Rate d1mQuote = incomingJsonData["request"]["liborRates"]["d1mQuote"];
        Rate d3mQuote = incomingJsonData["request"]["liborRates"]["d3mQuote"];
        Rate d6mQuote = incomingJsonData["request"]["liborRates"]["d6mQuote"];
        Rate d9mQuote = incomingJsonData["request"]["liborRates"]["d9mQuote"];
        Rate d1yQuote = incomingJsonData["request"]["liborRates"]["d1yQuote"];
        // swaps
        Rate s2yQuote = incomingJsonData["request"]["swapRates"]["s2yQuote"];
        Rate s3yQuote = incomingJsonData["request"]["swapRates"]["s3yQuote"];
        Rate s5yQuote = incomingJsonData["request"]["swapRates"]["s5yQuote"];
        Rate s10yQuote = incomingJsonData["request"]["swapRates"]["s10yQuote"];
        Rate s15yQuote = incomingJsonData["request"]["swapRates"]["s15yQuote"];

        /********************
         ***    QUOTES    ***
        ********************/

        // SimpleQuote stores a value which can be manually changed;
        // other Quote subclasses could read the value from a database
        // or some kind of data feed.

        // deposits
        ext::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote));
        ext::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote));
        ext::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote));
        ext::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote));
        ext::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote));
        ext::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote));
        // swaps
        ext::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote));
        ext::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote));
        ext::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote));
        ext::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote));
        ext::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote));

        /*********************
         ***  RATE HELPERS ***
        *********************/

        // RateHelpers are built from the above quotes together with
        // other instrument dependant infos.  Quotes are passed in
        // relinkable handles which could be relinked to some other
        // data source later.

        // deposits
        DayCounter depositDayCounter = Actual360();

        ext::shared_ptr<RateHelper> d1w(new DepositRateHelper(
                Handle<Quote>(d1wRate),
                1*Weeks, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));
        ext::shared_ptr<RateHelper> d1m(new DepositRateHelper(
                Handle<Quote>(d1mRate),
                1*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));
        ext::shared_ptr<RateHelper> d3m(new DepositRateHelper(
                Handle<Quote>(d3mRate),
                3*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));
        ext::shared_ptr<RateHelper> d6m(new DepositRateHelper(
                Handle<Quote>(d6mRate),
                6*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));
        ext::shared_ptr<RateHelper> d9m(new DepositRateHelper(
                Handle<Quote>(d9mRate),
                9*Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));
        ext::shared_ptr<RateHelper> d1y(new DepositRateHelper(
                Handle<Quote>(d1yRate),
                1*Years, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter));

        // setup swaps
        Frequency swFixedLegFrequency = Annual;
        BusinessDayConvention swFixedLegConvention = Unadjusted;
        DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European);
        ext::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M);

        const Period forwardStart(1*Days);

        ext::shared_ptr<RateHelper> s2y(new SwapRateHelper(
                Handle<Quote>(s2yRate), 2*Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(),forwardStart));
        ext::shared_ptr<RateHelper> s3y(new SwapRateHelper(
                Handle<Quote>(s3yRate), 3*Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(),forwardStart));
        ext::shared_ptr<RateHelper> s5y(new SwapRateHelper(
                Handle<Quote>(s5yRate), 5*Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(),forwardStart));
        ext::shared_ptr<RateHelper> s10y(new SwapRateHelper(
                Handle<Quote>(s10yRate), 10*Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(),forwardStart));
        ext::shared_ptr<RateHelper> s15y(new SwapRateHelper(
                Handle<Quote>(s15yRate), 15*Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(),forwardStart));

        /*********************
         **  CURVE BUILDING **
        *********************/

        // Any DayCounter would be fine.
        // ActualActual::ISDA ensures that 30 years is 30.0

        // A depo-swap curve
        std::vector<ext::shared_ptr<RateHelper> > depoSwapInstruments;
        depoSwapInstruments.push_back(d1w);
        depoSwapInstruments.push_back(d1m);
        depoSwapInstruments.push_back(d3m);
        depoSwapInstruments.push_back(d6m);
        depoSwapInstruments.push_back(d9m);
        depoSwapInstruments.push_back(d1y);
        depoSwapInstruments.push_back(s2y);
        depoSwapInstruments.push_back(s3y);
        depoSwapInstruments.push_back(s5y);
        depoSwapInstruments.push_back(s10y);
        depoSwapInstruments.push_back(s15y);
        ext::shared_ptr<YieldTermStructure> depoSwapTermStructure(
                new PiecewiseYieldCurve<Discount,LogLinear>(
                        settlementDate, depoSwapInstruments,
                        termStructureDayCounter));

        // Term structures that will be used for pricing:
        // the one used for discounting cash flows
        RelinkableHandle<YieldTermStructure> discountingTermStructure;
        // the one used for forward rate forecasting
        RelinkableHandle<YieldTermStructure> forecastingTermStructure;

        /*********************
         * BONDS TO BE PRICED *
         **********************/
        //Date (DateParser::parseISO( incomingJsonData["maturities"][0].get<std::string>())),
        // Common data
        Real faceAmount = 100;

        // Pricing engine
        ext::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine(discountingTermStructure));

        // Zero coupon bond

        ZeroCouponBond zeroCouponBond(
                settlementDays,
                UnitedStates(UnitedStates::GovernmentBond),
                faceAmount,
                Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["zeroCouponBond"]["expiryDate"].get<std::string>())),
                Following,
                Real(incomingJsonData["request"]["bondsToEvaluate"]["zeroCouponBond"]["realPrice"]),
                Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["zeroCouponBond"]["issueDate"].get<std::string>())));

        zeroCouponBond.setPricingEngine(bondEngine);

        // Fixed 4.5% US Treasury Note

        Schedule fixedBondSchedule(Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["FixedRateBond"]["issueDate"].get<std::string>())),
                Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["FixedRateBond"]["expiryDate"].get<std::string>())), Period(Semiannual),
                UnitedStates(UnitedStates::GovernmentBond),
                Unadjusted, Unadjusted, DateGeneration::Backward, false);

        FixedRateBond fixedRateBond(
                settlementDays,
                faceAmount,
                fixedBondSchedule,
                std::vector<Rate>(1, 0.045),
                ActualActual(ActualActual::Bond),
                ModifiedFollowing,
                100.0, Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["FixedRateBond"]["issueDate"].get<std::string>())));

        fixedRateBond.setPricingEngine(bondEngine);

        // Floating rate bond (3M USD Libor + 0.1%)
        // Should and will be priced on another curve later...

        // need to find out what is the deal with Libor here
        RelinkableHandle<YieldTermStructure> liborTermStructure;
        const ext::shared_ptr<IborIndex> libor3m(
                new USDLibor(Period(3,Months),liborTermStructure));
        libor3m->addFixing(Date(17, July, 2008),0.0278625);

        // need to find out what is the deal with Libor here
        Schedule floatingBondSchedule(Date(21, October, 2005),
                Date(21, October, 2010), Period(Quarterly),
                UnitedStates(UnitedStates::NYSE),
                Unadjusted, Unadjusted, DateGeneration::Backward, true);

        FloatingRateBond floatingRateBond(
                settlementDays,
                faceAmount,
                floatingBondSchedule,
                libor3m,
                Actual360(),
                ModifiedFollowing,
                Natural(2),
                // Gearings
                std::vector<Real>(1, 1.0),
                // Spreads
                std::vector<Rate>(1, 0.001),
                // Caps
                std::vector<Rate>(),
                // Floors
                std::vector<Rate>(),
                // Fixing in arrears
                true,
                Real(incomingJsonData["request"]["bondsToEvaluate"]["FloatingRateBond"]["realPrice"]),
                Date(DateParser::parseISO( incomingJsonData["request"]["bondsToEvaluate"]["FloatingRateBond"]["issueDate"].get<std::string>())));

        floatingRateBond.setPricingEngine(bondEngine);

        // Coupon pricers
        ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer);

        // optionLet volatilities
        Volatility volatility = 0.0;
        Handle<OptionletVolatilityStructure> vol;
        vol = Handle<OptionletVolatilityStructure>(
                ext::shared_ptr<OptionletVolatilityStructure>(new
                        ConstantOptionletVolatility(
                                settlementDays,
                                calendar,
                                ModifiedFollowing,
                                volatility,
                                Actual365Fixed())));

        pricer->setCapletVolatility(vol);
        setCouponPricer(floatingRateBond.cashflows(),pricer);

        // Yield curve bootstrapping
        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(bondDiscountingTermStructure);

        // We are using the depo & swap curve to estimate the future Libor rates
        liborTermStructure.linkTo(depoSwapTermStructure);

        /***************
         * BOND PRICING *
         ****************/

        incomingJsonData["response"]["zeroCouponBond"]["NPV"] = zeroCouponBond.NPV();
        incomingJsonData["response"]["FixedRateBond"]["NPV"] = fixedRateBond.NPV();
        incomingJsonData["response"]["FloatingRateBond"]["NPV"] = floatingRateBond.NPV();

        incomingJsonData["response"]["zeroCouponBond"]["cleanPrice"] = zeroCouponBond.cleanPrice();
        incomingJsonData["response"]["FixedRateBond"]["cleanPrice"] = fixedRateBond.cleanPrice();
        incomingJsonData["response"]["FloatingRateBond"]["cleanPrice"] = floatingRateBond.cleanPrice();

        incomingJsonData["response"]["zeroCouponBond"]["dirtyPrice"] = zeroCouponBond.dirtyPrice();
        incomingJsonData["response"]["FixedRateBond"]["dirtyPrice"] = fixedRateBond.dirtyPrice();
        incomingJsonData["response"]["FloatingRateBond"]["dirtyPrice"] = floatingRateBond.dirtyPrice();

        incomingJsonData["response"]["FixedRateBond"]["accruedAmount"] = fixedRateBond.accruedAmount();
        incomingJsonData["response"]["FloatingRateBond"]["accruedAmount"] = floatingRateBond.accruedAmount();

        incomingJsonData["response"]["zeroCouponBond"]["previousCouponRate"] = "N/A";
        incomingJsonData["response"]["FixedRateBond"]["previousCouponRate"] = fixedRateBond.previousCouponRate();
        incomingJsonData["response"]["FloatingRateBond"]["previousCouponRate"] = floatingRateBond.previousCouponRate();

        incomingJsonData["response"]["zeroCouponBond"]["nextCouponRate"] = "N/A";
        incomingJsonData["response"]["FixedRateBond"]["nextCouponRate"] = fixedRateBond.nextCouponRate();
        incomingJsonData["response"]["FloatingRateBond"]["nextCouponRate"] = floatingRateBond.nextCouponRate();

        incomingJsonData["response"]["zeroCouponBond"]["Yield"] = zeroCouponBond.yield(Actual360(),Compounded,Annual);
        incomingJsonData["response"]["FixedRateBond"]["Yield"] = fixedRateBond.yield(Actual360(),Compounded,Annual);
        incomingJsonData["response"]["FloatingRateBond"]["Yield"] = floatingRateBond.yield(Actual360(),Compounded,Annual);

        incomingJsonData["response"]["FloatingRateBond"]["YieldtoCleanPrice"] = floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(),Compounded,Annual),Actual360(),Compounded,Annual,settlementDate);
        incomingJsonData["response"]["FloatingRateBond"]["CleanPriceToYield"] = floatingRateBond.yield(floatingRateBond.cleanPrice(),Actual360(),Compounded,Annual,settlementDate);
         /* "Yield to Price"
            "Price to Yield" */

        std::cout << std::setw(4) << "Request: " << std::endl;
        std::cout << std::setw(4) << incomingJsonData << std::endl;

        std::cout << std::setw(4) << "Result: " << std::endl;
        std::cout << std::setw(4) << incomingJsonData["response"] << std::endl;

        std::string result = incomingJsonData.dump();
        std::cout << "result: " << result << std::endl;

         return result;

        } catch (std::exception& e) {
                std::cerr << e.what() << std::endl;
                return "1";
        } catch (...) {
                std::cerr << "unknown error" << std::endl;
                return "1";
        }
}

std::string calculateConvertibleBonds(std::string data){
        try {

                json jData = json::parse(data);
                std::cout << std::endl;

                Option::Type type(Option::Put);
                Real underlying = jData["request"]["underlying"];
                Real spreadRate = jData["request"]["spreadRate"];

                Spread dividendYield = jData["request"]["dividendYield"];
                Rate riskFreeRate = jData["request"]["riskFreeRate"];
                Volatility volatility =jData["request"]["volatility"];

                Integer settlementDays = jData["request"]["settlementDays"];
                Integer length = jData["request"]["length"];
                Real redemption = jData["request"]["redemption"];
                Real conversionRatio = redemption/underlying; // at the money

                // set up dates/schedules
                Calendar calendar = TARGET();
                Date today = calendar.adjust(Date::todaysDate());

                Settings::instance().evaluationDate() = today;
                Date settlementDate = calendar.advance(today, settlementDays, Days);
                Date exerciseDate = calendar.advance(settlementDate, length, Years);
                Date issueDate = calendar.advance(exerciseDate, -length, Years);

                BusinessDayConvention convention = ModifiedFollowing;

                Frequency frequency = Annual;

                Schedule schedule(issueDate, exerciseDate,
                                Period(frequency), calendar,
                                convention, convention,
                                DateGeneration::Backward, false);

                DividendSchedule dividends;
                CallabilitySchedule callability;

                std::vector<Real> coupons(1, 0.05);

                DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);

                Integer callLength[] = {
                        jData["request"]["calls"]["callLength"]["1"].get<int>(),
                        jData["request"]["calls"]["callLength"]["2"].get<int>()
                };
                Integer putLength[] = {
                        jData["request"]["puts"]["putLength"]["1"].get<int>()
                };

                Real callPrices[] = {
                        jData["request"]["calls"]["callPrices"]["1"].get<double>(),
                        jData["request"]["calls"]["callPrices"]["2"].get<double>()
                };
                Real putPrices[]= {
                        jData["request"]["puts"]["putPrices"]["1"].get<double>()
                };

                // Load call schedules
                for (Size i=0; i<LENGTH(callLength); i++) {
                callability.push_back(
                        ext::make_shared<SoftCallability>(Bond::Price(callPrices[i],
                                                                        Bond::Price::Clean),
                                                        schedule.date(callLength[i]),
                                                        1.20));
                }

                for (Size j=0; j<LENGTH(putLength); j++) {
                callability.push_back(
                        ext::make_shared<Callability>(Bond::Price(putPrices[j],
                                                                Bond::Price::Clean),
                                                        Callability::Put,
                                                        schedule.date(putLength[j])));
                }

                // Assume dividends are paid every 6 months.
                for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) {
                dividends.push_back(
                        ext::shared_ptr<Dividend>(new FixedDividend(1.0, d)));
                }

                DayCounter dayCounter = Actual365Fixed();
                Time maturity = dayCounter.yearFraction(settlementDate,
                                                        exerciseDate);

                std::cout << "option type = "  << type << std::endl;
                std::cout << "Time to maturity = "        << maturity
                        << std::endl;
                std::cout << "Underlying price = "        << underlying
                        << std::endl;
                std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate)
                        << std::endl;
                std::cout << "Dividend yield = " << io::rate(dividendYield)
                        << std::endl;
                std::cout << "Volatility = " << io::volatility(volatility)
                        << std::endl;
                std::cout << std::endl;

                std::string method;
                std::cout << std::endl ;

                // write column headings
                Size widths[] = { 35, 14, 14 };
                Size totalWidth = widths[0] + widths[1] + widths[2];
                std::string rule(totalWidth, '-'), dblrule(totalWidth, '=');

                std::cout << dblrule << std::endl;
                std::cout << "Tsiveriotis-Fernandes method" << std::endl;
                std::cout << dblrule << std::endl;
                std::cout << std::setw(widths[0]) << std::left << "Tree type"
                        << std::setw(widths[1]) << std::left << "European"
                        << std::setw(widths[1]) << std::left << "American"
                        << std::endl;

                std::cout << rule << std::endl;

                ext::shared_ptr<Exercise> exercise(
                                                new EuropeanExercise(exerciseDate));
                ext::shared_ptr<Exercise> amExercise(
                                                new AmericanExercise(settlementDate,
                                                                exerciseDate));

                Handle<Quote> underlyingH(
                ext::shared_ptr<Quote>(new SimpleQuote(underlying)));

                Handle<YieldTermStructure> flatTermStructure(
                ext::shared_ptr<YieldTermStructure>(
                        new FlatForward(settlementDate, riskFreeRate, dayCounter)));

                Handle<YieldTermStructure> flatDividendTS(
                ext::shared_ptr<YieldTermStructure>(
                        new FlatForward(settlementDate, dividendYield, dayCounter)));

                Handle<BlackVolTermStructure> flatVolTS(
                ext::shared_ptr<BlackVolTermStructure>(
                        new BlackConstantVol(settlementDate, calendar,
                                        volatility, dayCounter)));

                ext::shared_ptr<BlackScholesMertonProcess> stochasticProcess(
                                new BlackScholesMertonProcess(underlyingH,
                                                                flatDividendTS,
                                                                flatTermStructure,
                                                                flatVolTS));

                Size timeSteps = jData["request"]["timeSteps"].get<int>();

                Handle<Quote> creditSpread(
                        ext::shared_ptr<Quote>(new SimpleQuote(spreadRate)));

                ext::shared_ptr<Quote> rate(new SimpleQuote(riskFreeRate));

                Handle<YieldTermStructure> discountCurve(
                        ext::shared_ptr<YieldTermStructure>(
                        new FlatForward(today, Handle<Quote>(rate), dayCounter)));

                ext::shared_ptr<PricingEngine> engine(
                        new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess,
                                                                timeSteps));

                ConvertibleFixedCouponBond europeanBond(
                                exercise, conversionRatio, dividends, callability,
                                creditSpread, issueDate, settlementDays,
                                coupons, bondDayCount, schedule, redemption);
                europeanBond.setPricingEngine(engine);

                ConvertibleFixedCouponBond americanBond(
                                amExercise, conversionRatio, dividends, callability,
                                creditSpread, issueDate, settlementDays,
                                coupons, bondDayCount, schedule, redemption);
                americanBond.setPricingEngine(engine);

                method = "Jarrow-Rudd";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Jarrow-Rudd"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Jarrow-Rudd"]["European"]["NPV"] = europeanBond.NPV();

                method = "Cox-Ross-Rubinstein";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Cox-Ross-Rubinstein"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Cox-Ross-Rubinstein"]["European"]["NPV"] = europeanBond.NPV();

                method = "Additive equiprobabilities";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<AdditiveEQPBinomialTree>(
                                                                stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<AdditiveEQPBinomialTree>(
                                                                stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Additive equiprobabilities"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Additive equiprobabilities"]["European"]["NPV"] = europeanBond.NPV();

                method = "Trigeorgis";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Trigeorgis"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Trigeorgis"]["European"]["NPV"] = europeanBond.NPV();

                method = "Tian";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                                new BinomialConvertibleEngine<Tian>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                                new BinomialConvertibleEngine<Tian>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Tian"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Tian"]["European"]["NPV"] = europeanBond.NPV();

                method = "Leisen-Reimer";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Leisen-Reimer"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Leisen-Reimer"]["European"]["NPV"] = europeanBond.NPV();

                method = "Joshi";
                europeanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<Joshi4>(stochasticProcess,
                                                                timeSteps)));
                americanBond.setPricingEngine(ext::shared_ptr<PricingEngine>(
                        new BinomialConvertibleEngine<Joshi4>(stochasticProcess,
                                                                timeSteps)));
                std::cout << std::setw(widths[0]) << std::left << method
                        << std::fixed
                        << std::setw(widths[1]) << std::left << europeanBond.NPV()
                        << std::setw(widths[2]) << std::left << americanBond.NPV()
                        << std::endl;

                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Joshi"]["American"]["NPV"] = americanBond.NPV();
                jData["response"]["Tsiveriotis-Fernandes method"]["treeType"]["Joshi"]["European"]["NPV"] = europeanBond.NPV();

                std::cout << dblrule << std::endl;
                std::cout << "result: " << std::setw(4) << jData["response"] << std::endl;
                std::string result = jData.dump();
                return result;
        } catch (std::exception& e) {
                std::cerr << e.what() << std::endl;
                return "1";
        } catch (...) {
                std::cerr << "unknown error" << std::endl;
                return "1";
        }
};

std::string calculateCVAIRS(std::string data){
try {

        json jData = json::parse(data);

        std::cout << std::endl;

        Calendar calendar = TARGET();
        Date todaysDate(DateParser::parseISO( jData["request"]["todaysDate"].get<std::string>()));
        // must be a business day
        todaysDate = calendar.adjust(todaysDate);

        Settings::instance().evaluationDate() = todaysDate;

        ext::shared_ptr<IborIndex>  yieldIndx(new Euribor3M());
        Size tenorsSwapMkt[] = {5, 10, 15, 20, 25, 30};

        // rates ignoring counterparty risk:
        Rate ratesSwapmkt[] = {.03249, .04074, .04463, .04675, .04775, .04811};

        std::vector<ext::shared_ptr<RateHelper> > swapHelpers;
        for(Size i=0; i<sizeof(tenorsSwapMkt)/sizeof(Size); i++)
        swapHelpers.push_back(ext::make_shared<SwapRateHelper>(
                Handle<Quote>(ext::shared_ptr<Quote>(
                                new SimpleQuote(ratesSwapmkt[i]))),
                tenorsSwapMkt[i] * Years,
                TARGET(),
                Quarterly,
                ModifiedFollowing,
                ActualActual(ActualActual::ISDA),
                yieldIndx));

        ext::shared_ptr<YieldTermStructure> swapTS(
        new PiecewiseYieldCurve<Discount,LogLinear>(
        2, TARGET(), swapHelpers, ActualActual(ActualActual::ISDA)));
        swapTS->enableExtrapolation();

        ext::shared_ptr<PricingEngine> riskFreeEngine(
        ext::make_shared<DiscountingSwapEngine>(
                Handle<YieldTermStructure>(swapTS)));

        std::vector<Handle<DefaultProbabilityTermStructure> >
        defaultIntensityTS;

        Size defaultTenors[] = {0, 12, 36, 60, 84, 120, 180, 240, 300,
                                360};// months
        // Three risk levels:
        Real intensitiesLow[] = {0.0036, 0.0036, 0.0065, 0.0099, 0.0111,
                                0.0177, 0.0177, 0.0177, 0.0177, 0.0177,
                                0.0177};
        Real intensitiesMedium[] = {0.0202, 0.0202, 0.0231, 0.0266, 0.0278,
                                0.0349, 0.0349, 0.0349, 0.0349, 0.0349,
                                0.0349};
        Real intensitiesHigh[] = {0.0534, 0.0534, 0.0564, 0.06, 0.0614, 0.0696,
                                0.0696, 0.0696, 0.0696, 0.0696, 0.0696};
        // Recovery rates:
        Real ctptyRRLow = 0.4, ctptyRRMedium = 0.35, ctptyRRHigh = 0.3;

        std::vector<Date> defaultTSDates;
        std::vector<Real> intesitiesVLow, intesitiesVMedium, intesitiesVHigh;

        for(Size i=0; i<sizeof(defaultTenors)/sizeof(Size); i++) {
        defaultTSDates.push_back(TARGET().advance(todaysDate,
                Period(defaultTenors[i], Months)));
        intesitiesVLow.push_back(intensitiesLow[i]);
        intesitiesVMedium.push_back(intensitiesMedium[i]);
        intesitiesVHigh.push_back(intensitiesHigh[i]);
        }

        defaultIntensityTS.emplace_back(ext::shared_ptr<DefaultProbabilityTermStructure>(
        new InterpolatedHazardRateCurve<BackwardFlat>(defaultTSDates, intesitiesVLow,
                                                        Actual360(), TARGET())));
        defaultIntensityTS.emplace_back(ext::shared_ptr<DefaultProbabilityTermStructure>(
        new InterpolatedHazardRateCurve<BackwardFlat>(defaultTSDates, intesitiesVMedium,
                                                        Actual360(), TARGET())));
        defaultIntensityTS.emplace_back(ext::shared_ptr<DefaultProbabilityTermStructure>(
        new InterpolatedHazardRateCurve<BackwardFlat>(defaultTSDates, intesitiesVHigh,
                                                        Actual360(), TARGET())));

        Volatility blackVol = 0.15;
        ext::shared_ptr<PricingEngine> ctptySwapCvaLow =
        ext::make_shared<CounterpartyAdjSwapEngine>(
                Handle<YieldTermStructure>(swapTS),
                blackVol,
                defaultIntensityTS[0],
                ctptyRRLow
                );

        ext::shared_ptr<PricingEngine> ctptySwapCvaMedium =
        ext::make_shared<CounterpartyAdjSwapEngine>(
                Handle<YieldTermStructure>(swapTS),
                blackVol,
                defaultIntensityTS[1],
                ctptyRRMedium);
        ext::shared_ptr<PricingEngine> ctptySwapCvaHigh =
        ext::make_shared<CounterpartyAdjSwapEngine>(
                Handle<YieldTermStructure>(swapTS),
                blackVol,
                defaultIntensityTS[2],
                ctptyRRHigh);

        defaultIntensityTS[0]->enableExtrapolation();
        defaultIntensityTS[1]->enableExtrapolation();
        defaultIntensityTS[2]->enableExtrapolation();

        /// SWAP RISKY REPRICE----------------------------------------------

        // fixed leg
        Frequency fixedLegFrequency = Quarterly;
        BusinessDayConvention fixedLegConvention = ModifiedFollowing;
        DayCounter fixedLegDayCounter = ActualActual(ActualActual::ISDA);
        DayCounter floatingLegDayCounter = ActualActual(ActualActual::ISDA);

        QuantLib::VanillaSwap::Type swapType = QuantLib::VanillaSwap::Payer;
        ext::shared_ptr<IborIndex> yieldIndxS(
        new Euribor3M(Handle<YieldTermStructure>(swapTS)));
        std::vector<VanillaSwap> riskySwaps;
        for(Size i=0; i<sizeof(tenorsSwapMkt)/sizeof(Size); i++)
        riskySwaps.push_back(MakeVanillaSwap(tenorsSwapMkt[i]*Years,
                yieldIndxS,
                ratesSwapmkt[i],
                0*Days)
        .withSettlementDays(2)
        .withFixedLegDayCount(fixedLegDayCounter)
        .withFixedLegTenor(Period(fixedLegFrequency))
        .withFixedLegConvention(fixedLegConvention)
        .withFixedLegTerminationDateConvention(fixedLegConvention)
        .withFixedLegCalendar(calendar)
        .withFloatingLegCalendar(calendar)
        .withNominal(100.)
        .withType(swapType));

        std::cout << "-- Correction in the contract fix rate in bp --" << std::endl;
        /* The paper plots correction to be substracted, here is printed
        with its sign
        */
        for(Size i=0; i<riskySwaps.size(); i++) {
            std::cout << std::fixed << std::setprecision(3);
            std::cout << std::setw(4);
            riskySwaps[i].setPricingEngine(riskFreeEngine);
            // should recover the input here:
            Real nonRiskyFair = riskySwaps[i].fairRate();
            std::cout << tenorsSwapMkt[i];
            std::cout << std::setw(5);

            std::cout << " | " << io::rate(nonRiskyFair);
            std::cout << std::fixed << std::setprecision(2);
            std::cout << std::setw(5);
            // Low Risk:
            riskySwaps[i].setPricingEngine(ctptySwapCvaLow);
            std::cout << " | " << std::setw(6)
                    << 10000.*(riskySwaps[i].fairRate() - nonRiskyFair);
            //cout << " | " << setw(6) << riskySwaps[i].NPV() ;

            // Medium Risk:
            riskySwaps[i].setPricingEngine(ctptySwapCvaMedium);
            std::cout << " | " << std::setw(6)
                    << 10000.*(riskySwaps[i].fairRate() - nonRiskyFair);
            //cout << " | " << setw(6) << riskySwaps[i].NPV() ;

            riskySwaps[i].setPricingEngine(ctptySwapCvaHigh);
            std::cout << " | " << std::setw(6)
                    << 10000.*(riskySwaps[i].fairRate() - nonRiskyFair);
            //cout << " | " << setw(6) << riskySwaps[i].NPV() ;

            std::cout << std::endl;
        }

        std::cout << std::endl;
        std::cout << "JDATA" << std::setw(4) << jData << std::endl;
        std::string result = jData.dump();
        return result;

    }

    catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
        return "1";
    }

    catch (...) {
        std::cerr << "unknown error" << std::endl;
        return "1";
    }
}


#include <algorithm>
#include <memory>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <climits>
#include <numeric>

#include <Eigen/Dense>

#undef N_DEBUG
#include <assert.h>

#define loop(x,n) for(std::size_t (x) = 0; (x) < (n); ++(x))

using namespace std;

using UInt = uint64_t;
using Int = int64_t;
using Vector3d = Eigen::Matrix<double, 3, 1>;

struct Asteroid
{
  using Ptr = std::shared_ptr<Asteroid>;
  using ConstPtr = std::shared_ptr<const Asteroid>;

  EIGEN_MAKE_ALIGNED_OPERATOR_NEW;

  Asteroid()  {}
  Asteroid(Vector3d pos, Vector3d vel)
    : position(pos)
    , velocity(vel)
  {}

  Vector3d position;
  Vector3d velocity;
};

double getMinDistance(Asteroid::ConstPtr a, Asteroid::ConstPtr b)
{
  const auto w = a->position - b->position;
  const auto v = a->velocity - b->velocity;
  const auto t_cpa = -w.dot(v) / v.squaredNorm();
  const auto p_a = a->position + t_cpa * a->velocity;
  const auto p_b = b->position + t_cpa * b->velocity;
  return (p_a - p_b).norm();
}

double getMinDistanceWithin(Asteroid::ConstPtr a, Asteroid::ConstPtr b, double t_min, double t_max)
{
  const auto w = a->position - b->position;
  const auto v = a->velocity - b->velocity;
  const auto t_cpa = -w.dot(v) / v.squaredNorm();

  double t;
  if (t_cpa < t_min)
    t = t_min;
  else if (t_cpa > t_max)
    t = t_max;
  else
    t = t_cpa;

  const auto p_a = a->position + t * a->velocity;
  const auto p_b = b->position + t * b->velocity;
  return (p_a - p_b).norm();
}

struct Edge
{
  Edge(size_t first, size_t second, double weight)
    : first_vertex(first)
    , second_vertex(second)
    , weight(weight)
  {}
  size_t first_vertex;
  size_t second_vertex;
  double weight;

  bool operator <(const Edge& other) const
  {
    if (weight != other.weight)
      return weight < other.weight;
    if (first_vertex != other.first_vertex)
      return first_vertex < other.first_vertex;
    return second_vertex < other.second_vertex;
  }
  bool contains(size_t vertex) const  { return first_vertex == vertex || second_vertex == vertex; }
  size_t getOther(size_t vertex) const
  {
    if (first_vertex == vertex)
      return second_vertex;
    return first_vertex;
  }
};

struct Cluster
{
  Cluster() {}
  Cluster(const auto& asteroids, auto velocity)
    : asteroids(asteroids)
    , velocity(velocity)
  {}
  Cluster(const auto& asteroid)
    : asteroids({asteroid})
    , velocity(asteroid->velocity)
  {}

  double getMaxJumpDistance(Asteroid::ConstPtr start, Asteroid::ConstPtr end) const
  {
    const auto s = find(asteroids.cbegin(), asteroids.cend(), start);
    if (s == asteroids.cend())
      throw (int)1;

    size_t index_start = std::distance(asteroids.cbegin(), s);

    const auto e = find(asteroids.cbegin(), asteroids.cend(), end);
    if (e == asteroids.cend())
      throw (int)2;

    size_t index_end = std::distance(asteroids.cbegin(), e);

    auto unexplored = edges;
    set<size_t> explored;
    explored.insert(index_start);
    double max_jump = 0.0;
    size_t added = 0;
    do
    {
      for (auto it = unexplored.begin(); it != unexplored.end(); it++)
      {
        const auto first_it = explored.find(it->first_vertex);
        const auto second_it = explored.find(it->second_vertex);

        auto ex = explored.cend();
        if (first_it != explored.cend())
          ex = first_it;
        else if (second_it != explored.cend())
          ex = second_it;
        else
          continue;

        added = it->getOther(*ex);
        explored.insert(added);
        if (it->weight > max_jump)
          max_jump = it->weight;
        unexplored.erase(it);

        break;
      }
    }
    while (added != index_end);

    return sqrt(max_jump);
  }

  const vector<Asteroid::ConstPtr> getAllReachedAsteroids(Asteroid::ConstPtr start, double max_jump)
  {
    max_jump = max_jump*max_jump;

    const auto s = find(asteroids.cbegin(), asteroids.cend(), start);
    if (s == asteroids.cend())
      throw (int)3;

    vector<Asteroid::ConstPtr> reached({start});
    size_t index_start = std::distance(asteroids.cbegin(), s);

    vector<bool> explored(asteroids.size(), false);
    explored[index_start] = true;

    size_t added;
    do
    {
      added = asteroids.size();

      loop(ex, explored.size())
      {
        if (!explored[ex])
          continue;

        loop(unex, explored.size())
        {
          if (explored[unex])
            continue;

          if (getWeight(ex, unex) <= max_jump)
          {
            added = unex;
            explored[unex] = true;
            break;
          }

        }
        if (added != asteroids.size())
          break;

      }

      reached.push_back(asteroids[added]);

    }
    while (added < asteroids.size());

    return reached;
  }

  vector<Asteroid::Ptr> asteroids;
  Vector3d velocity;

  double getWeight(size_t first, size_t second) const
  {
    assert(first != second);
    const auto min_index = min(first, second);
    const auto max_index = max(first, second);
    return weights[min_index][max_index-min_index-1];
  }
  void calcWeights()
  {
    weights.resize(asteroids.size() - 1);
    loop(ii, asteroids.size() - 1)
    {
      weights[ii].reserve(asteroids.size() - ii - 1);
      for (size_t jj = ii + 1; jj < asteroids.size(); ++jj)
      {
        weights[ii].push_back((asteroids[ii]->position - asteroids[jj]->position).squaredNorm());
        edges.emplace(ii, jj, weights[ii].back());
      }
    }
  }
  vector<vector<double>> weights;
  set<Edge> edges;
};

int main()
{
  size_t num_test_cases;
  cin >> num_test_cases;

  for (size_t i = 0; i < num_test_cases; i++)
  {
    size_t N, S;
    cin >> N >> S;

    vector<Asteroid::Ptr> asteroids;
    asteroids.reserve(N);

    loop(ii, N)
    {
      Vector3d pos;
      Vector3d vel;

      cin >> pos[0] >> pos[1] >> pos[2]
          >> vel[0] >> vel[1] >> vel[2];

      Asteroid a;
      a.position = pos;
      a.velocity = vel;
      asteroids.emplace_back(make_shared<Asteroid>(pos, vel));
    }

    // sort into clusters
    vector<Cluster> clusters;
    for (const auto& a : asteroids)
    {
      const auto it = std::find_if(clusters.begin(), clusters.end(), [&a](const vector<Cluster>::value_type& c){
          return c.velocity == a->velocity;});

      if (it == clusters.end())
      {
        clusters.emplace_back(a);
      }
      else
      {
        it->asteroids.push_back(a);
      }
    }

    //cout <<"Number of clusters: " <<clusters.size() <<endl;
    for (auto& a : clusters)
      a.calcWeights();

    double res = 0.0;
    if (clusters.size() == 1)
    {
      res = clusters.front().getMaxJumpDistance(asteroids[0], asteroids[1]);
      std::cout <<"Case #" <<(i+1) <<": "  <<setprecision(10) <<res <<endl;
    }
    else
    {
      if (asteroids[0]->velocity != asteroids[1]->velocity)
        res = getMinDistanceWithin(asteroids[0], asteroids[1], 0.0, S);
      else
        res = -1.0;

      std::cout <<"Case #" <<(i+1) <<": "  <<setprecision(10) <<res <<endl;
    }

  }

  return 0;
}


/*Deep euler implementation*/

#include <iostream>
#include <fstream>
#define _USE_MATH_DEFINES
#include <cmath>
#include <vector>
#include <string>
#include <chrono>
#include <regex>

#include <boost/program_options.hpp>
#include <torch/script.h>

using namespace std;

//const int N = 16;
//const int N_z = N / 2 - 1;
const int nn_inputs = 4;
const int nn_outputs = 2; // output dimension
c10::TensorOptions global_tensor_op;

string file_name = "../lotka_dem.txt";
string file_name_normal_euler = "../lotka_euler.txt";
string time_counting_file_name = "../clock.txt";
string model_file = "../../training/traced_model_e20_2021_11_04.pt";
string range_model_file = "../../training/traced_range_model_e20_2021_11_04.pt";
string embedded_model_file = "../../training/traced_range_embedded_model_e22_2021_11_10.pt";
static bool use_embedded = false;
static bool use_generalized = false;

typedef double value_type;
typedef vector<value_type> state_type;

//ode function of bubble dynamic
class lotka {
//	std::ofstream outputs_out;
public:
	torch::jit::script::Module model;
	torch::Tensor inputs; //reused tensor of inputs

	lotka(std::vector<double> inital_values) {

		//metamodel initializations
		int _size = inital_values.size();
		inputs = torch::ones({ 1, _size }, global_tensor_op);
		//model inputs: dt x1 x2 x3 z... x1now x2now x3now
		for (int i = 0; i < _size; i++) inputs[0][i] = inital_values[i];
		//outputs: z... grad_z(wall)
		try {
			if (use_embedded) {
				model = torch::jit::load(embedded_model_file);
			}
			else if(use_generalized) {
				model = torch::jit::load(range_model_file);
			}
			else {
				model = torch::jit::load(model_file);
			}
			std::vector<torch::jit::IValue> inp;
			inp.push_back(torch::ones({ 1, _size }, global_tensor_op));
			std::cout << inp << endl;
			// Execute the model and turn its output into a tensor.
			at::Tensor output = model.forward(inp).toTensor().detach();
			std::cout << output << endl;
		}
		catch (const c10::Error& e) {
			std::cerr << "Error loading the model: " << e.what() << endl;
			//	exit(-1);
		}
	}

	/*Rewrites the errors array with the predicted local truncation errors*/
	void local_error(double t, double t_next, const double* x, double* errors) {
		//updating inputs
		inputs[0][0] = t_next;
		inputs[0][1] = t;
		for (int i = 0; i < nn_inputs - 2; i++) {
			inputs[0][i + 2] = x[i];
		}
		std::vector<torch::jit::IValue> inps;
		inps.push_back(inputs);
		//evaluating
		torch::Tensor loc_trun_err = model.forward(inps).toTensor().detach();

		for (int i = 0; i < nn_outputs; i++) {
			errors[i] = loc_trun_err[0][i].item<double>();
		}
	}

	/*ODE function*/
	inline void operator()(double t, double* x, double* dx) {
		dx[0] = x[0] - x[0] * x[1];
		dx[1] = -x[1] + x[0] * x[1];
	}
};

class ODESolver
{
public:
	// order is the number of the output
	ODESolver(int order): order(order) {
	// initialize necessary data structures
		derivative = (double*)malloc(sizeof(double) * order);
		local_error = (double*)malloc(sizeof(double) * order);
	};

	bool setInitialCondition(double* conds, double at) {
		begin_t = at;
		init_conds = (double*)malloc(sizeof(double) * order);
		for (int u = 0; u < order; u++) {
			init_conds[u] = conds[u];
		}
		return true;
	}

	void setTimeStep(double dt) {
		delta_t = dt;
	}

	bool setStepNumber(int steps) {
		max_l = steps;
		sol_t = (double*)malloc(sizeof(double) * (max_l + 1));
		sol = new double* [max_l + 1];
		for (int i = 0; i < max_l + 1; i++)
			sol[i] = new double[order];
		return true;
	}

	void solve(lotka& lot) {

		for (int u = 0; u < order; u++) {
			sol[0][u] = init_conds[u];
		}

		double t = begin_t;
		sol_t[0] = t;
		int l = 0;
		while (l < max_l) {
			lot.local_error(t, t + delta_t, sol[l], local_error); // fill the local_error
			lot(t, sol[l], derivative); // fill the derivative
			for (int j = 0; j < order; j++) {
				sol[l + 1][j] = sol[l][j] + delta_t * derivative[j] + delta_t * delta_t * local_error[j];
			}
			l++;
			t += delta_t;
			sol_t[l] = t;
		}
	}

	// disable residue approximation
	void solve_normal(lotka& lot) {

		for (int u = 0; u < order; u++) {
			sol[0][u] = init_conds[u];
		}

		double t = begin_t;
		sol_t[0] = t;
		int l = 0;
		while (l < max_l) {
			lot(t, sol[l], derivative); // fill the derivative
			for (int j = 0; j < order; j++) {
				sol[l + 1][j] = sol[l][j] + delta_t * derivative[j];
			}
			l++;
			t += delta_t;
			sol_t[l] = t;
		}
	}

	// output sol_t and sol
	void output(ofstream& out) {
		for (int i = 0; i <= max_l; i++) {
			out << sol_t[i];
			for (int j = 0; j < order; j++) {
				out << ' ' << sol[i][j];
			}
			out << '\n';
		}
	}

	~ODESolver(){
		free(init_conds);
		free(derivative);
		free(sol_t);
		for (int indx = 0; indx <= max_l; ++indx)
		{
			delete sol[indx];
		}
		delete[] sol;
	}
private:
	int order = 1;
	double* local_error;
	double* init_conds;
	double* sol_t;
	double** sol;
	double* derivative;
	double begin_t = 0;
	double delta_t = 0.1;
	int max_l = 10;
};

void setup_ofstream(ofstream& ofs) {
	if(!ofs.is_open())exit(-1);
	ofs.precision(17);
	ofs.flags(ios::scientific);
}

int main(int argc, const char* argv[]) {
	boost::program_options::options_description desc;
	desc.add_options()
		("help,h", "Show this help screen")
		("embedded", boost::program_options::value<bool>()->implicit_value(true)->default_value(false), "whether to use embedded model")
		("generalized", boost::program_options::value<bool>()->implicit_value(true)->default_value(false), "whether to use embedded model");

	boost::program_options::variables_map vm;
	boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
	boost::program_options::notify(vm);
	if (vm.count("help")) {
		std::cout << desc << '\n';
		return 0;
	}
	use_embedded = vm["embedded"].as<bool>();
	use_generalized = vm["generalized"].as<bool>();
	global_tensor_op = torch::TensorOptions().dtype(torch::kFloat64);
	std::cout << "Lotka Volterra with meta-model started\n" << setprecision(17) << endl;

	double* x = new double[nn_outputs]{ 2.0, 1.0 };

	double dem_step_list[] = {0.1, 0.05, 0.01, 0.005};
	double euler_step_list[] = { 0.002, 0.001, 0.0005, 0.0002};
	int step_list_length = (sizeof(dem_step_list) / sizeof(*dem_step_list));
	std::regex _regex(".txt");
	std::string replace_base;
	if (use_embedded) {
		replace_base = "_embedded.txt";
	}
	else if (use_generalized) {
		replace_base = "_generalized.txt";
	}
	else {
		replace_base = ".txt";
	}
	std::string time_counting_file_name_special = std::regex_replace(time_counting_file_name, _regex, replace_base);
	ofstream clock_of(time_counting_file_name_special);
	double t_stop = 15.0;
	//initial conditions
	std::vector<double> initial_inputs;
	if (use_generalized) {
		initial_inputs = { 0.0, 0.1, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0 };
	}
	else {
		initial_inputs = { 1e-5, 0.0, 2.0, 1.0 };
	}
	for (int i = 0; i < step_list_length; i++) {

		std::string file_name_i = std::regex_replace(file_name, _regex, std::to_string(i) + replace_base);
		std::string file_name_normal_euler_i = std::regex_replace(file_name_normal_euler, _regex, std::to_string(i) + ".txt");

		ofstream ofs(file_name_i);
		setup_ofstream(ofs);
		std::cout << "Writing file: " << file_name_i << endl;
		ofstream ofs_2(file_name_normal_euler_i);
		setup_ofstream(ofs_2);

		double t_start = 0.0;
		lotka bubi(initial_inputs);

		ODESolver solver(nn_outputs), solver_2(nn_outputs);
		solver.setInitialCondition(x, 0.0);
		solver.setTimeStep(dem_step_list[i]);
		solver.setStepNumber(int(t_stop/ dem_step_list[i]));

		std::cout << "Solving..." << endl;
		auto t1 = chrono::high_resolution_clock::now();
		solver.solve(bubi);
		auto t2 = chrono::high_resolution_clock::now();
		// std::cout << "DEM Time (ms):" << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << endl;
		clock_of << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
		solver.output(ofs);

		solver_2.setInitialCondition(x, 0.0);
		solver_2.setTimeStep(euler_step_list[i]);
		solver_2.setStepNumber(int(t_stop / euler_step_list[i]));
		t1 = chrono::high_resolution_clock::now();
		solver_2.solve_normal(bubi);
		t2 = chrono::high_resolution_clock::now();
		// std::cout << "EM Time (ms):" << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << endl;
		clock_of << ' ' << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << endl;
		solver_2.output(ofs_2);
		ofs.flush();
		ofs.close();
		ofs_2.flush();
		ofs_2.close();
	}
	clock_of.close();
	std::cout << "Ready"<< endl;
	return 0;
}


#include <armadillo>
#include <boost/program_options.hpp>
#include <HSMM.hpp>
#include <iostream>
#include <json.hpp>
#include <memory>
#include <ProMPs_emission.hpp>

using namespace arma;
using namespace hsmm;
using namespace std;
using json = nlohmann::json;
namespace po = boost::program_options;

mat fieldToMat(int njoints, field<mat> &samples) {
    mat ret(njoints, samples.n_elem);
    for(int i = 0; i < samples.n_elem; i++)
        ret.col(i) = samples(i);
    return ret;
}

vec pmfFromGaussian(double mean, double var, int size, int min_duration) {
    vec pmf(size);
    for (int t = min_duration; t < min_duration + size; t++) {
        int idx = t - min_duration;
        double tmp = ((t - mean) * (t - mean)) / var;
        pmf(idx) = exp(-0.5 * tmp);
    }
    pmf = pmf * (1.0 / sum(pmf));
    return pmf;
}

int main(int argc, char *argv[]) {
    po::options_description desc("Options");
    desc.add_options()
        ("help,h", "Produce help message")
        ("params,p", po::value<string>(), "JSON input params (optional)")
        ("output,o", po::value<string>(), "Filename to store the obs")
        ("vit,v", po::value<string>(), "Filename to store the viterbi output")
        ("ms", po::value<string>(), "state marginals file name")
        ("mr", po::value<string>(), "runlength marginals file name")
        ("md", po::value<string>(), "duration marginals file name ")
        ("imd", po::value<string>(), "implicit duration marginals file name."
                " This means it is computed from the runlength and state")
        ("polybasisfun", po::value<int>()->default_value(1), "Order of the"
                " poly basis")
        ("rbfbasisfun", po::value<int>()->default_value(3), "Number of radial"
                " basis functions to use between 0 and 1. 0,1 are excluded.")
        ("delta", po::value<double>(), "delta between sample locations")
        ("print_ll_vit", "If set then the ll from each hs for"
                " every ground truth segment is printed. Intended for "
                "debugging purposes");
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);
    if (vm.count("help")) {
        cout << desc << endl;
        return 0;
    }
    int min_duration = 30;
    mat transition = ones<mat>(2, 2);
    transition.diag().zeros();
    int nstates = transition.n_rows;
    int ndurations = 30;
    vec pi = {0.5, 0.5};
    mat durations(nstates, ndurations);
    durations.row(0) = conv_to<rowvec>::from(pmfFromGaussian(
                40, 16, ndurations, min_duration));
    durations.row(1) = conv_to<rowvec>::from(pmfFromGaussian(
                50, 16, ndurations, min_duration));
    int njoints = 1;
    json input_params;
    if (vm.count("params")) {
        string params = vm["params"].as<string>();
        ifstream input_params_file(params);
        input_params_file >> input_params;
        min_duration = input_params["min_duration"];
        nstates = input_params["nstates"];
        ndurations = input_params["ndurations"];
        transition = eye<mat>(nstates, nstates);
        pi = eye<vec>(nstates, 1);
        durations = eye<mat>(nstates, ndurations);
        njoints = input_params["emission_params"][0]["num_joints"];
    }

    // Setting a combination of polynomial and rbf basis functions.
    auto poly = make_shared<ScalarPolyBasis>(vm["polybasisfun"].as<int>());
    auto comb = shared_ptr<ScalarCombBasis>(new ScalarCombBasis({poly}));
    int nrbf = vm["rbfbasisfun"].as<int>();
    if (nrbf > 0) {
        vec centers = linspace<vec>(0, 1.0, nrbf + 2);
        centers = centers.subvec(1, centers.n_elem - 2);
        auto rbf = shared_ptr<ScalarGaussBasis>(new ScalarGaussBasis(centers,
                    0.25));
        comb = shared_ptr<ScalarCombBasis>(new ScalarCombBasis({rbf, poly}));
    }
    int n_basis_functions = comb->dim();
    int nparameters = n_basis_functions * njoints;

    // Instantiating as many ProMPs as hidden states.
    vector<FullProMP> promps;
    for(int i = 0; i < nstates; i++) {
        vec mu_w = zeros<vec>(nparameters);
        mat Sigma_w = eye<mat>(nparameters, nparameters);
        mat Sigma_y = 0.00001*eye<mat>(njoints, njoints);
        ProMP promp(mu_w, Sigma_w, Sigma_y);
        FullProMP poly(comb, promp, njoints);
        promps.push_back(poly);
    }

    // Creating the ProMP emission.
    shared_ptr<AbstractEmissionOnlineSetting> ptr_emission(new ProMPsEmission(
                promps));

    OnlineHSMM promp_hsmm(ptr_emission, transition, pi, durations,
            min_duration);
    if (vm.count("params"))
        promp_hsmm.from_stream(input_params);

    int nseq = 1;
    int nsegments = 10;
    field<ivec> hidden_states, hidden_durations;
    field<field<mat>> multiple_toy_obs = promp_hsmm.sampleMultipleSequences(
            nseq, nsegments, hidden_states, hidden_durations);
    cout << "Generated states and durations for the first sequence" << endl;
    imat viterbi = join_horiz(hidden_states(0), hidden_durations(0));
    cout << viterbi << endl;

    cout << "Model Parameters" << endl;
    json params = promp_hsmm.to_stream();
    cout << params.dump(4) << endl;

    mat obs = fieldToMat(njoints, multiple_toy_obs(0));
    if (vm.count("output"))
        obs.save(vm["output"].as<string>(), raw_ascii);
    if (vm.count("vit"))
        viterbi.save(vm["vit"].as<string>(), raw_ascii);

    // Note that delta only affects the online filtering, not the generation.
    if (vm.count("delta"))
        std::static_pointer_cast<ProMPsEmission>(
                ptr_emission)->setDelta(vm["delta"].as<double>());

    if (vm.count("print_ll_vit")) {
        int idx = 0;
        for(int i = 0; i < viterbi.n_rows; i++) {
            int dur = viterbi(i, 1);
            int hs = viterbi(i, 0);
            cout << "Segment #" << i << ": (" << hs << "," << dur << ")" <<
                    endl;
            const field<mat>& segment = multiple_toy_obs(0).rows(idx,
                    idx + dur - 1);
            idx += dur;
            for(int i = 0; i < nstates; i++) {
                double ll = promp_hsmm.emission_->loglikelihood(i, segment);
                double lld = ll + log(promp_hsmm.duration_(i, dur - min_duration));
                cout << "State " << i << ": " << ll << " with dur: " << lld <<
                        endl;
            }
        }
    }

    mat state_marginals_over_time(nstates, obs.n_cols);
    mat runlength_marginals_over_time(min_duration + ndurations,
            obs.n_cols);
    mat duration_marginals_over_time(ndurations, obs.n_cols);
    mat implicit_duration_marginals_over_time(ndurations, obs.n_cols);
    for(int c = 0; c < obs.n_cols; c++) {
        promp_hsmm.addNewObservation(obs.col(c));
        vec s_marginal = promp_hsmm.getStateMarginal();
        state_marginals_over_time.col(c) = s_marginal;
        vec r_marginal = promp_hsmm.getRunlengthMarginal();
        runlength_marginals_over_time.col(c) = r_marginal;
        vec d_marginal = promp_hsmm.getDurationMarginal();
        duration_marginals_over_time.col(c) = d_marginal;
        if (vm.count("imd")) {
            vec indirect_d_marginal = promp_hsmm.getImplicitDurationMarginal();
            implicit_duration_marginals_over_time.col(c) = indirect_d_marginal;
        }
        if (vm.count("print_ll_vit")) {
            cout << "Obs idx: " << c << endl;
            promp_hsmm.printTopKFromPosterior(40);
        }
    }

    // Saving the marginals if required.
    if (vm.count("ms"))
        state_marginals_over_time.save(vm["ms"].as<string>(), raw_ascii);
    if (vm.count("mr"))
        runlength_marginals_over_time.save(vm["mr"].as<string>(), raw_ascii);
    if (vm.count("md"))
        duration_marginals_over_time.save(vm["md"].as<string>(), raw_ascii);
    if (vm.count("imd"))
        implicit_duration_marginals_over_time.save(vm["imd"].as<string>(),
                raw_ascii);
    return 0;
}