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

/*!
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
 Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl
 Copyright (C) 2004 Ferdinando Ametrano

 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.
*/

/*  This example shows how to set up a Term Structure and then price a simple
    swap.
*/

#include <ql/qldefines.hpp>
#ifdef BOOST_MSVC
#  include <ql/auto_link.hpp>
#endif
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/ratehelpers.hpp>
#include <ql/pricingengines/swap/discountingswapengine.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/time/imm.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/time/daycounters/actualactual.hpp>

#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif

int main(int, char* []) {

    try {

        boost::timer timer;
        std::cout << std::endl;

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

        Calendar calendar = TARGET();
        Date settlementDate(22, September, 2004);
        // must be a business day
        settlementDate = calendar.adjust(settlementDate);

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

        todaysDate = Settings::instance().evaluationDate();
        std::cout << "Today: " << todaysDate.weekday()
                  << ", " << todaysDate << std::endl;

        std::cout << "Settlement date: " << settlementDate.weekday()
                  << ", " << settlementDate << std::endl;

        // deposits
        Rate d1wQuote=0.0382;
        Rate d1mQuote=0.0372;
        Rate d3mQuote=0.0363;
        Rate d6mQuote=0.0353;
        Rate d9mQuote=0.0348;
        Rate d1yQuote=0.0345;
        // FRAs
        Rate fra3x6Quote=0.037125;
        Rate fra6x9Quote=0.037125;
        Rate fra6x12Quote=0.037125;
        // futures
        Real fut1Quote=96.2875;
        Real fut2Quote=96.7875;
        Real fut3Quote=96.9875;
        Real fut4Quote=96.6875;
        Real fut5Quote=96.4875;
        Real fut6Quote=96.3875;
        Real fut7Quote=96.2875;
        Real fut8Quote=96.0875;
        // swaps
        Rate s2yQuote=0.037125;
        Rate s3yQuote=0.0398;
        Rate s5yQuote=0.0443;
        Rate s10yQuote=0.05165;
        Rate s15yQuote=0.055175;

        /********************
         ***    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
        boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote));
        boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote));
        boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote));
        boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote));
        boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote));
        boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote));
        // FRAs
        boost::shared_ptr<Quote> fra3x6Rate(new SimpleQuote(fra3x6Quote));
        boost::shared_ptr<Quote> fra6x9Rate(new SimpleQuote(fra6x9Quote));
        boost::shared_ptr<Quote> fra6x12Rate(new SimpleQuote(fra6x12Quote));
        // futures
        boost::shared_ptr<Quote> fut1Price(new SimpleQuote(fut1Quote));
        boost::shared_ptr<Quote> fut2Price(new SimpleQuote(fut2Quote));
        boost::shared_ptr<Quote> fut3Price(new SimpleQuote(fut3Quote));
        boost::shared_ptr<Quote> fut4Price(new SimpleQuote(fut4Quote));
        boost::shared_ptr<Quote> fut5Price(new SimpleQuote(fut5Quote));
        boost::shared_ptr<Quote> fut6Price(new SimpleQuote(fut6Quote));
        boost::shared_ptr<Quote> fut7Price(new SimpleQuote(fut7Quote));
        boost::shared_ptr<Quote> fut8Price(new SimpleQuote(fut8Quote));
        // swaps
        boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote));
        boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote));
        boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote));
        boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote));
        boost::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();

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

        // setup FRAs
        boost::shared_ptr<RateHelper> fra3x6(new FraRateHelper(
            Handle<Quote>(fra3x6Rate),
            3, 6, fixingDays, calendar, ModifiedFollowing,
            true, depositDayCounter));
        boost::shared_ptr<RateHelper> fra6x9(new FraRateHelper(
            Handle<Quote>(fra6x9Rate),
            6, 9, fixingDays, calendar, ModifiedFollowing,
            true, depositDayCounter));
        boost::shared_ptr<RateHelper> fra6x12(new FraRateHelper(
            Handle<Quote>(fra6x12Rate),
            6, 12, fixingDays, calendar, ModifiedFollowing,
            true, depositDayCounter));

        // setup futures
        // Rate convexityAdjustment = 0.0;
        Integer futMonths = 3;
        Date imm = IMM::nextDate(settlementDate);
        boost::shared_ptr<RateHelper> fut1(new FuturesRateHelper(
            Handle<Quote>(fut1Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut2(new FuturesRateHelper(
            Handle<Quote>(fut2Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut3(new FuturesRateHelper(
            Handle<Quote>(fut3Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut4(new FuturesRateHelper(
            Handle<Quote>(fut4Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut5(new FuturesRateHelper(
            Handle<Quote>(fut5Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut6(new FuturesRateHelper(
            Handle<Quote>(fut6Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut7(new FuturesRateHelper(
            Handle<Quote>(fut7Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));
        imm = IMM::nextDate(imm+1);
        boost::shared_ptr<RateHelper> fut8(new FuturesRateHelper(
            Handle<Quote>(fut8Price),
            imm,
            futMonths, calendar, ModifiedFollowing,
            true, depositDayCounter));

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

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

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

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

        double tolerance = 1.0e-15;

        // A depo-swap curve
        std::vector<boost::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);
        boost::shared_ptr<YieldTermStructure> depoSwapTermStructure(
            new PiecewiseYieldCurve<Discount,LogLinear>(
                                          settlementDate, depoSwapInstruments,
                                          termStructureDayCounter,
                                          tolerance));

        // A depo-futures-swap curve
        std::vector<boost::shared_ptr<RateHelper> > depoFutSwapInstruments;
        depoFutSwapInstruments.push_back(d1w);
        depoFutSwapInstruments.push_back(d1m);
        depoFutSwapInstruments.push_back(fut1);
        depoFutSwapInstruments.push_back(fut2);
        depoFutSwapInstruments.push_back(fut3);
        depoFutSwapInstruments.push_back(fut4);
        depoFutSwapInstruments.push_back(fut5);
        depoFutSwapInstruments.push_back(fut6);
        depoFutSwapInstruments.push_back(fut7);
        depoFutSwapInstruments.push_back(fut8);
        depoFutSwapInstruments.push_back(s3y);
        depoFutSwapInstruments.push_back(s5y);
        depoFutSwapInstruments.push_back(s10y);
        depoFutSwapInstruments.push_back(s15y);
        boost::shared_ptr<YieldTermStructure> depoFutSwapTermStructure(
            new PiecewiseYieldCurve<Discount,LogLinear>(
                                       settlementDate, depoFutSwapInstruments,
                                       termStructureDayCounter,
                                       tolerance));

        // A depo-FRA-swap curve
        std::vector<boost::shared_ptr<RateHelper> > depoFRASwapInstruments;
        depoFRASwapInstruments.push_back(d1w);
        depoFRASwapInstruments.push_back(d1m);
        depoFRASwapInstruments.push_back(d3m);
        depoFRASwapInstruments.push_back(fra3x6);
        depoFRASwapInstruments.push_back(fra6x9);
        depoFRASwapInstruments.push_back(fra6x12);
        depoFRASwapInstruments.push_back(s2y);
        depoFRASwapInstruments.push_back(s3y);
        depoFRASwapInstruments.push_back(s5y);
        depoFRASwapInstruments.push_back(s10y);
        depoFRASwapInstruments.push_back(s15y);
        boost::shared_ptr<YieldTermStructure> depoFRASwapTermStructure(
            new PiecewiseYieldCurve<Discount,LogLinear>(
                                       settlementDate, depoFRASwapInstruments,
                                       termStructureDayCounter,
                                       tolerance));

        // 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;

        /*********************
        * SWAPS TO BE PRICED *
        **********************/

        // constant nominal 1,000,000 Euro
        Real nominal = 1000000.0;
        // fixed leg
        Frequency fixedLegFrequency = Annual;
        BusinessDayConvention fixedLegConvention = Unadjusted;
        BusinessDayConvention floatingLegConvention = ModifiedFollowing;
        DayCounter fixedLegDayCounter = Thirty360(Thirty360::European);
        Rate fixedRate = 0.04;
        DayCounter floatingLegDayCounter = Actual360();

        // floating leg
        Frequency floatingLegFrequency = Semiannual;
        boost::shared_ptr<IborIndex> euriborIndex(
                                     new Euribor6M(forecastingTermStructure));
        Spread spread = 0.0;

        Integer lenghtInYears = 5;
        VanillaSwap::Type swapType = VanillaSwap::Payer;

        Date maturity = settlementDate + lenghtInYears*Years;
        Schedule fixedSchedule(settlementDate, maturity,
                               Period(fixedLegFrequency),
                               calendar, fixedLegConvention,
                               fixedLegConvention,
                               DateGeneration::Forward, false);
        Schedule floatSchedule(settlementDate, maturity,
                               Period(floatingLegFrequency),
                               calendar, floatingLegConvention,
                               floatingLegConvention,
                               DateGeneration::Forward, false);
        VanillaSwap spot5YearSwap(swapType, nominal,
            fixedSchedule, fixedRate, fixedLegDayCounter,
            floatSchedule, euriborIndex, spread,
            floatingLegDayCounter);

        Date fwdStart = calendar.advance(settlementDate, 1, Years);
        Date fwdMaturity = fwdStart + lenghtInYears*Years;
        Schedule fwdFixedSchedule(fwdStart, fwdMaturity,
                                  Period(fixedLegFrequency),
                                  calendar, fixedLegConvention,
                                  fixedLegConvention,
                                  DateGeneration::Forward, false);
        Schedule fwdFloatSchedule(fwdStart, fwdMaturity,
                                  Period(floatingLegFrequency),
                                  calendar, floatingLegConvention,
                                  floatingLegConvention,
                                  DateGeneration::Forward, false);
        VanillaSwap oneYearForward5YearSwap(swapType, nominal,
            fwdFixedSchedule, fixedRate, fixedLegDayCounter,
            fwdFloatSchedule, euriborIndex, spread,
            floatingLegDayCounter);

        /***************
        * SWAP PRICING *
        ****************/

        // utilities for reporting
        std::vector<std::string> headers(4);
        headers[0] = "term structure";
        headers[1] = "net present value";
        headers[2] = "fair spread";
        headers[3] = "fair fixed rate";
        std::string separator = " | ";
        Size width = headers[0].size() + separator.size()
                   + headers[1].size() + separator.size()
                   + headers[2].size() + separator.size()
                   + headers[3].size() + separator.size() - 1;
        std::string rule(width, '-'), dblrule(width, '=');
        std::string tab(8, ' ');

        // calculations
        std::cout << dblrule << std::endl;
        std::cout <<  "5-year market swap-rate = "
                  << std::setprecision(2) << io::rate(s5yRate->value())
                  << std::endl;
        std::cout << dblrule << std::endl;

        std::cout << tab << "5-years swap paying "
                  << io::rate(fixedRate) << std::endl;
        std::cout << headers[0] << separator
                  << headers[1] << separator
                  << headers[2] << separator
                  << headers[3] << separator << std::endl;
        std::cout << rule << std::endl;

        Real NPV;
        Rate fairRate;
        Spread fairSpread;

        boost::shared_ptr<PricingEngine> swapEngine(
                         new DiscountingSwapEngine(discountingTermStructure));

        spot5YearSwap.setPricingEngine(swapEngine);
        oneYearForward5YearSwap.setPricingEngine(swapEngine);

        // Of course, you're not forced to really use different curves
        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(depoSwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        // let's check that the 5 years swap has been correctly re-priced
        QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8,
                   "5-years swap mispriced by "
                   << io::rate(std::fabs(fairRate-s5yQuote)));

        forecastingTermStructure.linkTo(depoFutSwapTermStructure);
        discountingTermStructure.linkTo(depoFutSwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-fut-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8,
                   "5-years swap mispriced!");

        forecastingTermStructure.linkTo(depoFRASwapTermStructure);
        discountingTermStructure.linkTo(depoFRASwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-FRA-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        QL_REQUIRE(std::fabs(fairRate-s5yQuote)<1e-8,
                   "5-years swap mispriced!");

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

        // now let's price the 1Y forward 5Y swap

        std::cout << tab << "5-years, 1-year forward swap paying "
                  << io::rate(fixedRate) << std::endl;
        std::cout << headers[0] << separator
                  << headers[1] << separator
                  << headers[2] << separator
                  << headers[3] << separator << std::endl;
        std::cout << rule << std::endl;

        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(depoSwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        forecastingTermStructure.linkTo(depoFutSwapTermStructure);
        discountingTermStructure.linkTo(depoFutSwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-fut-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        forecastingTermStructure.linkTo(depoFRASwapTermStructure);
        discountingTermStructure.linkTo(depoFRASwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-FRA-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        // now let's say that the 5-years swap rate goes up to 4.60%.
        // A smarter market element--say, connected to a data source-- would
        // notice the change itself. Since we're using SimpleQuotes,
        // we'll have to change the value manually--which forces us to
        // downcast the handle and use the SimpleQuote
        // interface. In any case, the point here is that a change in the
        // value contained in the Quote triggers a new bootstrapping
        // of the curve and a repricing of the swap.

        boost::shared_ptr<SimpleQuote> fiveYearsRate =
            boost::dynamic_pointer_cast<SimpleQuote>(s5yRate);
        fiveYearsRate->setValue(0.0460);

        std::cout << dblrule << std::endl;
        std::cout <<  "5-year market swap-rate = "
                  << io::rate(s5yRate->value()) << std::endl;
        std::cout << dblrule << std::endl;

        std::cout << tab << "5-years swap paying "
                  << io::rate(fixedRate) << std::endl;
        std::cout << headers[0] << separator
                  << headers[1] << separator
                  << headers[2] << separator
                  << headers[3] << separator << std::endl;
        std::cout << rule << std::endl;

        // now get the updated results
        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(depoSwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8,
                   "5-years swap mispriced!");

        forecastingTermStructure.linkTo(depoFutSwapTermStructure);
        discountingTermStructure.linkTo(depoFutSwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-fut-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8,
                   "5-years swap mispriced!");

        forecastingTermStructure.linkTo(depoFRASwapTermStructure);
        discountingTermStructure.linkTo(depoFRASwapTermStructure);

        NPV = spot5YearSwap.NPV();
        fairSpread = spot5YearSwap.fairSpread();
        fairRate = spot5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-FRA-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        QL_REQUIRE(std::fabs(fairRate-s5yRate->value())<1e-8,
                   "5-years swap mispriced!");

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

        // the 1Y forward 5Y swap changes as well

        std::cout << tab << "5-years, 1-year forward swap paying "
                  << io::rate(fixedRate) << std::endl;
        std::cout << headers[0] << separator
                  << headers[1] << separator
                  << headers[2] << separator
                  << headers[3] << separator << std::endl;
        std::cout << rule << std::endl;

        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(depoSwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        forecastingTermStructure.linkTo(depoFutSwapTermStructure);
        discountingTermStructure.linkTo(depoFutSwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-fut-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        forecastingTermStructure.linkTo(depoFRASwapTermStructure);
        discountingTermStructure.linkTo(depoFRASwapTermStructure);

        NPV = oneYearForward5YearSwap.NPV();
        fairSpread = oneYearForward5YearSwap.fairSpread();
        fairRate = oneYearForward5YearSwap.fairRate();

        std::cout << std::setw(headers[0].size())
                  << "depo-FRA-swap" << separator;
        std::cout << std::setw(headers[1].size())
                  << std::fixed << std::setprecision(2) << NPV << separator;
        std::cout << std::setw(headers[2].size())
                  << io::rate(fairSpread) << separator;
        std::cout << std::setw(headers[3].size())
                  << io::rate(fairRate) << separator;
        std::cout << std::endl;

        double seconds = timer.elapsed();
        Integer hours = int(seconds/3600);
        seconds -= hours * 3600;
        Integer minutes = int(seconds/60);
        seconds -= minutes * 60;
        std::cout << " \nRun completed in ";
        if (hours > 0)
            std::cout << hours << " h ";
        if (hours > 0 || minutes > 0)
            std::cout << minutes << " m ";
        std::cout << std::fixed << std::setprecision(0)
                  << seconds << " s\n" << std::endl;

        return 0;

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


#include "cpca.hpp"

#include <Eigen/Eigenvalues>
#include <Eigen/QR>
#include <Eigen/SVD>
#include <chrono>
#include <cmath>
#include <iostream>
#include <utility>

CPCA::CPCA(Eigen::Index const nComponents, bool const standardize)
    : nComponents_(nComponents), standardize_(standardize) {
  initialize();
}

void CPCA::initialize() { components_.resize(0, 0); }

Eigen::MatrixXf
CPCA::fitTransform(Eigen::MatrixXf const &fg, Eigen::MatrixXf const &bg,
                   bool const autoAlphaSelection, float const alpha,
                   float const eta, float const convergenceRatio,
                   unsigned int maxIter, bool const keepReports) {
  fit(fg, bg, autoAlphaSelection, alpha, eta, convergenceRatio, maxIter,
      keepReports);
  return transform(fg_);
}

void CPCA::fit(Eigen::MatrixXf const &fg, Eigen::MatrixXf const &bg,
               bool const autoAlphaSelection, float const alpha,
               float const eta, float const convergenceRatio,
               unsigned int maxIter, bool const keepReports) {
  if (autoAlphaSelection) {
    fitWithBestAlpha(fg, bg, alpha, eta, convergenceRatio, maxIter,
                     keepReports);
  } else {
    fitWithManualAlpha(fg, bg, alpha);
  }
}

void CPCA::fitWithManualAlpha(Eigen::MatrixXf const &fg,
                              Eigen::MatrixXf const &bg, float const alpha) {
  fg_ = fg;
  bg_ = bg;

  Eigen::Index fgSize = fg_.size();
  Eigen::Index bgSize = bg_.size();

  if (fgSize == 0 && bgSize == 0) {
    std::cerr << "Both target and background matrices are empty." << std::endl;
  } else if (fgSize == 0) {
    // the result will be the same with when alpha is +inf
    fg_ = Eigen::MatrixXf::Zero(1, bg_.cols());
  } else if (bgSize == 0) {
    // the result will be the same with ordinary PCA
    bg_ = Eigen::MatrixXf::Zero(1, fg_.cols());
  }

  Eigen::Index nFeaturesFg = fg_.cols();
  Eigen::Index nFeaturesBg = bg_.cols();

  if (nFeaturesFg != nFeaturesBg) {
    std::cerr << "# of features of foregraound and background must be the same."
              << std::endl;
  }

  fg_ = fg_.rowwise() - fg_.colwise().mean();
  bg_ = bg_.rowwise() - bg_.colwise().mean();

  if (standardize_) {
    Eigen::RowVectorXf fgStd = fg_.array().square().colwise().mean().sqrt();
    Eigen::RowVectorXf bgStd = bg_.array().square().colwise().mean().sqrt();

    fg_ = fg_.array().rowwise() / fgStd.array();
    bg_ = bg_.array().rowwise() / bgStd.array();

    // NaN to 0.0f
    fg_ = fg_.unaryExpr([](float v) { return std::isfinite(v) ? v : 0.0f; });
    bg_ = bg_.unaryExpr([](float v) { return std::isfinite(v) ? v : 0.0f; });
  }

  fgCov_ = (fg_.adjoint() * fg_) /
           std::fmax(float(fg_.rows() - 1), std::numeric_limits<float>::min());
  bgCov_ = (bg_.adjoint() * bg_) /
           std::fmax(float(bg_.rows() - 1), std::numeric_limits<float>::min());

  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> es(fgCov_ - alpha * bgCov_);
  components_ = es.eigenvectors().rightCols(nComponents_).rowwise().reverse();
  eigenvalues_ = es.eigenvalues().real().tail(nComponents_).reverse();
  loadings_ = components_.array().rowwise() * eigenvalues_.array().abs().sqrt();
}

void CPCA::fitWithBestAlpha(Eigen::MatrixXf const &fg,
                            Eigen::MatrixXf const &bg, float const initAlpha,
                            float const eta, float const convergenceRatio,
                            unsigned int const maxIter,
                            bool const keepReports) {
  bestAlpha(fg, bg, initAlpha, eta, convergenceRatio, maxIter, keepReports);
  // updateComponents(bestAlpha_);
  fitWithManualAlpha(fg, bg, bestAlpha_);
}

void CPCA::updateComponents(float const alpha) {
  if (components_.cols() == 0) {
    std::cerr << "Run fit() at least once before updateComponents()"
              << std::endl;
  }
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> es(fgCov_ - alpha * bgCov_);
  components_ = es.eigenvectors().rightCols(nComponents_).rowwise().reverse();

  eigenvalues_ = es.eigenvalues().real().tail(nComponents_).reverse();
  loadings_ = components_.array().rowwise() * eigenvalues_.array().abs().sqrt();
}

Eigen::MatrixXf CPCA::transform(Eigen::MatrixXf const &X) {
  if (components_.cols() == 0) {
    std::cerr << "Run fit() before transform()" << std::endl;
  }
  return X * components_;
}

float CPCA::bestAlpha(Eigen::MatrixXf const &fg, Eigen::MatrixXf const &bg,
                      float const initAlpha, float const eta,
                      float const convergenceRatio, unsigned int const maxIter,
                      bool const keepReports) {
  reports_.clear();
  float alpha = initAlpha;
  fit(fg, bg, alpha);

  // method 1. discard minor eigenvectors to avoid singular
  // Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> esQ(bgCov_);
  // float ratioToKeep = 0.999f;
  // Eigen::RowVectorXf eigenvalues = esQ.eigenvalues().real().reverse();
  // float targetTotalEigenVal = eigenvalues.sum() * ratioToKeep;
  // Eigen::Index nEigenVectorsToKeep = 0;
  // while (targetTotalEigenVal > 0) {
  //     targetTotalEigenVal -= eigenvalues(nEigenVectorsToKeep);
  //     nEigenVectorsToKeep++;
  // }
  // float ratioToKeep = 0.9f;
  // Eigen::Index nEigenVectorsToKeep = Eigen::Index(bg.cols() * ratioToKeep);
  //
  // std::cout << fg.cols() << " " << nEigenVectorsToKeep << std::endl;
  //
  // Eigen::MatrixXf Q =
  //     esQ.eigenvectors().rightCols(nEigenVectorsToKeep).rowwise().reverse();
  // Eigen::MatrixXf fgCovQ = Q.adjoint() * fgCov_ * Q;
  // Eigen::MatrixXf bgCovQ = Q.adjoint() * bgCov_ * Q;
  //
  // Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> esU(fgCovQ - alpha *
  // bgCovQ); Eigen::MatrixXf U =
  //     esU.eigenvectors().rightCols(nComponents_).rowwise().reverse();
  //
  // if (keepReports) {
  //   reports_.push_back(alpha);
  // }
  //
  // for (unsigned int i = 0; i < maxIter; ++i) {
  //   float fgTr = (U.adjoint() * fgCovQ * U).trace();
  //   float bgTr = (U.adjoint() * bgCovQ * U).trace();
  //   bgTr = std::fmax(bgTr, std::numeric_limits<float>::min());
  //   alpha = fgTr / bgTr;
  //
  //   // update U
  //   Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> esU(fgCovQ - alpha *
  //   bgCovQ); U =
  //   esU.eigenvectors().rightCols(nComponents_).rowwise().reverse();
  //
  //   if (keepReports) {
  //     reports_.push_back(alpha);
  //   }
  // }
  // bestAlpha_ = alpha;

  // method 2: add small constant to diag of bgCov_ to avoid singular
  bgCov_ += Eigen::MatrixXf::Identity(bgCov_.rows(), bgCov_.cols()) * eta;

  if (keepReports) {
    reports_.push_back(alpha);
  }

  for (unsigned int i = 0; i < maxIter; ++i) {
    float fgTr = (components_.adjoint() * fgCov_ * components_).trace();
    float bgTr = (components_.adjoint() * bgCov_ * components_).trace();
    bgTr = std::fmax(bgTr, std::numeric_limits<float>::min());

    float prevAlpha = alpha;
    alpha = fgTr / bgTr;
    updateComponents(alpha);
    if (keepReports) {
      reports_.push_back(alpha);
    }

    if (std::abs(prevAlpha - alpha) / alpha < convergenceRatio)
      break;
  }
  bestAlpha_ = alpha;

  return bestAlpha_;
}

std::vector<float> CPCA::logspace(float const start, float const end,
                                  unsigned int const num, float const base) {
  float realStart = std::pow(base, start);
  float realBase = std::pow(
      base, (end - start) /
                std::fmax(float(num - 1), std::numeric_limits<float>::min()));

  std::vector<float> result;
  result.reserve(num);
  std::generate_n(
      std::back_inserter(result), num, [=]() mutable throw()->float {
        float val = realStart;
        realStart *= realBase;
        return val;
      });
  return result;
}

// TODO: implement semi-automatic selection of alpha of the original cpca
// (but not necessary for ccPCA)

std::vector<float> CPCA::findSpectalAlphas(unsigned int const nAlphasToReturn,
                                           unsigned int const nAlphas,
                                           float const maxLogAlpha) {
  std::vector<float> alphas;
  Eigen::MatrixXf affinityMat = createAffinityMatrix(fg_, nAlphas, maxLogAlpha);

  // TODO: implement rest of here

  return alphas;
}

Eigen::MatrixXf CPCA::createAffinityMatrix(Eigen::MatrixXf const &X,
                                           unsigned int const nAlphas,
                                           float const maxLogAlpha) {
  std::vector<float> alphas;
  alphas.reserve(nAlphas + 1);
  alphas.push_back(0.0f);

  auto logspaceAlphas = logspace(-1.0f, maxLogAlpha, nAlphas);
  alphas.insert(alphas.end(), logspaceAlphas.begin(), logspaceAlphas.end());

  auto k = alphas.size();
  Eigen::MatrixXf affinityMat =
      0.5 * Eigen::MatrixXf::Identity(Eigen::Index(k), Eigen::Index(k));

  std::vector<Eigen::MatrixXf> subspaces;
  subspaces.reserve(k);
  for (auto const &alpha : alphas) {
    updateComponents(alpha);
    Eigen::MatrixXf proj = transform(X);
    Eigen::HouseholderQR<Eigen::MatrixXf> qr(proj);
    Eigen::MatrixXf Q = qr.householderQ();
    subspaces.push_back(qr.householderQ());
  }

  for (size_t i = 0; i < k; ++i) {
    for (size_t j = i + 1; j < k; ++j) {
      Eigen::BDCSVD<Eigen::MatrixXf> svd(subspaces[i] * subspaces[j],
                                         Eigen::ComputeThinU |
                                             Eigen::ComputeThinV);
      Eigen::VectorXf s = svd.singularValues();
      affinityMat(Eigen::Index(i), Eigen::Index(j)) = s(0) * s(1);
    }
  }

  affinityMat = affinityMat + affinityMat.transpose();
  // NaN to 0.0f
  affinityMat = affinityMat.unaryExpr(
      [](float v) { return std::isfinite(v) ? v : 0.0f; });

  return affinityMat;
}


//
// SPDX-License-Identifier: MIT
// Copyright (c) 2016-2020 Michael Purcaro, Henry Pratt, Jill Moore, Zhiping Weng
//

#include <vector>
#include <string>
#include <unordered_map>

#include <armadillo>
#include <boost/filesystem.hpp>

#include "utils.hpp"
#include "lambda.hpp"
#include "region.hpp"
#include "rDHS.hpp"
#include "binarysignal.hpp"
#include "correlation.hpp"

namespace SCREEN {

  namespace bfs = boost::filesystem;
  namespace a = arma;

  /**
     compute the pairwise correlation of the regions for the given chromosome
     @returns: armadillo matrix containing pairwise correlation coefficients for the regions
   */
  a::Mat<float> runCorrelation(std::vector<ScoredRegionSet> &regions, const std::string &chr) {

    // create emptry matrix
    if (0 == regions.size()) return a::Mat<float>(0, 0);
    a::Mat<float> values(regions.size(), regions[0].regions_.regions_[chr].size());

    // populate with region scores
    for (auto i = 0; i < regions.size(); ++i) {
#pragma omp parallel for
      for (auto j = 0; j < regions[i].regions_.regions_[chr].size(); ++j) {
	values.at(i, j) = regions[i].regions_.regions_[chr][j].score;
      }
    }

    // return pairwise correlation
    return a::cor(values);

  }

  a::Mat<float> runCorrelation(BinarySignal &b, const std::vector<boost::filesystem::path> &signalfiles,
			       const std::string &chr, RegionSet &regions) {

    // create empty matrix
    if (0 == signalfiles.size()) return a::Mat<float>(0, 0);
    a::Mat<float> values(signalfiles.size(), regions.regions_.regions_[chr].size());

    // populate with region scores
    for (auto i = 0; i < signalfiles.size(); ++i) {
      a::Col<float> v = b.readSignal<RegionSet>(signalfiles[i], chr, regions);
#pragma omp parallel for
      for (auto j = 0; j < regions.regions_.regions_[chr].size(); ++j) {
	values.at(i, j) = v.at(j);
      }
    }

    // return pairwise correlation
    return a::cor(values);

  }

  /**
     write a computed pairwise correlation in JSON 2D array format to an output file
     @param corr: matrix containing the correlation coefficients
     @param output_path: output path to which to write the matrix
   */
  template <typename T>
  void writeCorrelation(const a::Mat<T> &corr, const bfs::path &output_path) {
    std::ofstream f(output_path.string());
    long i, j;
    f << '[';
    for (i = 0; i < corr.n_rows - 1; ++i) {
      for (j = 0; j < corr.n_cols - 1; ++j) {
	f << corr.at(i, j) << ',';
      }
      f << corr.at(i, j) << "],[";
    }
    for (j = 0; j < corr.n_cols - 1; ++j) {
      f << corr.at(i, j) << ',';
    }
    f << corr.at(i, j) << ']';
  }

  template void writeCorrelation<float>(const a::Mat<float> &corr, const bfs::path &output_path);
  template void writeCorrelation<double>(const a::Mat<double> &corr, const bfs::path &output_path);

} // SCREEN


//==================================================================================================
/*!
  @file
  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_ERFCX_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_ERFCX_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!
  @ingroup group-euler
    Function object implementing erfcx capabilities

   Computes the  underflow-compensating (scaled) complementary  error function:
   \f$\displaystyle e^{x^2}\frac{2}{\sqrt\pi}\int_{x}^{\infty} e^{-t^2}\mbox{d}t\f$

    @par Semantic:

    For every parameter of floating type T

    @code
    T r = erfcx(x);
    @endcode

    is similar to:

    @code
    T r = exp(sqr(x))*erfc(x);
    @endcode

    But avoid underflow as much as possible.

    @see erfc, erf

  **/
  Value erfcx(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/erfcx.hpp>
#include <boost/simd/function/simd/erfcx.hpp>

#endif


/** \file functors.hpp */

#pragma once

// std c++ headers
#include <cmath>
#include <complex>
#include <type_traits>

// boost headers
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/math/special_functions/pow.hpp>

// AMDiS headers
#include "operations/functor_generator.hpp"
#include "operations/meta.hpp"
#include "traits/basic.hpp"
#include "traits/scalar_types.hpp"

namespace AMDiS
{
  namespace functors
  {
    /// identity(v) == v
    AMDIS_MAKE_UNARY_FUNCTOR( identity , d0 , v )

    /// constant(v) == val
    template <class T>
    struct constant : FunctorBase
    {
      constant(T val_) : val(val_) {}

      template <class V>
      T operator()(V&&) const
      {
        return val;
      }

    private:
      T val;
    };

    /// ct_constant(v) == val
    template <class T, long val_>
    struct ct_constant : FunctorBase
    {
      static constexpr T val = val_;

      template <class V> static constexpr T eval(V&&)
      {
        return val;
      }
      template <class V> static constexpr T apply(V&&)
      {
        return val;
      }
      template <class V> constexpr T operator()(V&&) const
      {
        return val;
      }
    };

    /// abs(v) == |v|
    template <class T>
    struct abs : FunctorBase
    {
      static constexpr int getDegree(int d0)
      {
        return d0;
      }
      static constexpr auto eval(const T& v) RETURNS( math::abs(v) )
      constexpr auto operator()(const T& v) const RETURNS( eval(v) )
    };

    // specialization of abs for complex values
    template <class T>
    struct abs<std::complex<T>> : FunctorBase
    {
      static constexpr int getDegree(int d0)
      {
        return d0;
      }
      static constexpr auto eval(const T& v) RETURNS( std::norm(v) )
      constexpr auto operator()(const T& v) const RETURNS( eval(v) )
    };

    /// negate(v) == -v
    AMDIS_MAKE_UNARY_FUNCTOR( negate, d0, -v  )

    AMDIS_MAKE_BINARY_FUNCTOR( plus,  math::max(d0, d1), v0 + v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( minus, math::max(d0, d1), v0 - v1  )
//     AMDIS_MAKE_BINARY_FUNCTOR( multiplies, d0 + d1,      v0 * v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( divides,    d0 + d1,      v0 / v1  )

    struct multiplies : FunctorBase
    {
      int getDegree(int d0, int d1) const
      {
        return d0 + d1;
      }

      template <class T0, class T1 = T0>
      static constexpr auto eval(T0&& v0, T1&& v1) RETURNS( std::forward<T0>(v0) * std::forward<T1>(v1) )

      template <class T0, class T1 = T0>
      auto operator() (T0&& v0, T1&& v1) const RETURNS( std::forward<T0>(v0) * std::forward<T1>(v1) )
    };

    // _____ logical functors _________________________________________________

    AMDIS_MAKE_BINARY_FUNCTOR( equal,   0, v0 == v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( unequal, 0, v0 != v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( less,    0, v0 < v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( greater, 0, v0 > v1  )

    AMDIS_MAKE_BINARY_FUNCTOR( logical_and, 0, v0 && v1  )
    AMDIS_MAKE_BINARY_FUNCTOR( logical_or,  0, v0 || v1  )

    AMDIS_MAKE_BINARY_FUNCTOR( max, math::max(d0, d1), math::max(v0, v1)  )
    AMDIS_MAKE_BINARY_FUNCTOR( min, math::max(d0, d1), math::min(v0, v1)  )

    /// max(|a|,|b|)
    AMDIS_MAKE_BINARY_FUNCTOR( abs_max, math::max(d0, d1), math::max(math::abs(v0), math::abs(v1))  )

    /// min(|a|,|b|)
    AMDIS_MAKE_BINARY_FUNCTOR( abs_min, math::max(d0, d1), math::min(math::abs(v0), math::abs(v1))  )

    /// conditional(a,b,c) = a ? b : c
    template <class T1, class T2>
    struct conditional : FunctorBase
    {
      constexpr int getDegree(int d0, int d1, int d2) const
      {
        return math::max(d1, d2);
      }
      static constexpr typename std::common_type<T1, T2>::type
      eval(bool cond, T1 const& v1, T2 const& v2)
      {
        return cond ? v1 : v2;
      }
      constexpr auto operator()(bool cond, T1 const& v1, T2 const& v2) const RETURNS
      (
        eval(cond, v1, v2)
      )
    };

    /// cross(v1, v2) = v1 x v2, TODO: find better name
    template <class T1, class T2>
    struct MyCross : FunctorBase
    {
      using value_type = decltype( std::declval<T1>() * std::declval<T2>() );
      constexpr int getDegree(int /*d*/, int d0, int d1) const
      {
        return d0+d1;
      }

      template <class Vec1, class Vec2>
      static value_type eval(size_t i, const Vec1& v1, const Vec2& v2)
      {
        using size_type = Size_t<traits::category<Vec1>>;
        value_type result;

        TEST_EXIT_DBG( size(v1) == 3 && size(v1) == size(v2), "cross: inkompatible sizes!\n");

        size_type k = (i+1) % 3, l = (i+2) % 3;
        result = v1(k) * v2(l) - v1(l) * v2(k);
        return result;
      }

      template <class Vec1, class Vec2>
      auto operator()(size_t i, const Vec1& v1, const Vec2& v2) const RETURNS
      (
        eval(i, v1, v2)
      )
    };

    /// apply a functor N times
    template <class Functor, int N>
    struct apply
    {
      apply(Functor const& f_) : f(f_), inner(f_) {}

      int getDegree(int d0) const
      {
        return f.getDegree(inner.getDegree(d0));
      }

      template <class V>
      static auto eval(const V& v) RETURNS
      (
        Functor::eval(apply<Functor, N-1>::eval(v))
      )

      template <class V>
      auto operator()(const V& v) const RETURNS( f(inner(v)) )

    private:
      Functor f;
      apply<Functor, N-1> inner;
    };

    template <class Functor>
    struct apply<Functor, 0>
    {
      apply(Functor const& f_) : f(f_) {}
      int getDegree(int d0) const
      {
        return d0;
      }

      template <class V>
      static auto eval(const V& v) RETURNS( v )
      template <class V>
      auto operator()(const V& v) const RETURNS( v )

    private:
      Functor f;
    };

    // -------------------------------------------------------------------------

    template <class F, int arg, class G>
    struct compose;

    template <class F, class G>
    struct compose<F, 1, G>
    {
      template <class T>
      auto operator()(T const& v, T const& v0) RETURNS( f(g(v), v0) )

    private:
      F f;
      G g;
    };

    template <class F, class G>
    struct compose<F, 2, G>
    {
      template <class T>
      auto operator()(T const& v, T const& v0) RETURNS( f(v, g(v0)) )

    private:
      F f;
      G g;
    };

    /// pow<p>(v) == v^p
    template <int p, class T>
    struct pow : FunctorBase
    {
      constexpr int getDegree(int d0) const
      {
        return p*d0;
      }

      static constexpr T eval(const T& v)
      {
        return boost::math::pow<p>(v);
      }
      constexpr T operator()(const T& v) const
      {
        return eval(v);
      }
    };

    /// root<p>(v) == p-th-root(v)
    template <int p, class T, class = void>
    struct root_dispatch;

    template <int p, class T>
    struct root : FunctorBase
    {
      constexpr int getDegree(int d0) const
      {
        return p*d0;    // optimal polynomial approximation degree ?
      }

      static constexpr T eval(const T& v)
      {
        return root_dispatch<p,T>::eval(v);
      }
      constexpr T operator()(const T& v) const
      {
        return eval(v);
      }
    };

    template <int p, class T, class>
    struct root_dispatch
    {
      static constexpr T eval(const T& v)
      {
        return std::pow(v, 1.0/p);
      }
    };

    template <int p, class T>
    struct root_dispatch<p, T,
      Requires_t<meta::is_power_of<p, 3>> >
    {
      static constexpr T eval(const T& v)
      {
        return apply<root<3, T>, meta::log<p, 3>::value>::eval(v);
      }
    };

    template <int p, class T>
    struct root_dispatch<p, T,
      Requires_t<meta::is_power_of<p, 2>> >
    {
      static constexpr T eval(const T& v)
      {
        return apply<root<2, T>, meta::log<p, 2>::value>::eval(v);
      }
    };

    template <class T>
    struct root_dispatch<3, T>
    {
      static constexpr T eval(const T& v)
      {
        return boost::math::cbrt(v);
      }
    };

    template <class T>
    struct root_dispatch<2, T>
    {
      static constexpr T eval(const T& v)
      {
        return std::sqrt(v);
      }
    };

    template <class T>
    struct root_dispatch<1, T>
    {
      static constexpr T eval(const T& v)
      {
        return v;
      }
    };

    template <class T>
    struct root_dispatch<0, T>
    {
      static constexpr T eval(const T& /*v*/)
      {
        return 1.0;
      }
    };

  } // end namespace functors

} // end namespace AMDiS


//
//  EigenEx1DenseMatVec.cpp
//
//
//  Created by Zac Schulwolf on 12/26/16.
//
// Compile by g++ -I "$(brew --prefix eigen)/include/eigen3" EigenEx1DenseMatVec.cpp -o EigenEx1DenseMatVec
//
// From https://eigen.tuxfamily.org/dox/GettingStarted.html
// From https://eigen.tuxfamily.org/dox/group__TutorialMatrixClass.html
// Installing Libraries http://stackoverflow.com/questions/34340578/installing-c-libraries-on-os-x

#include <iostream>
#include <Eigen/Dense>
//using Eigen::MatrixXd; used for 1
using namespace Eigen;
using namespace std;

int main()
{
    //1
    MatrixXd m1(2,2);
    m1(0,0) = 3;
    m1(1,0) = 2.5;
    m1(0,1) = -1;
    m1(1,1) = m1(1,0) + m1(0,1);
    cout << "Here is the matrix m1:\n" << m1 << endl;

    VectorXd v1(2);
    v1(0) = 4;
    v1(1) = v1(0) - 1;
    cout << "Here is the vector v1:\n" << v1 << endl;
    cout << endl << endl;

    //2A
    Matrix3d m2a= Matrix3d::Random();
    m2a= (m2a+ Matrix3d::Constant(1.2)) * 50;
    cout << "m2a=" << endl << m2a<< endl;
    Vector3d v2a(1,2,3);
    cout << "m2a* v2a =" << endl << m2a* v2a << endl;
    cout << endl << endl;

    //2B
    MatrixXd m2b= MatrixXd::Random(3,3);
    m2b= (m2b+ MatrixXd::Constant(3,3,1.2)) * 50;
    cout << "m2b=" << endl << m2b<< endl;
    VectorXd v2b(3);
    v2b << 1, 2, 3;
    cout << "m2b* v2b =" << endl << m2b* v2b << endl;
    cout << endl << endl;

    //3
    //initialization
    Matrix3d m3a; //3 by 3 matrix of uninitialized coefficients
    MatrixXd m3b; //dynamic size matrix, size currently 0 by 0, coefficients not allocated
    MatrixXf m3c(10,15); //10 by 15 dynamic sized matrix, allocated and uninitialized coeffients
    VectorXf v3a(30); //size 30 dynamic sized vector, allocated and uninitialized coeffients
    Vector2d v3b(5.0, 6.0);
    Vector3d v3c(5.0, 6.0, 7.0);
    Vector4d v3d(5.0, 6.0, 7.0, 8.0); //can initialize up to 4 coeffients with this method
    Matrix3f m3d;
    m3d << 1, 2, 3, 4, 5, 6, 7, 8, 9; //Comma-initialization for matrix
    cout << m3d << endl;

    Vector3f v3e; //column vector <float,3,1> Vector
    RowVector3f v3f; //row vector <float,3,2> RowVector
    cout << endl << endl;

    //4
    //rows(), cols(), size(), resize()
    //resize() changes coeffients, use conservativeResize() to keep coeffients
    MatrixXd m4(2,5); //Note that this is Xd so its dynamic sized
    m4.resize(4,3); //Needs to be dynamic, now 4 by 3
    cout << "The matrix m4 is of size " << m4.rows() << "x" << m4.cols() << endl;
    cout << "It has " << m4.size() << " coefficients" << endl;

    VectorXd v4(2);
    v4.resize(5);
    cout << "The vector v4 is of size " << v4.size() << endl;
    cout << "As a matrix, v4 is of size " << v4.rows() << "x" << v4.cols() << endl;
    cout << endl << endl;

    //5
    //Assignment and resizing
    MatrixXf m5a(2,2);
    cout << "m5a is of size " << m5a.rows() << "x" << m5a.cols() << endl;
    MatrixXf m5b(3,3);
    m5a = m5b;
    cout << "m5a is now of size " << m5a.rows() << "x" << m5a.cols() << endl;
    cout << "m5a is now of size " << m5a.rows() << "x" << m5a.cols() << endl;

    //6
    //Formula
    /*
     cout << "MatrixNt; ex: MatrixXi (Matrix<int, Dynamic, Dynamic>)
     VectorNt; ex: Vector2f (Matrix<float, 2, 1>)
     RowVectonNt; ex: Vector3d (Matrix<double, 1, 3>)

     N can be 2,3,4, or x (dynamic)
     t can be i (int), f (float), d (double), cf (complex<float>), or cd(complex<double>)" << endl;
    */

}


#include "Discretization.hpp"
#include "constants.hpp"

#include <boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
#include <boost/numeric/odeint.hpp>

class DiscretizationODE {
private:
    Model::ControlVector u_t, u_t1;
    double sigma, dt;
    Model& model;

public:

    static constexpr size_t n_V_states = 3 + Model::n_states + 2 * Model::n_inputs;
    using state_type = Eigen::Matrix<double, Model::n_states, n_V_states>;

    DiscretizationODE(
        const Model::ControlVector &u_t,
        const Model::ControlVector &u_t1,
        const double &sigma,
        double dt,
        Model& model
    )
    :u_t(u_t)
    ,u_t1(u_t1)
    ,sigma(sigma)
    ,dt(dt)
    ,model(model) {}

    void operator()(const state_type &V, state_type &dVdt, const double t){

        const Model::StateVector &x = V.col(0);
        const Model::ControlVector u = u_t + t / dt * (u_t1 - u_t);

        const double alpha = t / dt;
        const double beta = 1. - alpha;

        const Model::StateMatrix   A_bar  = sigma * model.state_jacobian(x, u);
        const Model::ControlMatrix B_bar  = sigma * model.control_jacobian(x, u);
        const Model::StateVector   f      =         model.ode(x, u);

        Model::StateMatrix Phi_A_xi = V.block<Model::n_states, Model::n_states>(0, 1);
        Model::StateMatrix Phi_A_xi_inverse = Phi_A_xi.inverse();

        size_t cols = 0;

        dVdt.block<Model::n_states,               1>(0, cols) = sigma * f;                                   cols += 1;
        dVdt.block<Model::n_states, Model::n_states>(0, cols) = A_bar * Phi_A_xi;                            cols += Model::n_states;
        dVdt.block<Model::n_states, Model::n_inputs>(0, cols) = Phi_A_xi_inverse * B_bar * alpha;            cols += Model::n_inputs;
        dVdt.block<Model::n_states, Model::n_inputs>(0, cols) = Phi_A_xi_inverse * B_bar * beta;             cols += Model::n_inputs;
        dVdt.block<Model::n_states,               1>(0, cols) = Phi_A_xi_inverse * f;                        cols += 1;
        dVdt.block<Model::n_states,               1>(0, cols) = Phi_A_xi_inverse * (-A_bar * x - B_bar * u);
    }
};

void calculate_discretization (
    Model &model,
    double &sigma,
    Eigen::Matrix<double, Model::n_states, K> &X,
    Eigen::Matrix<double, Model::n_inputs, K> &U,
    array<Model::StateMatrix,   (K-1)> &A_bar,
    array<Model::ControlMatrix, (K-1)> &B_bar,
    array<Model::ControlMatrix, (K-1)> &C_bar,
    array<Model::StateVector,   (K-1)> &Sigma_bar,
    array<Model::StateVector,   (K-1)> &z_bar
) {

    const double dt = 1 / double(K-1);
    using namespace boost::numeric::odeint;
    runge_kutta4<DiscretizationODE::state_type, double, DiscretizationODE::state_type, double, vector_space_algebra> stepper;

    for (size_t k = 0; k < K-1; k++) {
        DiscretizationODE::state_type V;
        V.setZero();
        V.col(0) = X.col(k);
        V.block<Model::n_states,Model::n_states>(0, 1).setIdentity();

        DiscretizationODE discretizationODE(U.col(k), U.col(k+1), sigma, dt, model);
        integrate_n_steps( stepper , discretizationODE , V , 0. , dt/10.0 , 10 );

        size_t cols = 1;
        A_bar[k]      =            V.block<Model::n_states,Model::n_states>(0, cols);   cols += Model::n_states;
        B_bar[k]      = A_bar[k] * V.block<Model::n_states,Model::n_inputs>(0, cols);   cols += Model::n_inputs;
        C_bar[k]      = A_bar[k] * V.block<Model::n_states,Model::n_inputs>(0, cols);   cols += Model::n_inputs;
        Sigma_bar[k]  = A_bar[k] * V.block<Model::n_states,1>(0, cols);                 cols += 1;
        z_bar[k]      = A_bar[k] * V.block<Model::n_states,1>(0, cols);
    }
}


/*
 * spectral_impl.hpp
 *
 *  Created on: Jan 29, 2012
 *      Author: david
 */

#ifndef GRAPHSEG_SPECTRAL_SPECTRALIMPL_HPP_
#define GRAPHSEG_SPECTRAL_SPECTRALIMPL_HPP_

#include "../Common.hpp"
#include "../as_range.hpp"
#include <boost/graph/adjacency_list.hpp>
#include <functional>
#include <iostream>
#include <fstream>
#include <vector>
#ifdef SEGS_DBG_PRINT
#include <boost/format.hpp>
#include <fstream>
#endif

namespace graphseg { namespace detail {

constexpr float c_D_min = 0.001f;

template<typename K>
struct DenseGev
{
	Eigen::Matrix<K,-1,-1> L;
	Eigen::Matrix<K,-1,1> D;
};

template<typename K, typename Graph, typename EdgeWeightMap>
DenseGev<K> dense_graph_to_gev(const Graph& graph, EdgeWeightMap edge_weights)
{
	typedef Eigen::Matrix<K,-1,-1> matrix_t;
	typedef Eigen::Matrix<K,-1,1> vector_t;
	const unsigned int dim = boost::num_vertices(graph);
	// creating matrices
	matrix_t W = matrix_t::Zero(dim,dim);
	vector_t D = vector_t::Zero(dim);

#ifdef SPECTRAL_VERBOSE
	std::cout << "DEBUG: Number of vertices = " << boost::num_vertices(graph) << std::endl;
	std::cout << "DEBUG: Number of edges = " << boost::num_edges(graph) << std::endl;
#endif

	for(auto eid : as_range(boost::edges(graph))) {
		unsigned int ea = boost::source(eid, graph);
		unsigned int eb = boost::target(eid, graph);
		K ew = edge_weights[eid];
		if(std::isnan(ew)) {
			std::cerr << "ERROR: Weight for edge (" << ea << "," << eb << ") is nan!" << std::endl;
			continue;
		}
		if(ew < 0) {
			std::cerr << "ERROR: Weight for edge (" << ea << "," << eb << ") is negative!" << std::endl;
			continue;
		}
		W(ea, eb) = ew;
		W(eb, ea) = ew;
		D[ea] += ew;
		D[eb] += ew;
	}
	// connect disconnected segments to everything
	// FIXME why is this necessary?
#ifdef SPECTRAL_VERBOSE
	std::vector<int> nodes_with_no_connection;
#endif
	for(unsigned int i=0; i<dim; i++) {
		K& di = D[i];
		if(di < c_D_min) {
#ifdef SPECTRAL_VERBOSE
			nodes_with_no_connection.push_back(i);
#endif
			// connect the disconnected cluster to all other clusters with a very small weight
			di = static_cast<K>(1);
			K q = di / static_cast<K>(dim-1);
			for(unsigned int j=0; j<dim; j++) {
				if(j == i) continue;
				W(i,j) = q;
				W(j,i) = q;
			}
		}
	}
#ifdef SPECTRAL_VERBOSE
	if(!nodes_with_no_connection.empty()) {
		std::cout << "DEBUG: Nodes without connections (#=" << nodes_with_no_connection.size() << "): ";
		for(int i : nodes_with_no_connection) {
			std::cout << i << ", ";
		}
		std::cout << std::endl;
	}
#endif
	// compute matrix L = D - W
	matrix_t L = -W;
	for(unsigned int i=0; i<dim; i++) {
		L(i,i) += D[i];
	}
	// ready
	return { L, D };
}

template<typename K>
struct DenseGevTransformed
{
	Eigen::Matrix<K,-1,-1> A;
	Eigen::Matrix<K,-1,1> D_inv_sqrt;
};

template<typename K>
DenseGevTransformed<K> dense_gev_to_ev(const DenseGev<K>& gev)
{
	typedef Eigen::Matrix<K,-1,-1> matrix_t;
	typedef Eigen::Matrix<K,-1,1> vector_t;
	// The general eigenvalue problem
	//     (D - W) x = \lambda D x
	// can be transformed as follows:
	// <=> D^{-1/2} (D - W) x = \lambda D^{1/2} x
	// using z := D^{1/2} x i.e. x = D^{-1/2} z
	// <=> D^{-1/2} (D - W) D^{-1/2} z = \lambda z
	// Thus we have a "normal" eigenvalue problem A z = \lambda z with
	//     A := D^{-1/2} (D - W) D^{-1/2}
	// Using L := D - W this gives for coefficients:
	//		a_ij = l_ij / \sqrt(d_i * d_j)
	// where d_i := D_ii
	const matrix_t& L = gev.L;
	const vector_t& D = gev.D;
	assert(L.cols() == L.rows());
	assert(D.rows() == L.rows());
	const unsigned int N = L.rows();
	vector_t D_inv_sqrt = D.array().sqrt().inverse().matrix();
	matrix_t A(N,N);
	for(unsigned int i=0; i<N; i++) {
		A.col(i) = D_inv_sqrt[i] * L.col(i).cwiseProduct(D_inv_sqrt);
	}
	return { A, D_inv_sqrt };
}

template<typename K>
struct SparseGEVT
{
	SparseMatrix A;
	Eigen::VectorXf D_inv_sqrt;
};

template<typename K, typename Graph, typename EdgeWeightMap>
SparseGEVT<K> sparse_graph_entries(const Graph& graph, EdgeWeightMap edge_weights)
{
	// We want to solve the EV problem: (D - W) x = \lamda D x.
	// Each edge of the graph defines two entries into the symmetric matrix W.
	// The diagonal matrix D is defined via d_i = sum_j{w_ij}.

	// As D is a diagonal matrix the the general problem can be easily transformed
	// into a normal eigenvalue problem by decomposing D = L L^t, which yields L = sqrt(D).
	// Thus the EV problem is: L^{-1} (D - W) L^{-T} y = \lambda y.
	// Eigenvectors can be transformed using x = L^{-T} y.

	// The dimension of the problem
	const int n = boost::num_vertices(graph);

	// Each edge defines two entries (one in the upper and one in the lower).
	// In addition all diagonal entries are non-zero.
	// Thus the number of non-zero entries in the lower triangle is equal to
	// the number of edges plus the number of nodes.
	// This is not entirely true as some connections are possibly rejected.
	// Additionally some connections may be added to assure global connectivity.
	const int nnz_guess = boost::num_edges(graph) + n;

	// collect all non-zero elements
	SparseGEVT<K> sgevt;
	sgevt.A.dim = n;

	std::vector<SparseEntry>& entries = sgevt.A.entries;
	entries.reserve(nnz_guess);

	// also collect diagonal entries
	Eigen::VectorXf& diag = sgevt.D_inv_sqrt;
	diag = Eigen::VectorXf(n);

	// no collect entries
	for(auto eid : as_range(boost::edges(graph))) {
		int ea = static_cast<int>(boost::source(eid, graph));
		int eb = static_cast<int>(boost::target(eid, graph));
		K ew = edge_weights[eid];
		// assure correct edge weight
		if(std::isnan(ew)) {
			std::cerr << "ERROR: Weight for edge (" << ea << "," << eb << ") is nan!" << std::endl;
			continue;
		}
		if(ew < 0) {
			std::cerr << "ERROR: Weight for edge (" << ea << "," << eb << ") is negative!" << std::endl;
			continue;
		}
		// assure that no vertices is connected to self
		if(ea == eb) {
			std::cerr << "ERROR: Vertex " << ea << " is connected to self!" << std::endl;
			continue;
		}
		// In the lower triangle the row index i is bigger or equal than the column index j.
		// The next statement fullfills this requirement.
		if(ea < eb) {
			std::swap(ea, eb);
		}
		entries.push_back(SparseEntry{ea, eb, ew});
		diag[ea] += ew;
		diag[eb] += ew;
	}

	// do the conversion to a normal ev problem
	// assure global connectivity
	for(unsigned int i=0; i<diag.size(); i++) {
		K& v = diag[i];
		if(v == 0) {
			// connect the disconnected cluster to all other clusters with a very small weight
			v = static_cast<K>(1);
			K q = static_cast<K>(1) / static_cast<K>(n-1);
			for(unsigned int j=0; j<i; j++) {
				auto it = std::find_if(entries.begin(), entries.end(), [i, j](const SparseEntry& e) { return e.i == i && e.j == j; });
				if(it == entries.end()) {
					entries.push_back(SparseEntry{i, j, q});
				}
			}
			for(unsigned int j=i+1; j<n; j++) {
				auto it = std::find_if(entries.begin(), entries.end(), [j, i](const SparseEntry& e) { return e.i == j && e.j == i; });
				if(it == entries.end()) {
					entries.push_back(SparseEntry{j, i, q});
				}
			}
			std::cerr << "ERROR: Diagonal is 0! (i=" << i << ")" << std::endl;
		}
		else {
			v = static_cast<K>(1) / std::sqrt(v);
		}
	}

	// a_ij for the transformed "normal" EV problem
	//		A x = \lambda x
	// is computed as follow from the diagonal matrix D and the weight
	// matrix W of the general EV problem
	//		(D - W) x = \lambda D x
	// as follows:
	//		a_ij = - w_ij / sqrt(d_i * d_j) if i != j
	//		a_ii = 1
	for(SparseEntry& e : entries) {
		e.weight = - e.weight * diag[e.i] * diag[e.j];
	}
	for(unsigned int i=0; i<n; i++) {
		entries.push_back(SparseEntry{i, i, static_cast<K>(1)});
	}

	// sort entries to form a lower triangle matrix
	std::sort(entries.begin(), entries.end(), [](const SparseEntry& a, const SparseEntry& b) {
		return (a.j != b.j) ? (a.j < b.j) : (a.i < b.i);
	});

	return sgevt;
}

template<typename K>
void transform_gev_solution(const Eigen::Matrix<K,-1,1>& D_inv_sqrt, std::vector<EigenComponent>& ec)
{
	// We have x = D^{-1/2} z (see dense_gev_to_ev)
	// thus x_i = z_i / \sqrt(d_i)
	const unsigned int dim = D_inv_sqrt.rows();
	for(std::size_t i=0; i<ec.size(); i++) {
		ec[i].eigenvector = ec[i].eigenvector.cwiseProduct(D_inv_sqrt);
	}
}

/** Assembles edge weights from eigenvalues and eigenvectors */
template<typename Graph>
Eigen::VectorXf ev_to_graph_weights(const Graph& graph, const std::vector<EigenComponent>& solution)
{
	typedef Eigen::Matrix<float,-1,-1> matrix_t;
	typedef Eigen::Matrix<float,-1,1> vector_t;
	vector_t edge_weight = vector_t::Zero(boost::num_edges(graph));
//	// later we weight by eigenvalues
//	// find a positive eigenvalue (need to do this because of ugly instabilities ...
//	Real ew_pos = -1.0f;
//	for(unsigned int i=0; ; i++) {
//		if(solver.eigenvalues()[i] > 0) {
//			// F IXME magic to get a not too small eigenvalue
////			unsigned int x = (n_used_ew + i)/2;
//			unsigned int x = i + 5;
//			ew_pos = solver.eigenvalues()[x];
//			break;
//		}
//	}
//	// compute normalized weights from eigenvalues
//	Vec weights = Vec::Zero(n_used_ew);
//	for(unsigned int k=0; k<n_used_ew; k++) {
//		Real ew = solver.eigenvalues()[k + 1];
//		if(ew <= ew_pos) {
//			ew = ew_pos;
//		}
//		weights[k] = 1.0f / std::sqrt(ew);
//	}
//	std::cout << "Weights = " << weights.transpose() << std::endl;
	// look into first eigenvectors
	// skip first component
	for(unsigned int k=0; k<solution.size(); k++) {
		const EigenComponent& eigen = solution[k];
		// omit if eigenvalue is not positive
		float ew = eigen.eigenvalue;
		// FIXME this is due to numerical instabilities
		if(ew <= 0.0001f) {
			continue;
		}
		// weight by eigenvalue
		float w = 1.0f / std::sqrt(ew);
		// get eigenvector and normalize
		vector_t ev = eigen.eigenvector;
		ev = (ev - ev.minCoeff() * vector_t::Ones(ev.rows())) / (ev.maxCoeff() - ev.minCoeff());
		// for each edge compute difference of eigenvector values
		vector_t e_k = vector_t::Zero(edge_weight.rows());
		// FIXME proper edge indexing
		unsigned int eid_index = 0;
		for(auto eid : as_range(boost::edges(graph))) {
			e_k[eid_index] = std::abs(ev[boost::source(eid, graph)] - ev[boost::target(eid, graph)]);
			eid_index++;
		}
#ifdef SPECTRAL_VERBOSE
		std::cout << "DEBUG w=" << w << " e_k.maxCoeff()=" << e_k.maxCoeff() << std::endl;
#endif
//		e_k /= e_k.maxCoeff();
//		for(unsigned int i=0; i<e_k.rows(); i++) {
//			e_k[i] = std::exp(-e_k[i]);
//		}
		e_k *= w;

#ifdef SEGS_DBG_PRINT
		{
			std::ofstream ofs((boost::format("/tmp/edge_weights_%03d.txt") % k).str());
			for(unsigned int i=0; i<e_k.rows(); i++) {
				ofs << e_k[i] << std::endl;
			}
		}
#endif
		//
		edge_weight += e_k;
	}
	return edge_weight;
}

template<typename K, int ROWS, int COLS>
inline void print_matrix(std::ostream& os, const Eigen::Matrix<K,ROWS,COLS>& m)
{
	for(unsigned int j=0; j<m.rows(); j++) {
		for(unsigned int i=0; i<m.cols(); i++) {
			os << m(j,i);
			if(i+1 == m.cols()) {
				os << "\n";
			}
			else {
				os << "\t";
			}
		}
	}
}

template<typename Graph, typename EdgeWeightMap>
std::vector<EigenComponent> solve_dense(const Graph& graph, EdgeWeightMap edge_weights,
	const std::function<std::vector<EigenComponent>(const Eigen::MatrixXf&)>& solver)
{
	typedef float K;

	DenseGev<K> gev = dense_graph_to_gev<K>(graph, edge_weights);
#ifdef SEGS_DBG_PRINT
		{	std::ofstream ofs("/tmp/L.tsv"); print_matrix(ofs, gev.L); }
		{	std::ofstream ofs("/tmp/D.tsv"); print_matrix(ofs, gev.D); }
#endif

	DenseGevTransformed<K> gevt = dense_gev_to_ev(gev);
#ifdef SEGS_DBG_PRINT
		{	std::ofstream ofs("/tmp/A.tsv"); print_matrix(ofs, gevt.A); }
		{	std::ofstream ofs("/tmp/D_inv_sqrt.tsv"); print_matrix(ofs, gevt.D_inv_sqrt); }
#endif

	std::vector<EigenComponent> v_ec = solver(gevt.A);

	transform_gev_solution(gevt.D_inv_sqrt, v_ec);

	return v_ec;
}

template<typename Graph, typename EdgeWeightMap>
std::vector<EigenComponent> solve_sparse(const Graph& graph, EdgeWeightMap edge_weights,
	const std::function<std::vector<EigenComponent>(const SparseMatrix&)>& solver)
{
	typedef float K;
	SparseGEVT<K> sgevt = sparse_graph_entries<K>(graph, edge_weights);
	std::vector<EigenComponent> v_ec = solver(sgevt.A);
	transform_gev_solution(sgevt.D_inv_sqrt, v_ec);
	return v_ec;
}

}}

#endif


//////////////////////////////////////////////////////////////////////////////////
// distribution::toolkit::distributions::students_t::log_unnormalized_pdf.hpp    //
//                                                                              //
//  (C) Copyright 2009 Erwann Rogard                                            //
//  Use, modification and distribution are subject to 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)            //
//////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_STUDENTS_T_LOG_UNNORMALIZED_PDF_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_STUDENTS_T_LOG_UNNORMALIZED_PDF_HPP_ER_2009
#include <boost/math/distributions/students_t.hpp>
#include <boost/math/special_functions/log1p.hpp>
#include <boost/numeric/conversion/converter.hpp>
// #include <boost/math/policies/policy.hpp> // TODO

namespace boost{
namespace math{

    template<typename T,typename Policy>
    T
    log_unnormalized_pdf(
        const boost::math::students_t_distribution<T,Policy>& d,
        const T& x
    ){

        typedef boost::numeric::converter<T,int> int2R_t;

        T r1 = int2R_t::convert(1);
        T r2 = int2R_t::convert(2);

        T nu = d.degrees_of_freedom();
        T m = ( nu + r1 ) / r2;
        T y = ( x * x ) / nu;
        return (- m ) * math::log1p(y);
    }

}// math
}// boost

#endif


/* based upon http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/cpp_dec_float.html
 * Use, modification and distribution are subject to 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)
 * Copyright ???? 20??. */

#include <iostream>
#include <utility>

#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

using boost::multiprecision::cpp_dec_float;
typedef boost::multiprecision::number<cpp_dec_float<64> > mp_type;

int main(void)
{
    // Operations at fixed precision and full numeric_limits support:
    mp_type b = 2;
    std::cout << std::numeric_limits<mp_type>::digits << std::endl;
    // Note that digits10 is the same as digits, since we're base 10! :
    std::cout << std::numeric_limits<mp_type>::digits10 << std::endl;
    // We can use any C++ std lib function, lets print all the digits as well:
    std::cout << std::setprecision(std::numeric_limits<mp_type>::max_digits10) << log(b) << std::endl; // print log(2)
    // We can also use any function from Boost.Math:
    std::cout << boost::math::tgamma(b) << std::endl;
    // These even work when the argument is an expression template:
    std::cout << boost::math::tgamma(b * b) << std::endl;
    // And since we have an extended exponent range we can generate some really large numbers here (4.0238726007709377354370243e+2564):
    std::cout << boost::math::tgamma(mp_type(1000)) << std::endl;

    return 0;
}


#include <iostream>
#include "EigenMatrix.h"
#include <Eigen/Dense>
#include <Eigen/LU>

typedef Eigen::Matrix<double, Eigen::Dynamic, 1> ColumnVector;
typedef Eigen::Matrix<double, 1, Eigen::Dynamic> RowVector;
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Matrix;

ColumnVector* CastToColumnVector(void* ptr);

ColumnVector* NewColumnVector();

ColumnVector* NewColumnVector(const ColumnVector& v);

RowVector* CastToRowVector(void* ptr);

RowVector* NewRowVector();

RowVector* NewRowVector(const RowVector& v);

void* EigenMatrix_Create(int rows, int columns)
{
	auto m = new Matrix(rows, columns);
	for (int y = 0; y < columns; y++)
		for (int x = 0; x < rows; x++)
			(*m)(x, y) = 0;

	return m;
}

void* EigenMatrix_CreateIdentity(int rows, int columns)
{
	auto m = new Matrix(rows, columns);
	for (int y = 0; y < columns; y++)
	{
		for (int x = 0; x < rows; x++)
		{
			if (x == y)
				(*m)(x,y) = 1;
			else
				(*m)(x, y) = 0;
		}
	}

	return m;
}

void EigenMatrix_Release(void* ptr)
{
	auto obj = static_cast<Matrix*>(ptr);
	if (obj != nullptr)
	{
		delete obj;
		obj = nullptr;
	}
}

Matrix* CastToMatrix(void* ptr)
{
	return static_cast<Matrix*>(ptr);
}

Matrix* NewMatrix(const Matrix& m)
{
	return new Matrix(m.rows(), m.cols());
}

Matrix* NewMatrix()
{
	return new Matrix();
}

int EigenMatrix_Rows(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return (int)m->rows();
}

int EigenMatrix_Columns(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return (int)m->cols();
}

double EigenMatrix_GetXY(void* ptr, int x, int y)
{
	auto m = CastToMatrix(ptr);
	return (*m)(x, y);
}

void EigenMatrix_SetXY(void* ptr, int x, int y, double value)
{
	auto m = CastToMatrix(ptr);
	(*m)(x, y) = value;
}

double EigenMatrix_GetX(void* ptr, int x)
{
	auto m = CastToMatrix(ptr);
	return (*m)(x);
}

void EigenMatrix_SetX(void* ptr, int x, double value)
{
	auto m = CastToMatrix(ptr);
	(*m)(x) = value;
}

void* EigenMatrix_Transpose(void* ptr)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->transpose();
	return m2;
}

void* EigenMatrix_Conjugate(void* ptr)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->conjugate();
	return m2;
}

void* EigenMatrix_Adjoint(void* ptr)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->adjoint();
	return m2;
}

void* EigenMatrix_Inverse(void* ptr)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->inverse();
	return m2;
}

BOOL EigenMatrix_IsInvertible(void* ptr)
{
	auto m = CastToMatrix(ptr);
	Eigen::FullPivLU<Matrix> lu(*m);

	return lu.isInvertible();
}

void* EigenMatrix_TryInverse(void* ptr)
{
	auto m = CastToMatrix(ptr);
	Eigen::FullPivLU<Matrix> lu(*m);

	if (lu.isInvertible())
	{
		auto inv = NewMatrix();
		(*inv) = lu.inverse();
		return inv;
	}
	else
	{
		return nullptr;
	}
}

double EigenMatrix_Determinant(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->determinant();
}

double EigenMatrix_Trace(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->trace();
}

BOOL EigenMatrix_IsIdentity(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->isIdentity();
}

BOOL EigenMatrix_IsDiagonal(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->isDiagonal();
}

BOOL EigenMatrix_IsUpperTriangular(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->isUpperTriangular();
}

BOOL EigenMatrix_IsLowerTriangular(void* ptr)
{
	auto m = CastToMatrix(ptr);
	return m->isLowerTriangular();
}

void* EigenMatrix_MulScalar(void* ptr1, double s)
{
	auto m1 = CastToMatrix(ptr1);
	auto m = NewMatrix(*m1);
	(*m) = (*m1) * s;
	return m;
}

void* EigenMatrix_DivideScalar(void* ptr1, double s)
{
	auto m1 = CastToMatrix(ptr1);
	auto m = NewMatrix(*m1);
	(*m) = (*m1) / s;
	return m;
}

void* EigenMatrix_MulMatrix(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto m2 = CastToMatrix(ptr2);
	auto m = NewMatrix(*m1);
	(*m) = (*m1) * (*m2);
	return m;
}

void* EigenMatrix_AddMatrix(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto m2 = CastToMatrix(ptr2);
	auto m = NewMatrix(*m1);
	(*m) = (*m1) + (*m2);
	return m;
}

void* EigenMatrix_SubMatrix(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto m2 = CastToMatrix(ptr2);
	auto m = NewMatrix(*m1);
	(*m) = (*m1) - (*m2);
	return m;
}

void* EigenMatrix_MulColumnVector(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto v2 = CastToColumnVector(ptr2);
	auto v = NewColumnVector(*v2);
	(*v) = (*m1) * (*v2);
	return v;
}

void* EigenMatrix_MulRowVector(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto v2 = CastToRowVector(ptr2);
	auto v = NewRowVector(*v2);
	(*v) = (*m1) * (*v2);
	return v;
}

void* EigenMatrix_Block(void* ptr, int startRox, int startCol, int blockRows, int blockCols)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->block(startRox, startCol, blockRows, blockCols);
	return m2;
}

void* EigenMatrix_Reshaped(void* ptr, int rows, int cols)
{
	auto m = CastToMatrix(ptr);
	auto m2 = NewMatrix();
	(*m2) = m->reshaped(rows, cols);
	return m2;
}

void* EigenMatrix_ColPivHouseholderQr_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->colPivHouseholderQr().solve(*v);
	return x;
}

void* EigenMatrix_ColPivHouseholderQr_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->colPivHouseholderQr().solve(*v);
	return x;
}

void* EigenMatrix_PartialPivLu_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->partialPivLu().solve(*v);
	return x;
}

void* EigenMatrix_PartialPivLu_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->partialPivLu().solve(*v);
	return x;
}

void* EigenMatrix_FullPivLu_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->fullPivLu().solve(*v);
	return x;
}

void* EigenMatrix_FullPivLu_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->fullPivLu().solve(*v);
	return x;
}

void* EigenMatrix_HouseholderQr_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->householderQr().solve(*v);
	return x;
}

void* EigenMatrix_HouseholderQr_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->householderQr().solve(*v);
	return x;
}

void* EigenMatrix_LLT_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->llt().solve(*v);
	return x;
}

void* EigenMatrix_LLT_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->llt().solve(*v);
	return x;
}

void* EigenMatrix_LDLT_Vec(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m1->ldlt().solve(*v);
	return x;
}

void* EigenMatrix_LDLT_Mat(void* ptr1, void* ptr2)
{
	auto m1 = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m1->ldlt().solve(*v);
	return x;
}

void* EigenMatrix_BdcSvd_Vec(void* ptr1, void* ptr2, int options)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->bdcSvd(options).solve(*v);
	return x;
}

void* EigenMatrix_BdcSvd_Mat(void* ptr1, void* ptr2, int options)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->bdcSvd(options).solve(*v);
	return x;
}

void* EigenMatrix_JacobiSvd_Vec(void* ptr1, void* ptr2, int options)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->jacobiSvd(options).solve(*v);
	return x;
}

void* EigenMatrix_JacobiSvd_Mat(void* ptr1, void* ptr2, int options)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->jacobiSvd(options).solve(*v);
	return x;
}

void* EigenMatrix_FullPivHouseholderQr_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->fullPivHouseholderQr().solve(*v);
	return x;
}

void* EigenMatrix_FullPivHouseholderQr_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->fullPivHouseholderQr().solve(*v);
	return x;
}

void* EigenMatrix_CompleteOrthogonalDecomposition_Vec(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToColumnVector(ptr2);
	auto x = NewColumnVector();

	(*x) = m->completeOrthogonalDecomposition().solve(*v);
	return x;
}

void* EigenMatrix_CompleteOrthogonalDecomposition_Mat(void* ptr1, void* ptr2)
{
	auto m = CastToMatrix(ptr1);
	auto v = CastToMatrix(ptr2);
	auto x = NewMatrix();

	(*x) = m->completeOrthogonalDecomposition().solve(*v);
	return x;
}

double EigenMatrix_RelativeError_Vec(void* ptr1, void* ptr2, void* ptr3)
{
	auto A = CastToMatrix(ptr1);
	auto b = CastToColumnVector(ptr2);
	auto x = CastToColumnVector(ptr3);

	return ((*A) * (*x) - (*b)).norm() / b->norm();
}

double EigenMatrix_RelativeError_Mat(void* ptr1, void* ptr2, void* ptr3)
{
	auto A = CastToMatrix(ptr1);
	auto b = CastToMatrix(ptr2);
	auto x = CastToMatrix(ptr3);

	return ((*A) * (*x) - (*b)).norm() / b->norm();
}

void* EigenMatrix_Eigenvalues(void* ptr)
{
	auto m = CastToMatrix(ptr);

	Eigen::SelfAdjointEigenSolver<Matrix> solver(*m);

	if (solver.info() != Eigen::Success)
		return nullptr;
	else
	{
		auto x = NewColumnVector();
		(*x) = solver.eigenvalues();

		return x;
	}
}

void* EigenMatrix_Eigenvectors(void* ptr)
{
	auto m = CastToMatrix(ptr);

	Eigen::SelfAdjointEigenSolver<Matrix> solver(*m);

	if (solver.info() != Eigen::Success)
		return nullptr;
	else
	{
		auto x = NewMatrix();
		(*x) = solver.eigenvectors();

		return x;
	}
}

BOOL EigenMatrix_EigenValuesVectors(void* ptr, void** values, void** vectors)
{
	auto m = CastToMatrix(ptr);

	Eigen::SelfAdjointEigenSolver<Matrix> solver(*m);
	*values = nullptr;
	*vectors = nullptr;

	if (solver.info() != Eigen::Success)
	{
		return FALSE;
	}
	else
	{
		auto _values = solver.eigenvalues();
		auto _vectors = solver.eigenvectors();

		auto v = NewColumnVector(_values);
		for (auto i = 0; i < _values.size(); i++)
			(*v)[i] = _values[i];

		auto m = NewMatrix(_vectors);
		for (auto i = 0; i < _vectors.size(); i++)
			(*m)(i) = _vectors(i);

		(*values) = v;
		(*vectors) = m;

		return TRUE;
	}
}


#include <complex>
//#include <fftw3.h>
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <blitz/array.h>
#include "lapack.hpp"

//#define MKL_Complex16 std::complex<double>
//#define lapack_int int
//#define lapack_complex_double std::complex<double>
//#include "mkl_lapacke.h"

#include "formats/gnuplot.hpp"
#include "types.hpp"
#include "io.hpp"

typedef std::complex<double> cmplx;

void printMatrix(const std::vector<cmplx>& matrix){
    size_t n = sqrt(matrix.size());
    std::vector<cmplx>::const_iterator it = matrix.begin();
    for(size_t i = 0; i < n; ++i){
        std::cout << " | ";
        for(size_t j = 0; j < n; ++j){
            fprintf( stdout, " %+2.2f %+2.2f i ", real(*it), imag(*it));
            ++it;
        }
        std::cout << " | \n";
    }
    std::cout << std::endl;
}
void printVector(const std::vector<cmplx>& line){
    size_t n = line.size();
    std::vector<cmplx>::const_iterator it = line.begin();
        std::cout << " | ";
        for(size_t j = 0; j < n; ++j){
            fprintf( stdout, " %+2.2f %+2.2f i ", real(*it), imag(*it));
            ++it;
        }
        std::cout << " | \n";
    std::cout << std::endl;
}

// Handcoded 1d DFT on a 2d function
void r2c1dz_code_v2(){
    size_t N = 2300;
    // v1: N = 10 000 needed 7.5 GB RES MEM and 15 minutes (infinijazz node)

    // Here we generate a 2d function input(x,z) = input[x+z*N]
    // that decays in z like exp(-kz z) where kz = sqrt(2E + kx^2)
    // We CHOOSE kx = 1/N and want kz=2/N => E = 3/2 /N/N
    // Goal: Obtain the Fourier coefficients of this function in x,
    //  no matter at which z we give the function.
    double kx = 1.0/N;
    double E  = 1.5/N/N;
    double kz = sqrt(2 * E + kx*kx); // = 2/N

    std::vector< cmplx > input;
    for(int z = 0; z < N; ++z) {
        for(int x = 0; x < N; ++x){
            input.push_back( sin(2.0 * M_PI *x * kx)*exp(-kz*z)  );
        }
    }
    //printMatrix(input);

    std::vector< int > st (N);   // z-indices, where we know f
    std::vector<cmplx> f  (N);   // the function value f at these points

    for(int i = 0; i < N; ++i) {
         st[i] = i % 70;              // lets say we know f on a "stair"
         f[i]  = input[ i + N* st[i]];
    }

    // Build matrix for modified Fourier transform
    // now need transposed matrix
    std::vector<cmplx> matrix (N*N);
    int ir, jr;
    double k;
    for(int i =0; i < N; ++i){
        ir = (2*i < N)? i : i-N;
        for(int j=0; j < N; ++j){
            jr = (2*j < N)? j : j-N;
            k = double(jr)/N;
            //double k = double(j)/N ;
            matrix[i + j*N] =
                exp( 2.0 * M_PI * cmplx(0,1) * double(ir) * k
                     - sqrt( 2*E + k*k) * st[i]);

        }
    }
    //printMatrix(matrix);

    // Invert this fucker
    int one=1;
    int info;
    int n = N;
    std::vector< int > ipiv (N);   // z-indices, where we know f
    std::cout << "Before inverse" << std::endl;
    zgesv_(&n, &one, &*matrix.begin(), &n, &*ipiv.begin(), &*f.begin(), &n, &info);

    //printVector(coeff);
    std::cout << " coeff[1] " << f[1] << " coeff[2] " << f[2];
    std::cout << " coeff[N-2] " << f[N-2] << " coeff[N-1] " << f[N-1];

    std::vector<double> coeffReal;
    std::vector<cmplx>::const_iterator it = f.begin(), end = f.end();
    while(it!=end){ coeffReal.push_back( abs(*it)); ++it; }
    types::String s = formats::gnuplot::writeMatrix(coeffReal, N, 1);
    io::writeStream("fourier", s);

}

// Handcoded 1d DFT on a 2d function
void r2c1dz_code(){
    size_t N = 10000;
    // v1: N = 10 000 needed 7.5 GB RES MEM and 15 minutes (infinijazz node)

    // Here we generate a 2d function input(x,z) = input[x+z*N]
    // that decays in z like exp(-kz z) where kz = sqrt(2E + kx^2)
    // We CHOOSE kx = 1/N and want kz=2/N => E = 3/2 /N/N
    // Goal: Obtain the Fourier coefficients of this function in x,
    //  no matter at which z we give the function.
    double kx = 1.0/N;
    double E  = 1.5/N/N;
    double kz = sqrt(2 * E + kx*kx); // = 2/N

    std::vector< cmplx > input;
    for(int z = 0; z < N; ++z) {
        for(int x = 0; x < N; ++x){
            input.push_back( sin(2.0 * M_PI *x * kx)*exp(-kz*z)  );
        }
    }
    //printMatrix(input);

    std::vector< int > st (N);   // z-indices, where we know f
    std::vector<cmplx> f  (N);   // the function value f at these points

    for(int i = 0; i < N; ++i) {
         st[i] = i;              // lets say we know f on a "stair"
         f[i]  = input[ i + N* st[i]];
    }

    // Build matrix for modified Fourier transform
    std::vector<cmplx> matrix (N*N);
    for(int i =0; i < N; ++i)
        for(int j=0; j < N; ++j){
            double k = (2*j < N)? double(j)/N : (double(N) - j)/N;
            matrix[i*N+j] =
                exp( 2.0 * M_PI * cmplx(0,1) * double(i) * double(j)/double(N)
                     - sqrt( 2*E + k*k) * st[i]);

        }
    //printMatrix(matrix);

    // Invert this fucker
    std::vector<cmplx> inverse(N*N);
    std::cout << "Before inverse" << std::endl;
    //MatrixComplexInverse(&inverse[0],&matrix[0],  N);
    //printMatrix(inverse);
    std::cout << "afterinverse";

    // Obtain Fourier coefficients
    std::vector<cmplx> coeff(N, 0.0);

    for (int i = 0; i<N; ++i){
        for(int j = 0; j < N; ++j){
            coeff[i] += inverse[i*N + j] * f[j];
        }
    }
    //printVector(coeff);
    std::cout << " coeff[1] " << coeff[1] << " coeff[N-1] " << coeff[N-1];

}

// Handcoded 2d DFT on a 3d function
void r2c2dz_code(){
    size_t nX = 32;
    size_t nY = 62;
    size_t N = nX * nY;

    // Here we generate a 3d function input(x,y,z) = input[x+y*N+z*N*N]
    // that decays in z like exp(-kz z) where kz = sqrt(2E + kx^2 + ky^2)
    // We CHOOSE kx = 1/N = ky and want kz=2/N => E = 1/N/N
    // Goal: Obtain the Fourier coefficients of this function in x,
    //  no matter at which z we give the function.
    double kx = 1.0/nX;
    double ky = 1.0/nY;
    double E  = 1.0/N;
    double kz = sqrt(2 * E + kx*kx + ky*ky); // = 2/N

    std::vector< int > st (N);   // z-indices, where we know f
    std::vector<cmplx> f  (N);   // the function value f at these points

    for(int x = 0; x < nX; ++x) {
        for(int y = 0; y < nY; ++y) {
         st[x*nY + y] = 0;              // lets say we know f on a "stair"
         f[x*nY + y]  = sin(2.0 * M_PI *( x*kx + y*ky)) * exp(-kz* double(st[x*nY + y]));
         //f[x*nY + y]  = 1.0;
        }
    }

    std::vector<double> func;
    std::vector<cmplx>::const_iterator it = f.begin(), end = f.end();
    while(it!=end){ func.push_back( abs(*it)); ++it; }
    types::String s2 = formats::gnuplot::writeMatrix(func, nX, nY);
    io::writeStream("function", s2);

    // Build matrix for modified Fourier transform
    std::vector<cmplx> A(N*N);

    int iX, iY, jX, jY;
    double kX, kY, kZ;
    for(int i = 0; i < N; ++i){
            iX = i / nY; iY = i % nY;
            std::cout << "(" << iX << "," << iY << "): ";
        for(int j = 0; j < N; ++j){

            jX = j / nY; jY = j % nY;
            //std::cout << "(" << jX << "," << jY << "), ";
            kX = ( 2* jX > nX) ? (double(jX) - double(nX))/ double(nX) : double(jX) / double(nX);
            kY = ( 2* jY > nY) ? (double(jY) - double(nY))/ double(nY) : double(jY) / double(nY);

            kZ = sqrt( 2 * E + kX * kX + kY * kY);
            //std::cout << ( double(iX) * kX +
            //          double(iY) * kY ) << " ";
            // Need transposed matrix for Fortran lapack
            A[i + j*N ] = exp(
                    2.0 * M_PI * cmplx(0,1) *
                    ( double(iX) * kX +
                      double(iY) * kY )
                    - kZ * st[i]  );
            //                std::cout << A[i + j*n] << " ";

        }
        std::cout << std::endl;
        //          std::cout << "\n";

    }
    //printMatrix(A);

    // Variant1: Solve linear system
    int one=1;
    int info;
    int n = N;
    std::vector< int > ipiv (N);   //permutationmatrix
    std::cout << "Before inverse" << std::endl;
    zgesv_(&n, &one, &*A.begin(), &n, &*ipiv.begin(), &*f.begin(), &n, &info);
    if (info != 0) std::cout << "Error: " << info << "\n";
//    // Variant1b: With lapacke interface
//    int one=1;
//    int n = N;
//    std::vector< int > ipiv (N);   //permutationmatrix
//    std::cout << "Before inverse" << std::endl;
//    int info = LAPACKE_zgesv(LAPACK_ROW_MAJOR, n, one, &*A.begin(), n, &*ipiv.begin(), &*f.begin(), n);

//    // Variant2: Invert this fucker
//    std::vector<cmplx> inverse(N*N);
//    std::cout << "Before inverse" << std::endl;
//    MatrixComplexInverse(&inverse[0],&A[0],  N);
//
//    for(int j = 0; j < N; ++j){
//       f[j] = 0;
//       for(int i = 0; i < N; ++i){
//           // Need transposed matrix for Fortran lapack
//           f[j] += inverse[j*N + i];
//       }
//   }
//
    std::cout << "afterinverse";
//

    std::vector<double> coeffReal;
    std::vector<cmplx>::const_iterator itf = f.begin(), endf = f.end();
    while(itf!=endf){ coeffReal.push_back( abs(*itf)); ++itf; }
    types::String s = formats::gnuplot::writeMatrix(coeffReal, nX, nY);
    io::writeStream("fourier2d", s);

    //    // Invert this fucker
//    std::vector<cmplx> inverse(N*N);
//    MatrixComplexInverse(&inverse[0],&matrix[0],  N);
//    //printMatrix(inverse);
//
//    // Obtain Fourier coefficients
//    std::vector<cmplx> coeff(N, 0.0);
//
//    for (int i = 0; i<N; ++i){
//        for(int j = 0; j < N; ++j){
//            coeff[i] += inverse[i*N + j] * f[j];
//        }
//    }
//    printVector(coeff);
//
}

int main() {
//  r2c1dz_code();
//  r2c1dz_code_v2();
    r2c2dz_code();
    return 0;
}


#include "geometry.h"
#include <Eigen/LU>
#include <Eigen/Geometry>
using namespace Eigen;
namespace marvel {
double clo_surf_vol(const MatrixXd& nods, const MatrixXi& surf)
{
    //TODO:check if the surface is closed and manifold
    double volume = 0;
    for (size_t i = 0; i < surf.cols(); ++i)
    {
        Matrix3d tet;
        for (size_t j = 0; j < 3; ++j)
        {
            tet.row(j) = nods.col(surf(j, i));
        }
        //TODO:check
        volume += tet.determinant();
    }

    return volume;
}

int build_bdbox(const MatrixXd& nods, MatrixXd& bdbox)
{
    //simple bounding box
    //bounding box is a dimension * 2 matrix, whose first column is minimal value and second column is maximal value.
    bdbox = nods.col(0) * MatrixXd::Ones(1, 2);
    for (size_t i = 0; i < nods.cols(); ++i)
    {
        for (size_t j = 0; j < nods.rows(); ++j)
        {
            if (bdbox(j, 0) > nods(j, i))
                bdbox(j, 0) = nods(j, i);
            if (bdbox(j, 1) < nods(j, i))
                bdbox(j, 1) = nods(j, i);
        }
    }
    return 0;
}

}  // namespace marvel


#include <NTL/ZZX.h>

using namespace std;
using namespace NTL;

void inner(int i, ZZX& t, Vec<ZZX>& phi)
{
        for (long j = 1; j <= i-1; j++)
         if (i % j == 0)
            t *= phi(j);
}

void outer(int i, Vec<ZZX>& phi)
{
        ZZX t;
        t = 1;
        inner(i, t, phi);
        phi(i) = (ZZX(INIT_MONO, i) - 1)/t;
        cout << phi(i) << "\n";
}

int main()
{
   Vec<ZZX> phi(INIT_SIZE, 100);

   for (long i = 1; i <= phi.length(); i++) {
      outer(i, phi);
   }
}


#include <algorithm>
#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

using namespace std;
using boost::multiprecision::cpp_int;

int main(int argc, char *argv[]) {
	vector<cpp_int> nums;
	string base = "0123456789";
	do {
		// Convert to integers.
		int a = stoull(base.substr(1, 3));
		int b = stoull(base.substr(2, 3));
		int c = stoull(base.substr(3, 3));
		int d = stoull(base.substr(4, 3));
		int e = stoull(base.substr(5, 3));
		int f = stoull(base.substr(6, 3));
		int g = stoull(base.substr(7, 3));
		if((a % 2 == 0) && (b % 3 == 0) && (c % 5 == 0) && (d % 7 == 0) && (e % 11 == 0) && (f % 13 == 0) && (g % 17 == 0)) {
			nums.push_back((cpp_int)stoull(base));
		}
	} while(next_permutation(base.begin(), base.end()));
	cpp_int sum = 0;
	for(const auto &n : nums) {
		sum += n;
	}
	cout << sum << endl;
	return 0;
}