/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2018 StatPro Italia srl

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include "callablebonds.hpp"
#include "utilities.hpp"
#include <ql/experimental/callablebonds/callablebond.hpp>
#include <ql/experimental/callablebonds/treecallablebondengine.hpp>
#include <ql/instruments/bonds/zerocouponbond.hpp>
#include <ql/instruments/bonds/fixedratebond.hpp>
#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/schedule.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/models/shortrate/onefactormodels/hullwhite.hpp>
#include <boost/make_shared.hpp>
#include <iomanip>

using namespace QuantLib;
using namespace boost::unit_test_framework;

namespace {

    struct Globals {
        // global data
        Date today, settlement;
        Calendar calendar;
        DayCounter dayCounter;
        BusinessDayConvention rollingConvention;

        RelinkableHandle<YieldTermStructure> termStructure;
        RelinkableHandle<ShortRateModel> model;

        SavedSettings backup;

        Date issueDate() const {
            // ensure that we're in mid-coupon
            return calendar.adjust(today - 100*Days);
        }

        Date maturityDate() const {
            // ensure that we're in mid-coupon
            return calendar.advance(issueDate(),10,Years);
        }

        std::vector<Date> evenYears() const {
            std::vector<Date> dates;
            for (Size i=2; i<10; i+=2)
                dates.push_back(calendar.advance(issueDate(),i,Years));
            return dates;
        }

        std::vector<Date> oddYears() const {
            std::vector<Date> dates;
            for (Size i=1; i<10; i+=2)
                dates.push_back(calendar.advance(issueDate(),i,Years));
            return dates;
        }

        template <class R>
        ext::shared_ptr<YieldTermStructure> makeFlatCurve(const R& r) const {
            return ext::shared_ptr<YieldTermStructure>(
                                  new FlatForward(settlement, r, dayCounter));
        }

        Globals() {
            calendar = TARGET();
            dayCounter = Actual365Fixed();
            rollingConvention = ModifiedFollowing;

            today = Date::todaysDate();
            Settings::instance().evaluationDate() = today;
            settlement = calendar.advance(today,2,Days);
        }
    };

}

void CallableBondTest::testInterplay() {

    BOOST_TEST_MESSAGE("Testing interplay of callability and puttability for callable bonds...");

    Globals vars;

    vars.termStructure.linkTo(vars.makeFlatCurve(0.03));
    vars.model.linkTo(ext::make_shared<HullWhite>(vars.termStructure));

    Size timeSteps = 240;

    ext::shared_ptr<PricingEngine> engine =
        ext::make_shared<TreeCallableZeroCouponBondEngine>(
                                *(vars.model), timeSteps, vars.termStructure);

    /* case 1: an earlier out-of-the-money callability must prevent
               a later in-the-money puttability
    */

    CallabilitySchedule callabilities;

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(100.0, Bond::Price::Clean),
                         Callability::Call,
                         vars.calendar.advance(vars.issueDate(),4,Years)));

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(1000.0, Bond::Price::Clean),
                         Callability::Put,
                         vars.calendar.advance(vars.issueDate(),6,Years)));

    CallableZeroCouponBond bond(3, 100.0, vars.calendar,
                                vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention, 100.0,
                                vars.issueDate(), callabilities);
    bond.setPricingEngine(engine);

    double expected = callabilities[0]->price().amount() *
                      vars.termStructure->discount(callabilities[0]->date()) /
                      vars.termStructure->discount(bond.settlementDate());

    if (std::fabs(bond.settlementValue() - expected) > 1.0e-2)
        BOOST_ERROR(
            "callability not exercised correctly:\n"
            << std::setprecision(5)
            << "    calculated NPV: " << bond.settlementValue() << "\n"
            << "    expected:       " << expected << "\n"
            << "    difference:     " << bond.settlementValue()-expected);

    /* case 2: same as case 1, with an added callability later on */

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(100.0, Bond::Price::Clean),
                         Callability::Call,
                         vars.calendar.advance(vars.issueDate(),8,Years)));

    bond = CallableZeroCouponBond(3, 100.0, vars.calendar,
                                  vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                  vars.rollingConvention, 100.0,
                                  vars.issueDate(), callabilities);
    bond.setPricingEngine(engine);

    if (std::fabs(bond.settlementValue() - expected) > 1.0e-2)
        BOOST_ERROR(
            "callability not exercised correctly:\n"
            << std::setprecision(5)
            << "    calculated NPV: " << bond.settlementValue() << "\n"
            << "    expected:       " << expected << "\n"
            << "    difference:     " << bond.settlementValue()-expected);

    /* case 3: an earlier in-the-money puttability must prevent
               a later in-the-money callability
    */

    callabilities.clear();

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(100.0, Bond::Price::Clean),
                         Callability::Put,
                         vars.calendar.advance(vars.issueDate(),4,Years)));

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(10.0, Bond::Price::Clean),
                         Callability::Call,
                         vars.calendar.advance(vars.issueDate(),6,Years)));

    bond = CallableZeroCouponBond(3, 100.0, vars.calendar,
                                  vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                  vars.rollingConvention, 100.0,
                                  vars.issueDate(), callabilities);
    bond.setPricingEngine(engine);

    expected = callabilities[0]->price().amount() *
               vars.termStructure->discount(callabilities[0]->date()) /
               vars.termStructure->discount(bond.settlementDate());

    if (std::fabs(bond.settlementValue() - expected) > 1.0e-2)
        BOOST_ERROR(
            "puttability not exercised correctly:\n"
            << std::setprecision(5)
            << "    calculated NPV: " << bond.settlementValue() << "\n"
            << "    expected:       " << expected << "\n"
            << "    difference:     " << bond.settlementValue()-expected);

    /* case 4: same as case 3, with an added puttability later on */

    callabilities.push_back(ext::make_shared<Callability>(
                         Bond::Price(100.0, Bond::Price::Clean),
                         Callability::Put,
                         vars.calendar.advance(vars.issueDate(),8,Years)));

    bond = CallableZeroCouponBond(3, 100.0, vars.calendar,
                                  vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                  vars.rollingConvention, 100.0,
                                  vars.issueDate(), callabilities);
    bond.setPricingEngine(engine);

    if (std::fabs(bond.settlementValue() - expected) > 1.0e-2)
        BOOST_ERROR(
            "puttability not exercised correctly:\n"
            << std::setprecision(5)
            << "    calculated NPV: " << bond.settlementValue() << "\n"
            << "    expected:       " << expected << "\n"
            << "    difference:     " << bond.settlementValue()-expected);
}

void CallableBondTest::testConsistency() {

    BOOST_TEST_MESSAGE("Testing consistency of callable bonds...");

    Globals vars;

    vars.termStructure.linkTo(vars.makeFlatCurve(0.032));
    vars.model.linkTo(ext::make_shared<HullWhite>(vars.termStructure));

    Schedule schedule =
        MakeSchedule()
        .from(vars.issueDate())
        .to(vars.maturityDate())
        .withCalendar(vars.calendar)
        .withFrequency(Semiannual)
        .withConvention(vars.rollingConvention)
        .withRule(DateGeneration::Backward);

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

    FixedRateBond bond(3, 100.0, schedule,
                       coupons, Thirty360(Thirty360::BondBasis));
    bond.setPricingEngine(
               ext::make_shared<DiscountingBondEngine>(vars.termStructure));

    CallabilitySchedule callabilities;
    std::vector<Date> callabilityDates = vars.evenYears();
    for (auto& callabilityDate : callabilityDates) {
        callabilities.push_back(ext::make_shared<Callability>(
            Bond::Price(110.0, Bond::Price::Clean), Callability::Call, callabilityDate));
    }

    CallabilitySchedule puttabilities;
    std::vector<Date> puttabilityDates = vars.oddYears();
    for (auto& puttabilityDate : puttabilityDates) {
        puttabilities.push_back(ext::make_shared<Callability>(Bond::Price(90.0, Bond::Price::Clean),
                                                              Callability::Put, puttabilityDate));
    }

    Size timeSteps = 240;

    ext::shared_ptr<PricingEngine> engine =
        ext::make_shared<TreeCallableFixedRateBondEngine>(
                                *(vars.model), timeSteps, vars.termStructure);

    CallableFixedRateBond callable(3, 100.0, schedule,
                                   coupons, Thirty360(Thirty360::BondBasis),
                                   vars.rollingConvention,
                                   100.0, vars.issueDate(),
                                   callabilities);
    callable.setPricingEngine(engine);

    CallableFixedRateBond puttable(3, 100.0, schedule,
                                   coupons, Thirty360(Thirty360::BondBasis),
                                   vars.rollingConvention,
                                   100.0, vars.issueDate(),
                                   puttabilities);
    puttable.setPricingEngine(engine);

    if (bond.cleanPrice() <= callable.cleanPrice())
        BOOST_ERROR(
            "inconsistent prices:\n"
            << std::setprecision(8)
            << "    plain bond: " << bond.cleanPrice() << "\n"
            << "    callable:   " << callable.cleanPrice() << "\n"
            << " (should be lower)");

    if (bond.cleanPrice() >= puttable.cleanPrice())
        BOOST_ERROR(
            "inconsistent prices:\n"
            << std::setprecision(8)
            << "    plain bond: " << bond.cleanPrice() << "\n"
            << "    puttable:   " << puttable.cleanPrice() << "\n"
            << " (should be higher)");
}

void CallableBondTest::testObservability() {

    BOOST_TEST_MESSAGE("Testing observability of callable bonds...");

    Globals vars;

    ext::shared_ptr<SimpleQuote> observable(new SimpleQuote(0.03));
    Handle<Quote> h(observable);
    vars.termStructure.linkTo(vars.makeFlatCurve(h));
    vars.model.linkTo(ext::make_shared<HullWhite>(vars.termStructure));

    Schedule schedule =
        MakeSchedule()
        .from(vars.issueDate())
        .to(vars.maturityDate())
        .withCalendar(vars.calendar)
        .withFrequency(Semiannual)
        .withConvention(vars.rollingConvention)
        .withRule(DateGeneration::Backward);

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

    CallabilitySchedule callabilities;

    std::vector<Date> callabilityDates = vars.evenYears();
    for (auto& callabilityDate : callabilityDates) {
        callabilities.push_back(ext::make_shared<Callability>(
            Bond::Price(110.0, Bond::Price::Clean), Callability::Call, callabilityDate));
    }
    std::vector<Date> puttabilityDates = vars.oddYears();
    for (auto& puttabilityDate : puttabilityDates) {
        callabilities.push_back(ext::make_shared<Callability>(Bond::Price(90.0, Bond::Price::Clean),
                                                              Callability::Put, puttabilityDate));
    }

    CallableZeroCouponBond bond(3, 100.0, vars.calendar,
                                vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention, 100.0,
                                vars.issueDate(), callabilities);

    Size timeSteps = 240;

    ext::shared_ptr<PricingEngine> engine =
        ext::make_shared<TreeCallableFixedRateBondEngine>(
                                *(vars.model), timeSteps, vars.termStructure);

    bond.setPricingEngine(engine);

    double originalValue = bond.NPV();

    observable->setValue(0.04);

    if (bond.NPV() == originalValue)
        BOOST_ERROR(
            "callable coupon bond was not notified of observable change");

}

void CallableBondTest::testDegenerate() {

    BOOST_TEST_MESSAGE("Repricing bonds using degenerate callable bonds...");

    Globals vars;

    vars.termStructure.linkTo(vars.makeFlatCurve(0.034));
    vars.model.linkTo(ext::make_shared<HullWhite>(vars.termStructure));

    Schedule schedule =
        MakeSchedule()
        .from(vars.issueDate())
        .to(vars.maturityDate())
        .withCalendar(vars.calendar)
        .withFrequency(Semiannual)
        .withConvention(vars.rollingConvention)
        .withRule(DateGeneration::Backward);

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

    ZeroCouponBond zeroCouponBond(3, vars.calendar, 100.0,
                                  vars.maturityDate(),
                                  vars.rollingConvention);
    FixedRateBond couponBond(3, 100.0, schedule,
                             coupons, Thirty360(Thirty360::BondBasis));

    // no callability
    CallabilitySchedule callabilities;

    CallableZeroCouponBond bond1(3, 100.0, vars.calendar,
                                 vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                 vars.rollingConvention, 100.0,
                                 vars.issueDate(), callabilities);

    CallableFixedRateBond bond2(3, 100.0, schedule,
                                coupons, Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention,
                                100.0, vars.issueDate(),
                                callabilities);

    ext::shared_ptr<PricingEngine> discountingEngine =
        ext::make_shared<DiscountingBondEngine>(vars.termStructure);

    zeroCouponBond.setPricingEngine(discountingEngine);
    couponBond.setPricingEngine(discountingEngine);

    Size timeSteps = 240;

    ext::shared_ptr<PricingEngine> treeEngine =
        ext::make_shared<TreeCallableFixedRateBondEngine>(
                                *(vars.model), timeSteps, vars.termStructure);

    bond1.setPricingEngine(treeEngine);
    bond2.setPricingEngine(treeEngine);

    double tolerance = 1.0e-4;

    if (std::fabs(bond1.cleanPrice() - zeroCouponBond.cleanPrice()) > tolerance)
        BOOST_ERROR(
            "failed to reproduce zero-coupon bond price:\n"
            << std::setprecision(7)
            << "    calculated: " << bond1.cleanPrice() << "\n"
            << "    expected:   " << zeroCouponBond.cleanPrice());

    if (std::fabs(bond2.cleanPrice() - couponBond.cleanPrice()) > tolerance)
        BOOST_ERROR(
            "failed to reproduce fixed-rate bond price:\n"
            << std::setprecision(7)
            << "    calculated: " << bond2.cleanPrice() << "\n"
            << "    expected:   " << couponBond.cleanPrice());

    // out-of-the-money callability

    std::vector<Date> callabilityDates = vars.evenYears();
    for (auto& callabilityDate : callabilityDates) {
        callabilities.push_back(ext::make_shared<Callability>(
            Bond::Price(10000.0, Bond::Price::Clean), Callability::Call, callabilityDate));
    }
    std::vector<Date> puttabilityDates = vars.oddYears();
    for (auto& puttabilityDate : puttabilityDates) {
        callabilities.push_back(ext::make_shared<Callability>(Bond::Price(0.0, Bond::Price::Clean),
                                                              Callability::Put, puttabilityDate));
    }

    bond1 = CallableZeroCouponBond(3, 100.0, vars.calendar,
                                   vars.maturityDate(), Thirty360(Thirty360::BondBasis),
                                   vars.rollingConvention, 100.0,
                                   vars.issueDate(), callabilities);

    bond2 = CallableFixedRateBond(3, 100.0, schedule,
                                  coupons, Thirty360(Thirty360::BondBasis),
                                  vars.rollingConvention,
                                  100.0, vars.issueDate(),
                                  callabilities);

    bond1.setPricingEngine(treeEngine);
    bond2.setPricingEngine(treeEngine);

    if (std::fabs(bond1.cleanPrice() - zeroCouponBond.cleanPrice()) > tolerance)
        BOOST_ERROR(
            "failed to reproduce zero-coupon bond price:\n"
            << std::setprecision(7)
            << "    calculated: " << bond1.cleanPrice() << "\n"
            << "    expected:   " << zeroCouponBond.cleanPrice());

    if (std::fabs(bond2.cleanPrice() - couponBond.cleanPrice()) > tolerance)
        BOOST_ERROR(
            "failed to reproduce fixed-rate bond price:\n"
            << std::setprecision(7)
            << "    calculated: " << bond2.cleanPrice() << "\n"
            << "    expected:   " << couponBond.cleanPrice());
}

void CallableBondTest::testCached() {

    BOOST_TEST_MESSAGE("Testing callable-bond value against cached values...");

    Globals vars;

    vars.today = Date(3,June,2004);
    Settings::instance().evaluationDate() = vars.today;
    vars.settlement = vars.calendar.advance(vars.today,3,Days);

    vars.termStructure.linkTo(vars.makeFlatCurve(0.032));
    vars.model.linkTo(ext::make_shared<HullWhite>(vars.termStructure));

    Schedule schedule =
        MakeSchedule()
        .from(vars.issueDate())
        .to(vars.maturityDate())
        .withCalendar(vars.calendar)
        .withFrequency(Semiannual)
        .withConvention(vars.rollingConvention)
        .withRule(DateGeneration::Backward);

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

    CallabilitySchedule callabilities;
    CallabilitySchedule puttabilities;
    CallabilitySchedule all_exercises;

    std::vector<Date> callabilityDates = vars.evenYears();
    for (auto& callabilityDate : callabilityDates) {
        ext::shared_ptr<Callability> exercise = ext::make_shared<Callability>(
            Bond::Price(110.0, Bond::Price::Clean), Callability::Call, callabilityDate);
        callabilities.push_back(exercise);
        all_exercises.push_back(exercise);
    }
    std::vector<Date> puttabilityDates = vars.oddYears();
    for (auto& puttabilityDate : puttabilityDates) {
        ext::shared_ptr<Callability> exercise = ext::make_shared<Callability>(
            Bond::Price(100.0, Bond::Price::Clean), Callability::Put, puttabilityDate);
        puttabilities.push_back(exercise);
        all_exercises.push_back(exercise);
    }

    Size timeSteps = 240;

    ext::shared_ptr<PricingEngine> engine =
        ext::make_shared<TreeCallableFixedRateBondEngine>(
                                *(vars.model), timeSteps, vars.termStructure);

    double tolerance = 1.0e-8;

    double storedPrice1 = 110.60975477;
    CallableFixedRateBond bond1(3, 100.0, schedule,
                                coupons, Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention,
                                100.0, vars.issueDate(),
                                callabilities);
    bond1.setPricingEngine(engine);

    if (std::fabs(bond1.cleanPrice() - storedPrice1) > tolerance)
        BOOST_ERROR(
            "failed to reproduce cached callable-bond price:\n"
            << std::setprecision(12)
            << "    calculated: " << bond1.cleanPrice() << "\n"
            << "    expected:   " << storedPrice1);

    double storedPrice2 = 115.16559362;
    CallableFixedRateBond bond2(3, 100.0, schedule,
                                coupons, Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention,
                                100.0, vars.issueDate(),
                                puttabilities);
    bond2.setPricingEngine(engine);

    if (std::fabs(bond2.cleanPrice() - storedPrice2) > tolerance)
        BOOST_ERROR(
            "failed to reproduce cached puttable-bond price:\n"
            << std::setprecision(12)
            << "    calculated: " << bond2.cleanPrice() << "\n"
            << "    expected:   " << storedPrice2);

    double storedPrice3 = 110.97509625;
    CallableFixedRateBond bond3(3, 100.0, schedule,
                                coupons, Thirty360(Thirty360::BondBasis),
                                vars.rollingConvention,
                                100.0, vars.issueDate(),
                                all_exercises);
    bond3.setPricingEngine(engine);

    if (std::fabs(bond3.cleanPrice() - storedPrice3) > tolerance)
        BOOST_ERROR(
            "failed to reproduce cached callable/puttable-bond price:\n"
            << std::setprecision(12)
            << "    calculated: " << bond3.cleanPrice() << "\n"
            << "    expected:   " << storedPrice3);

}

test_suite* CallableBondTest::suite() {
    auto* suite = BOOST_TEST_SUITE("Convertible-bond tests");
    suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testConsistency));
    suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testInterplay));
    suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testObservability));
    suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testDegenerate));
    suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testCached));
    return suite;
}


/**
 * \file libs/numeric/ublasx/test/rep.cpp
 *
 * \brief Test suite for the \c rep operation.
 *
 * Copyright (c) 2011, Marco Guazzone
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */

#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublasx/operation/rep.hpp>
#include <cstddef>
#include "libs/numeric/ublasx/test/utils.hpp"

namespace ublas = ::boost::numeric::ublas;
namespace ublasx = ::boost::numeric::ublasx;

static const double tol = 1e-5;

BOOST_UBLASX_TEST_DEF( rep_matrix_col_major )
{
	typedef double value_type;
	typedef ublas::matrix<value_type,ublas::column_major> matrix_type;

	const ::std::size_t nr(2);
	const ::std::size_t nc(2);
	const ::std::size_t out_nr(2*3);
	const ::std::size_t out_nc(2*4);

	matrix_type A(ublas::identity_matrix<value_type>(nr,nc));

	matrix_type E(out_nr, out_nc);
	ublas::subrange(E, 0, 2, 0, 2) = A; ublas::subrange(E, 0, 2, 2, 4) = A; ublas::subrange(E, 0, 2, 4, 6) = A; ublas::subrange(E, 0, 2, 6, 8) = A;
	ublas::subrange(E, 2, 4, 0, 2) = A; ublas::subrange(E, 2, 4, 2, 4) = A; ublas::subrange(E, 2, 4, 4, 6) = A; ublas::subrange(E, 2, 4, 6, 8) = A;
	ublas::subrange(E, 4, 6, 0, 2) = A; ublas::subrange(E, 4, 6, 2, 4) = A; ublas::subrange(E, 4, 6, 4, 6) = A; ublas::subrange(E, 4, 6, 6, 8) = A;

	matrix_type X;

	X = ublasx::rep(A, 3, 4);
	BOOST_UBLASX_DEBUG_TRACE("A=" << A);
	BOOST_UBLASX_DEBUG_TRACE("Expected rep(A, " << out_nr << ", " << out_nc << ")=" << E);
	BOOST_UBLASX_DEBUG_TRACE("X=" << X);
	BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, out_nr, out_nc, tol );
}

BOOST_UBLASX_TEST_DEF( rep_matrix_row_major )
{
	typedef double value_type;
	typedef ublas::matrix<value_type,ublas::row_major> matrix_type;

	const ::std::size_t nr(2);
	const ::std::size_t nc(2);
	const ::std::size_t out_nr(2*3);
	const ::std::size_t out_nc(2*4);

	matrix_type A(ublas::identity_matrix<value_type>(nr,nc));

	matrix_type E(out_nr, out_nc);
	ublas::subrange(E, 0, 2, 0, 2) = A; ublas::subrange(E, 0, 2, 2, 4) = A; ublas::subrange(E, 0, 2, 4, 6) = A; ublas::subrange(E, 0, 2, 6, 8) = A;
	ublas::subrange(E, 2, 4, 0, 2) = A; ublas::subrange(E, 2, 4, 2, 4) = A; ublas::subrange(E, 2, 4, 4, 6) = A; ublas::subrange(E, 2, 4, 6, 8) = A;
	ublas::subrange(E, 4, 6, 0, 2) = A; ublas::subrange(E, 4, 6, 2, 4) = A; ublas::subrange(E, 4, 6, 4, 6) = A; ublas::subrange(E, 4, 6, 6, 8) = A;

	matrix_type X;

	X = ublasx::rep(A, 3, 4);
	BOOST_UBLASX_DEBUG_TRACE("A=" << A);
	BOOST_UBLASX_DEBUG_TRACE("Expected rep(A, " << out_nr << ", " << out_nc << ")=" << E);
	BOOST_UBLASX_DEBUG_TRACE("X=" << X);
	BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, out_nr, out_nc, tol );
}

BOOST_UBLASX_TEST_DEF( rep_vector )
{
	typedef double value_type;
	typedef ublas::vector<value_type> vector_type;
	typedef ublas::matrix<value_type> matrix_type;

	const ::std::size_t n(3);
	const ::std::size_t out_nr(3*3);
	const ::std::size_t out_nc(1*4);

	vector_type v(n);
	v(0) = 1;
	v(1) = 2;
	v(2) = 3;

	matrix_type E(out_nr, out_nc);
	E(0,0) = 1; E(0,1) = 1; E(0,2) = 1; E(0,3) = 1;
	E(1,0) = 2; E(1,1) = 2; E(1,2) = 2; E(1,3) = 2;
	E(2,0) = 3; E(2,1) = 3; E(2,2) = 3; E(2,3) = 3;
	E(3,0) = 1; E(3,1) = 1; E(3,2) = 1; E(3,3) = 1;
	E(4,0) = 2; E(4,1) = 2; E(4,2) = 2; E(4,3) = 2;
	E(5,0) = 3; E(5,1) = 3; E(5,2) = 3; E(5,3) = 3;
	E(6,0) = 1; E(6,1) = 1; E(6,2) = 1; E(6,3) = 1;
	E(7,0) = 2; E(7,1) = 2; E(7,2) = 2; E(7,3) = 2;
	E(8,0) = 3; E(8,1) = 3; E(8,2) = 3; E(8,3) = 3;

	matrix_type X;

	X = ublasx::rep(v, out_nr, out_nc);
	BOOST_UBLASX_DEBUG_TRACE("v=" << v);
	BOOST_UBLASX_DEBUG_TRACE("Expected rep(v, " << out_nr << ", " << out_nc << ")=" << E);
	BOOST_UBLASX_DEBUG_TRACE("X=" << X);
	BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, out_nr, out_nc, tol );
}

int main()
{
	BOOST_UBLASX_TEST_BEGIN();

	BOOST_UBLASX_TEST_DO( rep_matrix_col_major );
	BOOST_UBLASX_TEST_DO( rep_matrix_row_major );
	BOOST_UBLASX_TEST_DO( rep_vector );

	BOOST_UBLASX_TEST_END();
}


#ifndef SETTINGS_HPP
#define SETTINGS_HPP

#include <boost/lexical_cast.hpp>
#include <clstatphys/ensemble/harmonic_chain_periodic_boundary.hpp>
#include <clstatphys/physics/harmonic_oscillator.hpp>
#include <common/settings_common.hpp>

using Ensembler = ensemble::HarmonicChainPeriodicBoundary;
using Hamiltonian = hamiltonian::HarmonicOscillator;

struct Settings : public SettingsCommon{
  double J = 1.0; //interaction constant;
  double heating_temperture = 2.0;
  int num_heating_particles = 1;

  Settings(int argc, char **argv, int & input_counter)
    : SettingsCommon(argc, argv, input_counter)
  {
    set(argc, argv, input_counter);
  }
  Settings() = default;

  inline void set(int argc, char **argv, int & input_counter){
    if (argc > input_counter) J =     boost::lexical_cast<double>(argv[input_counter]);++input_counter;
  }

  template <class Dataput>
  inline void declare(Dataput & dataput){
    SettingsCommon::declare(dataput);

    dataput <<  "<<System Depend Settings>> " << std::endl
            <<  "  " << Hamiltonian::name() << std::endl
            <<  "  " << Ensembler::name() << std::endl
            <<  "  " << Lattice::name() << std::endl
            << "  Coupling constant : J = " << J << std::endl;
  }

  Hamiltonian hamiltonian(){
    Lattice lattice_t(Ns);

    std::vector<std::vector<int> > pair_table(
                                              num_particles,
                                              std::vector<int>(N_adj)
                                              );
    lattice_t.create_table(pair_table);

    Hamiltonian hamiltonian_t(
                              num_particles,
                              J,
                              pair_table,
                              N_adj
                              );
    return hamiltonian_t;
  }

  Ensembler ensembler(){
    Ensembler ensembler_t(
                          J,
                          temperture,
                          num_particles
                          );
    return ensembler_t;
  }

};

#endif


/*ckwg +29
 * Copyright 2014 by Kitware, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither name of Kitware, Inc. nor the names of any contributors may be used
 *    to endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
* \file
* \brief Header defining VXL algorithm implementation of camera optimization.
*/

#include "optimize_cameras.h"

#include <map>
#include <vector>
#include <utility>

#include <boost/foreach.hpp>

#include <maptk/core/exceptions.h>
#include <maptk/vxl/camera.h>
#include <maptk/vxl/camera_map.h>

#include <vcl_vector.h>
#include <vgl/vgl_homg_point_3d.h>
#include <vgl/vgl_point_2d.h>
#include <vnl/algo/vnl_levenberg_marquardt.h>
#include <vnl/vnl_double_3.h>
#include <vpgl/algo/vpgl_optimize_camera.h>

namespace maptk
{

namespace vxl
{

namespace // anonymous
{

/// Reproduction of vpgl_optimize_camera::opt_orient_pos(...), but without
/// trace statement.
vpgl_perspective_camera<double>
maptk_opt_orient_pos(vpgl_perspective_camera<double> const& camera,
                     vcl_vector<vgl_homg_point_3d<double> > const& world_points,
                     vcl_vector<vgl_point_2d<double> > const& image_points)
{
  const vpgl_calibration_matrix<double>& K = camera.get_calibration();
  vgl_point_3d<double> c = camera.get_camera_center();
  const vgl_rotation_3d<double>& R = camera.get_rotation();

  // compute the Rodrigues vector from the rotation
  vnl_double_3 w = R.as_rodrigues();

  vpgl_orientation_position_lsqr lsqr_func(K,world_points,image_points);
  vnl_levenberg_marquardt lm(lsqr_func);
  vnl_vector<double> params(6);
  params[0]=w[0];  params[1]=w[1];  params[2]=w[2];
  params[3]=c.x();  params[4]=c.y();  params[5]=c.z();
  lm.minimize(params);
  vnl_double_3 w_min(params[0],params[1],params[2]);
  vgl_homg_point_3d<double> c_min(params[3], params[4], params[5]);

  return vpgl_perspective_camera<double>(K, c_min, vgl_rotation_3d<double>(w_min) );
}

}

void
optimize_cameras
::optimize(camera_map_sptr & cameras,
           track_set_sptr tracks,
           landmark_map_sptr landmarks) const
{
  if (!cameras || !tracks || !landmarks)
  {
    throw invalid_value("One or more input data pieces are Null!");
  }
  typedef maptk::camera_map::map_camera_t map_camera_t;
  typedef maptk::vxl::camera_map::map_vcam_t map_vcam_t;
  typedef maptk::landmark_map::map_landmark_t map_landmark_t;

  // extract data from containers
  map_vcam_t vcams = camera_map_to_vpgl(*cameras);
  map_landmark_t lms = landmarks->landmarks();
  std::vector<track_sptr> trks = tracks->tracks();

  // Compose a map of frame IDs to a nested map of track ID to the state on
  // that frame number. Using a pointer with the track state to ease the burden
  // on memory.
  typedef std::map< track_id_t, track::track_state const* > inner_map_t;
  typedef std::map< frame_id_t, inner_map_t > states_map_t;

  states_map_t states_map;
  // O( len(trks) * avg_t_len )
  BOOST_FOREACH(track_sptr const& t, trks)
  {
    // Only record a state if there is a corresponding landmark for the
    // track (constant-time check), the track state has a feature and thus a
    // location (constant-time check), and if we have a camera on the track
    // state's frame (constant-time check).
    if (lms.count(t->id()))
    {
      for (track::history_const_itr tsi = t->begin();
           tsi != t->end();
           ++tsi)
      {
        if (tsi->feat && vcams.count(tsi->frame_id))
        {
          states_map[tsi->frame_id][t->id()] = &(*tsi);
        }
      }
    }
  }

  // For each camera in the input map, create corresponding point sets for 2D
  // and 3D coordinates of tracks and matching landmarks, respectively, for
  // that camera's frame.
  map_camera_t optimized_cameras;
  vcl_vector< vgl_point_2d<double> > pts_2d;
  vcl_vector< vgl_homg_point_3d<double> > pts_3d;
  vector_2d tmp_2d;
  vector_3d tmp_3d;
  BOOST_FOREACH(map_vcam_t::value_type const& p, vcams)
  {
    pts_2d.clear();
    pts_3d.clear();

    // Construct 2d<->3d correspondences
    BOOST_FOREACH(inner_map_t::value_type const& q, states_map[p.first])
    {
      // Already guaranteed that feat and landmark exists above.
      tmp_2d = q.second->feat->loc();
      tmp_3d = lms[q.first]->loc();
      pts_2d.push_back(vgl_point_2d<double>(tmp_2d.x(), tmp_2d.y()));
      pts_3d.push_back(vgl_homg_point_3d<double>(tmp_3d.x(), tmp_3d.y(), tmp_3d.z()));
    }

    //optimized_cameras[p.first] =
    //  vpgl_camera_to_maptk(vpgl_optimize_camera::opt_orient_pos(p.second,
    //                                                            pts_3d,
    //                                                            pts_2d));
    optimized_cameras[p.first] =
      vpgl_camera_to_maptk(maptk_opt_orient_pos(p.second, pts_3d, pts_2d));
  }

  cameras = camera_map_sptr(new simple_camera_map(optimized_cameras));
}

}

}


#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#define _USE_MATH_DEFINES
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include "gate_factory.hpp"
#include "gate.hpp"
#include "gate_named_one.hpp"
#include "gate_named_two.hpp"
#include "gate_named_pauli.hpp"
#include "gate_matrix.hpp"
#include "gate_matrix_sparse.hpp"
#include "gate_reversible.hpp"
#include "gate_reflect.hpp"
#include "type.hpp"
#include <Eigen/QR>

namespace gate{
    ComplexMatrix get_IBMQ_matrix(double theta, double phi, double lambda);

	QuantumGateBase* Identity(UINT qubit_index){
        return new ClsIGate(qubit_index);
    }
    QuantumGateBase* X(UINT qubit_index){
        return new ClsXGate(qubit_index);
    }
    QuantumGateBase* Y(UINT qubit_index){
        return new ClsYGate(qubit_index);
    }
    QuantumGateBase* Z(UINT qubit_index){
        return new ClsZGate(qubit_index);
    }
    QuantumGateBase* H(UINT qubit_index) {
        return new ClsHGate(qubit_index);
    }
    QuantumGateBase* S(UINT qubit_index) {
        return new ClsSGate(qubit_index);
    }
    QuantumGateBase* Sdag(UINT qubit_index) {
        return new ClsSdagGate(qubit_index);
    }
    QuantumGateBase* T(UINT qubit_index) {
        return new ClsTGate(qubit_index);
    }
    QuantumGateBase* Tdag(UINT qubit_index) {
        return new ClsTdagGate(qubit_index);
    }
    QuantumGateBase* sqrtX(UINT qubit_index) {
        return new ClsSqrtXGate(qubit_index);
    }
    QuantumGateBase* sqrtXdag(UINT qubit_index) {
        return new ClsSqrtXdagGate(qubit_index);
    }
    QuantumGateBase* sqrtY(UINT qubit_index) {
        return new ClsSqrtYGate(qubit_index);
    }
    QuantumGateBase* sqrtYdag(UINT qubit_index) {
        return new ClsSqrtYdagGate(qubit_index);
    }
    QuantumGateBase* P0(UINT qubit_index) {
        return new ClsP0Gate(qubit_index);
    }
    QuantumGateBase* P1(UINT qubit_index) {
        return new ClsP1Gate(qubit_index);
    }
    QuantumGateBase* RX(UINT qubit_index,double angle){
        return new ClsRXGate(qubit_index,angle);
    }
    QuantumGateBase* RY(UINT qubit_index, double angle) {
        return new ClsRYGate(qubit_index, angle);
    }
    QuantumGateBase* RZ(UINT qubit_index, double angle) {
        return new ClsRZGate(qubit_index, angle);
    }

    ComplexMatrix get_IBMQ_matrix(double theta, double phi, double lambda) {
        CPPCTYPE im(0, 1);
        CPPCTYPE exp_val1 = exp(im * phi);
        CPPCTYPE exp_val2 = exp(im * lambda);
        CPPCTYPE cos_val = cos(theta / 2);
        CPPCTYPE sin_val = sin(theta / 2);

        ComplexMatrix matrix(2, 2);
        matrix(0,0) = cos_val;
        matrix(0,1) = -exp_val2 * sin_val;
        matrix(1,0) = exp_val1 * sin_val;
        matrix(1,1) = exp_val1 * exp_val2 * cos_val;
        return matrix;
    }
    QuantumGateBase* U1(UINT qubit_index, double lambda) {
        ComplexMatrix matrix = get_IBMQ_matrix(0, 0, lambda);
        std::vector<UINT> vec;
        vec.push_back(qubit_index);
        return new QuantumGateMatrix(vec, matrix);
    }
    QuantumGateBase* U2(UINT qubit_index, double phi, double lambda) {
        ComplexMatrix matrix = get_IBMQ_matrix(M_PI / 2, phi, lambda);
        std::vector<UINT> vec;
        vec.push_back(qubit_index);
        return new QuantumGateMatrix(vec, matrix);
    }
    QuantumGateBase* U3(UINT qubit_index, double theta, double phi, double lambda) {
        ComplexMatrix matrix = get_IBMQ_matrix(theta, phi, lambda);
        std::vector<UINT> vec;
        vec.push_back(qubit_index);
        return new QuantumGateMatrix(vec, matrix);
    }

    QuantumGateBase* CNOT(UINT control_qubit_index, UINT target_qubit_index) {
		if (control_qubit_index == target_qubit_index) {
			std::cerr << "Error: gate::CNOT(UINT, UINT): control_qubit_index and target_qubit_index has the same value." << std::endl;
			return NULL;
		}
		return new ClsCNOTGate(control_qubit_index, target_qubit_index);
    }
    QuantumGateBase* CZ(UINT control_qubit_index, UINT target_qubit_index) {
		if (control_qubit_index == target_qubit_index) {
			std::cerr << "Error: gate::CZ(UINT, UINT): control_qubit_index and target_qubit_index has the same value." << std::endl;
			return NULL;
		}
		return new ClsCZGate(control_qubit_index, target_qubit_index);
    }
    QuantumGateBase* SWAP(UINT qubit_index1, UINT qubit_index2) {
		if (qubit_index1 == qubit_index2) {
			std::cerr << "Error: gate::SWAP(UINT, UINT): two indices have the same value." << std::endl;
			return NULL;
		}
		return new ClsSWAPGate(qubit_index1, qubit_index2);
    }

    QuantumGateBase* Pauli(std::vector<UINT> target, std::vector<UINT> pauli_id) {
		if (!check_is_unique_index_list(target)) {
			std::cerr << "Error: gate::Pauli(std::vector<UINT> target, std::vector<UINT>pauli_id): target list contains duplicated values." << std::endl;
			return NULL;
		}
		auto pauli = new PauliOperator(target, pauli_id);
        return new ClsPauliGate(pauli);
    }
    QuantumGateBase* PauliRotation(std::vector<UINT> target, std::vector<UINT> pauli_id, double angle) {
		if (!check_is_unique_index_list(target)) {
			std::cerr << "Error: gate::PauliRotation(std::vector<UINT> target, std::vector<UINT>pauli_id, double angle): target list contains duplicated values." << std::endl;
			return NULL;
		}
		auto pauli = new PauliOperator(target, pauli_id,angle);
        return new ClsPauliRotationGate(angle,pauli);
    }

    QuantumGateMatrix* DenseMatrix(UINT target_index, ComplexMatrix matrix) {
        std::vector<UINT> target_list(1, target_index);
        return new QuantumGateMatrix(target_list, matrix);
    }
    QuantumGateMatrix* DenseMatrix(std::vector<UINT> target_list, ComplexMatrix matrix) {
		if (!check_is_unique_index_list(target_list)) {
			std::cerr << "Error: gate::DenseMatrix(std::vector<UINT> target_list, ComplexMatrix matrix): target list contains duplicated values." << std::endl;
			return NULL;
		}
		return new QuantumGateMatrix(target_list, matrix);
    }

	QuantumGateBase* SparseMatrix(std::vector<UINT> target_list, SparseComplexMatrix matrix) {
		if (!check_is_unique_index_list(target_list)) {
			std::cerr << "Error: gate::SparseMatrix(std::vector<UINT> target_list, SparseComplexMatrix matrix): target list contains duplicated values." << std::endl;
			return NULL;
		}
		return new QuantumGateSparseMatrix(target_list, matrix);
	}

	QuantumGateMatrix* RandomUnitary(std::vector<UINT> target_list) {
		if (!check_is_unique_index_list(target_list)) {
			std::cerr << "Error: gate::RandomUnitary(std::vector<UINT> target_list): target list contains duplicated values." << std::endl;
			return NULL;
		}
		Random random;
		UINT qubit_count = (UINT)target_list.size();
		ITYPE dim = 1ULL << qubit_count;
		ComplexMatrix matrix(dim, dim);
		for (ITYPE i = 0; i < dim; ++i) {
			for (ITYPE j = 0; j < dim; ++j) {
				matrix(i, j) = (random.normal() + 1.i * random.normal())/sqrt(2.);
			}
		}
		Eigen::HouseholderQR<ComplexMatrix> qr_solver(matrix);
		ComplexMatrix Q = qr_solver.householderQ();
		// actual R matrix is upper-right triangle of matrixQR
		auto R = qr_solver.matrixQR();
		for (ITYPE i = 0; i < dim; ++i) {
			CPPCTYPE phase = R(i, i) / abs(R(i, i));
			for (ITYPE j = 0; j < dim; ++j) {
				Q(j,i) *= phase;
			}
		}
		return new QuantumGateMatrix(target_list, Q);
	}
	QuantumGateBase* ReversibleBoolean(std::vector<UINT> target_qubit_index_list, std::function<ITYPE(ITYPE,ITYPE)> function_ptr) {
		if (!check_is_unique_index_list(target_qubit_index_list)) {
			std::cerr << "Error: gate::ReversibleBoolean(std::vector<UINT> target_qubit_index_list, std::function<ITYPE(ITYPE,ITYPE)> function_ptr): target list contains duplicated values." << std::endl;
			return NULL;
		}
		return new ClsReversibleBooleanGate(target_qubit_index_list, function_ptr);
	}
	QuantumGateBase* StateReflection(const QuantumStateBase* reflection_state) {
		return new ClsStateReflectionGate(reflection_state);
	}

    QuantumGateBase* BitFlipNoise(UINT target_index, double prob) {
		auto gate0 = X(target_index);
		auto gate1 = Identity(target_index);
        auto new_gate = new QuantumGate_Probabilistic({ prob, 1-prob }, { gate0, gate1 });
		delete gate0;
		delete gate1;
		return new_gate;
    }
    QuantumGateBase* DephasingNoise(UINT target_index, double prob) {
		auto gate0 = Z(target_index);
		auto gate1 = Identity(target_index);
		auto new_gate = new QuantumGate_Probabilistic({ prob, 1-prob }, { gate0, gate1 });
		delete gate0;
		delete gate1;
		return new_gate;
    }
    QuantumGateBase* IndependentXZNoise(UINT target_index, double prob) {
		auto gate0 = X(target_index);
		auto gate1 = Z(target_index);
		auto gate2 = Y(target_index);
		auto gate3 = Identity(target_index);
		double p1 = prob * (1 - prob);
		double p2 = prob * prob;
        auto new_gate = new QuantumGate_Probabilistic({ p1, p1, p2, 1-2*p1-p2}, { gate0, gate1, gate2, gate3});
		delete gate0;
		delete gate1;
		delete gate2;
		return new_gate;
	}
    QuantumGateBase* DepolarizingNoise(UINT target_index, double prob) {
		auto gate0 = X(target_index);
		auto gate1 = Z(target_index);
		auto gate2 = Y(target_index);
		auto gate3 = Identity(target_index);
		auto new_gate = new QuantumGate_Probabilistic({ prob / 3,prob / 3,prob / 3, 1-prob }, { gate0, gate1, gate2, gate3 });
		delete gate0;
		delete gate1;
		delete gate2;
		delete gate3;
		return new_gate;
	}
	QuantumGateBase* TwoQubitDepolarizingNoise(UINT target_index1, UINT target_index2, double prob) {
		if (target_index1 == target_index2) {
			std::cerr << "Error: gate::TwoQubitDepolarizingNoise(UINT, UINT, double): target list contains duplicated values." << std::endl;
			return NULL;
		}
		std::vector<QuantumGateBase*> gate_list;
		for (int i = 0; i < 16; ++i) {
			if (i != 0) {
				UINT pauli_qubit1 = i % 4;
				UINT pauli_qubit2 = i / 4;
				auto gate = Pauli({ target_index1, target_index2 }, { pauli_qubit1, pauli_qubit2 });
				gate_list.push_back(gate);
			}
			else {
				gate_list.push_back(Identity(target_index1));
			}
		}
		std::vector<double> probabilities(16, prob/15);
		probabilities[0] = 1 - prob;
		auto new_gate = new QuantumGate_Probabilistic(probabilities, gate_list);
		for (UINT gate_index = 0; gate_index < 15; ++gate_index) {
			delete gate_list[gate_index];
		}
		return new_gate;
	}
	QuantumGateBase* AmplitudeDampingNoise(UINT target_index, double prob) {
		ComplexMatrix damping_matrix_0(2, 2), damping_matrix_1(2,2);
		damping_matrix_0 << 1, 0, 0, sqrt(1 - prob);
		damping_matrix_1 << 0, sqrt(prob), 0, 0;
		auto gate0 = DenseMatrix({ target_index }, damping_matrix_0);
		auto gate1 = DenseMatrix({ target_index }, damping_matrix_1);
		auto new_gate = new QuantumGate_CPTP({ gate0, gate1 });
		delete gate0;
		delete gate1;
		return new_gate;
	}
	QuantumGateBase* Measurement(UINT target_index, UINT classical_register_address) {
		auto gate0 = P0(target_index);
		auto gate1 = P1(target_index);
        auto new_gate = new QuantumGate_Instrument({ gate0, gate1},classical_register_address);
		delete gate0;
		delete gate1;
		return new_gate;
    }

    QuantumGateBase* create_quantum_gate_from_string(std::string gate_string){
        const char* gateString = gate_string.c_str();
        char* sbuf;
        //ITYPE elementCount;
        std::vector<CPPCTYPE> element;
        const char delim[] = " ";
        std::vector<UINT> targets;
        QuantumGateBase* gate = NULL;
        char* buf = (char*)calloc(strlen(gateString)+1, sizeof(char));
        strcpy(buf, gateString);
        sbuf = strtok(buf , delim);

        if(strcasecmp(sbuf,"I")==0)         gate = gate::Identity( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"X")==0)    gate = gate::X( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"Y")==0)    gate = gate::Y( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"Z")==0)    gate = gate::Z( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"H")==0)    gate = gate::H( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"S")==0)    gate = gate::S( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"Sdag")==0) gate = gate::Sdag( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"T")==0)    gate = gate::T( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"Tdag")==0) gate = gate::Tdag( atoi( strtok(NULL , delim) ) );
        else if(strcasecmp(sbuf,"CNOT")==0 || strcasecmp(sbuf,"CX")==0) {
            unsigned int control = atoi( strtok(NULL , delim) );
            unsigned int target = atoi( strtok(NULL , delim) );
            gate = gate::CNOT( control, target );
        }
        else if(strcasecmp(sbuf,"CZ")==0)   {
            unsigned int control = atoi( strtok(NULL , delim) );
            unsigned int target = atoi( strtok(NULL , delim) );
            gate = gate::CZ( control, target );
        }
        else if(strcasecmp(sbuf,"SWAP")==0) {
            unsigned int target1 = atoi( strtok(NULL , delim) );
            unsigned int target2 = atoi( strtok(NULL , delim) );
            gate = gate::SWAP( target1, target2 );
        }
        else if(strcasecmp(sbuf,"U1")==0)   {
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta1 = atof( strtok(NULL , delim) );
            gate = gate::U1( target, theta1 );
        }
        else if(strcasecmp(sbuf,"U2")==0)   {
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta1 = atof( strtok(NULL , delim) );
            double theta2 = atof( strtok(NULL , delim) );
            gate = gate::U2( target, theta1, theta2 );
        }
        else if(strcasecmp(sbuf,"U3")==0)   {
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta1 = atof( strtok(NULL , delim) );
            double theta2 = atof( strtok(NULL , delim) );
            double theta3 = atof( strtok(NULL , delim) );
            gate = gate::U3( target, theta1, theta2, theta3 );
        }
        else if(strcasecmp(sbuf,"RX")==0){
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta = atof( strtok(NULL , delim) );
            gate = gate::RX( target, theta );
        }
        else if(strcasecmp(sbuf,"RY")==0){
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta = atof( strtok(NULL , delim) );
            gate = gate::RY( target, theta );
        }
        else if(strcasecmp(sbuf,"RZ")==0){
            unsigned int target = atoi( strtok(NULL , delim) );
            double theta = atof( strtok(NULL , delim) );
            gate = gate::RZ( target, theta );
        }
        else if(strcasecmp(sbuf,"RM")==0){
            char* pauliStr = strtok(NULL,delim);
            unsigned int targetCount = (UINT)strlen(pauliStr);

            std::vector<UINT> pauli(targetCount, 0);
            for(unsigned int i=0;i<targetCount;i++){
                if(pauliStr[i] == 'x' || pauliStr[i] == 'X') pauli[i] = 1;
                else if(pauliStr[i] == 'y' || pauliStr[i] == 'Y') pauli[i] = 2;
                else if(pauliStr[i] == 'z' || pauliStr[i] == 'Z') pauli[i] = 3;
            }

            targets = std::vector<UINT>(targetCount,0);
            for(unsigned int i=0;i<targetCount;i++){
                targets[i] = atoi( strtok(NULL , delim) );
            }

            double theta = atof(strtok(NULL, delim));
            gate = gate::PauliRotation(targets, pauli, theta);
        }
        else if(strcasecmp(sbuf,"U")==0)  {
            unsigned int targetCount = atoi(strtok(NULL,delim));

            targets = std::vector<UINT>(targetCount,0);
            for(unsigned int i=0;i<targetCount;i++){
                targets[i] = atoi( strtok(NULL , delim) );
            }
            ITYPE dim = 1ULL << targetCount;
            ComplexMatrix matrix(dim, dim);

            for(ITYPE i=0;i<dim*dim;i++){
                char* token;
                token = strtok(NULL, delim);
                matrix(i/dim, i%dim) = atof( token );
                token = strtok(NULL, delim);
                matrix(i/dim, i%dim) += CPPCTYPE(0,1) * atof( token );
            }
            gate = gate::DenseMatrix(targets, matrix);
        }
        free(buf);
        return gate;
    }

}


#include <boost/compute/random/default_random_engine.hpp>