//
// Created by Сергей Кривонос on 01.09.17.
//
#include "Exponentiation.h"

#include "Valuable.h"
#include "e.h"
#include "i.h"
#include "Infinity.h"
#include "pi.h"
#include "Integer.h"
#include "Sum.h"
#include "Product.h"

#include <cmath>
#include <limits>

#include <boost/numeric/conversion/cast.hpp>

namespace omnn{
namespace math {

    Exponentiation::Exponentiation(const Valuable& b, const Valuable& exponentiation)
        : base(b, exponentiation)
    {
        InitVars();
    }

    max_exp_t Exponentiation::getMaxVaExp(const Valuable& b, const Valuable& e)
    {
        if (e.IsInt()) {
            return b.getMaxVaExp() * e.ca();
        } else if (e.FindVa()) {
            auto i = b.getMaxVaExp();
            if (i) {
                auto _ = e;
                const Variable* v;
                while ((v = _.FindVa())) {
                    _.Eval(*v, 0);
                }
                _.optimize();
                i *= static_cast<a_int>(_);
            }
            return i;
        } else {
            auto maxVaExp = e * b.getMaxVaExp();
            if (maxVaExp.IsInt()) {
                return maxVaExp.ca();
            } else if (maxVaExp.IsSimpleFraction()) {
                auto& f = maxVaExp.as<Fraction>();
                return {f.getNumerator().ca(), f.getDenominator().ca()};
            } else if(!optimizations) {
                optimizations = true;
                maxVaExp.optimize();
                optimizations = {};
                if (maxVaExp.IsInt()) {
                    return maxVaExp.ca();
                }
            }
        }

        IMPLEMENT
    }

    max_exp_t Exponentiation::getMaxVaExp() const
    {
        return getMaxVaExp(getBase(), getExponentiation());
    }

    Valuable Exponentiation::varless() const
    {
        if(getBase().IsVa()) {
            return constants::one;
        } else if (FindVa()) {
            IMPLEMENT;
        }
        else
            return *this;
    }

    void Exponentiation::InitVars() {
        v.clear();
        if (ebase().IsVa())
            v[ebase().as<Variable>()] = eexp();
    }

    void Exponentiation::optimize()
    {
        if (optimized) {
            return;
        }

        if (!optimizations)
        {
            hash = ebase().Hash() ^ eexp().Hash();
            InitVars();
            return;
        }

        ebase().optimize();
        eexp().optimize();

        if (ebase().IsExponentiation() || ebase().IsProduct())
        {
            ebase() ^= eexp();
            Become(std::move(ebase()));
            return;
        }

        if (eexp().IsSum())
        {
            auto& s = eexp().as<Sum>();
            auto sz = s.size();
            auto v = 1_v;
            for(auto it = s.begin(), e = s.end();
                it != e; )
            {
                if (it->IsInt()) {
                    v *= ebase()^*it;
                    s.Delete(it);
                }
                else
                    ++it;
            }
            if (sz != s.size()) {
                Become(*this * v);
                return;
            }
        }

        // todo : check it, comment this and try System test
//        if (ebase().IsSum() && eexp().IsFraction())
//        {
//            auto f = Fraction::cast(eexp());
//            auto& d = f->getDenominator();
//            if (d == ebase().getMaxVaExp()) {
//                auto vars = ebase().Vars();
//                if (vars.size() == 1) {
//                    auto va = *vars.begin();
//                    auto baseToSolve = ebase();
//                    baseToSolve.SetView(View::Solving);
//                    baseToSolve.optimize();
//                    auto eq = va - baseToSolve(va);
//                    auto sq = eq ^ d;
//                    auto check = ebase()/sq;
//                    if (check.IsInt() || check.IsSimpleFraction()) {
//                        ebase() = eq;
//                        eexp() = f->getNumerator();
//                    } else {
//                        // TODO : IMPLEMENT
//                    }
//                }
//            }
//        }
//
//        if (ebase().IsSum() && eexp().IsFraction())
//        {
//            auto f = Fraction::cast(eexp());
//            auto& d = f->getDenominator();
//            auto e = ebase() ^ f->getNumerator();
//            if (d == e.getMaxVaExp()) {
//                auto vars = e.Vars();
//                if (vars.size() == 1) {
//                    auto va = *vars.begin();
//                    auto eq = va - e(va);
//                    auto sq = eq ^ d;
//                    auto check = e / sq;
//                    if (check.IsInt() || check.IsSimpleFraction()) {
//                        Become(std::move(eq));
//                        return;
//                    } else {
//                        // TODO : IMPLEMENT
//                    }
//                }
//            }
//        }

        if (ebase().IsFraction() && eexp().IsMultival()==YesNoMaybe::No) {
            auto& f = ebase().as<Fraction>();
            auto _ = (f.getNumerator() ^ eexp()) / (f.getDenominator() ^ eexp());
            if (_.IsExponentiation()) {
                auto& e = _.as<Exponentiation>();
                if (!(e.ebase()==ebase() && eexp()==e.eexp())) {
                    IMPLEMENT
                }
            } else {
                Become(std::move(_));
                return;
            }
        }

        if (ebase().IsFraction() && eexp().IsInt() && eexp() < 0_v) {
            eexp() = -eexp();
            ebase() = ebase().as<Fraction>().Reciprocal();
        }

		// e^(i*pi) = -1
                // it is a fundamental equation that gives us a hint on cross-dimmensional relations
                // because i and -1 are 1 of different signs/dimmensions
        if (ebase().Is_e()) {
            if (eexp().IsProduct()) {
                auto& p = eexp().as<Product>();
                if (p.Has(constants::pi) &&
                    p.Has(constants::i)) { // TODO : sequence does matter :
																 // e^(i*pi) =?= e^(pi*i)
																 // https://en.wikipedia.org/wiki/Commutative_property#Division,_subtraction,_and_exponentiation
																 // what about Commutativity on irrationals product?
																 // lets assume yes for this particular expression, but there are some doubts, need a prove

						// here is implementation for case of no commutativity:
                                    // auto e = p.end();
                                    // auto it = std::find(p.begin(), e, constant::i);
                                    // auto has_i = it != e;
                                    // if (has_i) {
                                    //     it = std::find(++it, e, constant::pi);
                                    //     auto has_pi_next_to_i = it != e; // maybe sequence does matter
                                    // }

                    p /= constant::i;
                    p /= constant::pi;
                    Become(Exponentiation{-1, p});
                    return;
                }
            }
        }
        // todo : check
        if (ebase().IsSimple()) {
            if (eexp().IsProduct()) {
                auto& p = eexp().as<Product>();
                auto it = p.GetFirstOccurence<Integer>();
                auto in = ebase() ^ *it;  // IsExponentiationSimplifiable
                if (in.IsInt()) {
                    ebase() = in;
                    p.Delete(it);
                    if (p.size() == 1) {
                        eexp().optimize();
                    }
                }
            }
            if (ebase()==1) {
                if (eexp().IsInt()) {
                    Become(std::move(ebase()));
                    return;
                } else if (eexp().IsSimpleFraction() && eexp() > 0_v) {
                    auto& f = eexp().as<Fraction>();
                    auto& n = f.getNumerator();
                    if (n.IsEven() == YesNoMaybe::Yes) {
                        auto&& toOptimize = std::move(eexp());
                        toOptimize.optimize();
                        setExponentiation(std::move(toOptimize));
                        optimize();
                        return;
                    }
                    auto& dn = f.getDenominator();
                    if (dn.bit(0)) {
                        Become(std::move(ebase()));
                        return;
                    } else if (n != 1_v) {
                        hash ^= f.Hash();
                        f.update1(1_v);
                        hash ^= f.Hash();
					}
                } else if (!!eexp().IsMultival()) {
                } else if (!(eexp().IsInfinity() || eexp().IsMInfinity())) {
                    Become(std::move(ebase()));
                    return;
                } else
                    IMPLEMENT;
            } else if (ebase() == -1 && eexp().IsInt() && eexp() > 0 && eexp() != 1) {
                    eexp() = eexp().bit(0);
            } else if (eexp()==-1) {
                Become(Fraction{1,ebase()});
                return;
            } else if (eexp().IsInfinity()) {
                IMPLEMENT
            } else if (eexp().IsFraction()) {
                auto& f = eexp().as<Fraction>();
                auto& n = f.getNumerator();
                if (n != 1) {
                    // TODO: auto is = ebase().IsExponentiationSimplifiable(n);
                    auto newBase = ebase() ^ n;
                    if(!newBase.IsExponentiation()){
                        Become(newBase ^ (1_v / f.getDenominator()));
                        return;
                    }
                }
            }
        }

        bool ebz = ebase() == 0_v;
        bool exz = eexp() == 0_v;
        if(exz)
        {
            if (ebase().IsInfinity() || ebase().IsMInfinity()) {
                IMPLEMENT
            }
            if(ebz)
                throw "NaN";

            Become(1_v);
            return;
        }
        else if(eexp() == 1_v)
        {
            Become(std::move(ebase()));
            return;
        }
        else if (ebz)
        {
            if (exz)
                throw "NaN";
            Become(0_v);
            return;
        }
        else if (ebase().IsInfinity())
        {
            if (eexp() > 0) {
                Become(std::move(ebase()));
            } else
                IMPLEMENT
        }
        else if (ebase().IsMInfinity())
        {
            if (eexp() > 0) {
                if ((eexp() % 2) > 0) // TODO : test with non-ints
                    Become(std::move(ebase()));
                else
                    Become(Infinity());
            } else
                IMPLEMENT
        }
        else if (ebase().IsVa() && eexp().IsSimple())
        {
        }
        else
        {
            switch(view)
            {
                case View::Solving:

                case View::None:
                case View::Calc:
                {
                    if (eexp().IsInt() && eexp()>0) {
                        auto b = 1_v;
                        for (; eexp()--;) {
                            b *= ebase();
                        }
                        Become(std::move(b));
                        return;
                    }
                    if (ebase().IsInt() && eexp().IsInt()) {
                        Become(ebase() ^ eexp());
                        return;
                    }
                    break;
                }
                case View::Flat: {

                    if(eexp().IsInt())
                    {
                        if (ebase().IsVa()) {
                            break;
                        }
                        if (eexp() != 0_v) {
                            if (eexp() > 1) {
                                Valuable x = ebase();
                                Valuable n = eexp();
                                if (n < 0_v)
                                {
                                    x = 1_v / x;
                                    n = -n;
                                }
                                if (n == 0_v)
                                {
                                    Become(1_v);
                                    return;
                                }
                                auto y = 1_v;
                                while(n > 1)
                                {
                                    bool isInt = n.IsInt();
                                    if (!isInt)
                                        IMPLEMENT
                                    if (isInt && n.bit(0) == 0_v)
                                    {
                                        x.sq();
                                        n /= 2;
                                    }
                                    else
                                    {
                                        y *= x;
                                        x.sq();
                                        --n;
                                        n /= 2;
                                    }
                                }
                                x *= y;
                                Become(std::move(x));
                            } else if (eexp()!=-1){
                                // negative
                                Become(1_v/(ebase()^(-eexp())));
                            }
                        }
                        else { // zero
                            if (ebase() == 0_v)
                            {
                                IMPLEMENT
                                throw "NaN"; // feel free to handle this properly
                            }
                            else
                            {
                                Become(1_v);
                            }
                        }
                    }
//                    else
//                    IMPLEMENT
                    break;
                }
                case View::Equation: {
                    if(eexp().IsSimple())
                        Become(std::move(ebase()));
                    break;
                }
                default:
                	LOG_AND_IMPLEMENT(str() << " mode is " << view);
            }
        }

        if(IsExponentiation() && ebase().IsExponentiation())
        {
            auto& e = ebase().as<Exponentiation>();
            auto& eeexp = e.getExponentiation();
            if ((eeexp.FindVa() == nullptr) == (eexp().FindVa() == nullptr)) {
                eexp() *= eeexp;
                // todo : copy if it shared
                ebase() = std::move(const_cast<Valuable&>((e.getBase())));
            }
        }

        if (IsExponentiation()) {
            hash = ebase().Hash() ^ eexp().Hash();
            optimized = true;
            InitVars();
        }
    }

    Valuable& Exponentiation::operator +=(const Valuable& v)
    {
        return Become(Sum {*this, v});
    }

    Valuable& Exponentiation::operator *=(const Valuable& v)
    {
        const Exponentiation* e;
        const Fraction* f;
        const Product* fdn = {};
        const Exponentiation* fdne;
        auto isProdHasExpWithSameBase = [this](const Product* p) -> const Exponentiation*
        {
            for(auto& it : *p){
                if (it.IsExponentiation()) {
                    auto& e = it.as<Exponentiation>();
                    if (ebase() == e.getBase()) {
                        return &e;
                    }
                }
            }
            return {};
        };
        auto& b = ebase();
        if (v.IsExponentiation()
            && b == (e = &v.as<Exponentiation>())->getBase()
            && (eexp().IsInt() || eexp().IsSimpleFraction()) && eexp() > 0
            && (e->eexp().IsInt() || e->eexp().IsSimpleFraction()) && e->eexp() > 0
            )
        {
            updateExponentiation(eexp() + e->getExponentiation());
            optimized={};
        }
        else if(v.IsFraction()
                && (f = &v.as<Fraction>())->getDenominator() == ebase())
        {
            --eexp();
            optimized={};
            optimize();
            return *this *= f->getNumerator();
        }
        else if(v.IsFraction()
                && f->getDenominator().IsProduct()
                && (fdn = &f->getDenominator().as<Product>())->Has(ebase()))
        {
            --eexp();
            optimized={};
            optimize();
            return *this *= f->getNumerator() / (*fdn / ebase());
        }
        else if(fdn
                && (fdne = isProdHasExpWithSameBase(fdn)))
        {
            eexp() -= fdne->getExponentiation();
            optimized={};
            optimize();
            return *this *= f->getNumerator() / (*fdn / *fdne);
        }
        else if(b == v && v.FindVa())
        {
            updateExponentiation(eexp()+1);
            optimized={};
        }
        else if(-b == v && v.FindVa())
        {
            updateExponentiation(eexp()+1);
            optimized = {};
            return Become(Product{-1, *this});
        }
        else if(v.IsProduct())
        {
            return Become(v * *this);
        }
        else if(v.IsInt())
        {
            if(v==1)
                return *this;
            else if(eexp()==-1 && ebase().IsInt())
                return Become(v/ebase());
            else
                return Become(Product{v, *this});
        }
        else
            return Become(Product{v, *this});

        optimize();
        return *this;
    }

    bool Exponentiation::MultiplyIfSimplifiable(const Valuable& v)
    {
        auto is = v == getBase();
        if (is) {
            ++eexp();
            optimized = {};
            optimize();
        } else if (v == constants::one) {
            is = true;
        } else if (!FindVa() && IsMultival() == YesNoMaybe::Yes && !v.FindVa()) {
            solutions_t values;
            for (auto& value : Distinct()) {
                auto&& extract = std::move(const_cast<Valuable&>(value));
                if (extract.MultiplyIfSimplifiable(v)) {
                    is = true;
				} else {
                    extract *= v;
				}
                values.emplace(std::move(extract));
            }
            Become(Valuable(std::move(values)));
        } else if (v.IsExponentiation()) {
            auto& vexpo = v.as<Exponentiation>();
            is = vexpo.getBase() == getBase();
            if (is) {
                eexp() += vexpo.eexp();
                optimized = {};
                optimize();
            } // TODO : else if ? (base^2 == v.base)
            else {
                is = vexpo.getExponentiation() == getExponentiation();
                if (is) {
                    auto wasBaseHash = ebase().Hash();
                    is = ebase().MultiplyIfSimplifiable(vexpo.getBase());
                    if (is) {
                        Valuable::hash ^= wasBaseHash ^ ebase().Hash();
                        optimized = {};
                        optimize();
                    }
                }
            }
        } else if (v.IsMultival() == YesNoMaybe::Yes) {
            LOG_AND_IMPLEMENT(str() << " Exponentiation::MultiplyIfSimplifiable " << v);
        } else if (v.IsInt()) {
            IMPLEMENT
        } else {
//            std::cout << str() << " * " << v.str() << std::endl;
        }
        return is;
    }

    std::pair<bool,Valuable> Exponentiation::IsMultiplicationSimplifiable(const Valuable& v) const
    {
        std::pair<bool,Valuable> is, expSumSimplifiable = {};
        is.first = v == getBase()
            && (expSumSimplifiable = eexp().IsSummationSimplifiable(constants::one)).first;
        if (is.first) {
            is.second = getBase() ^ expSumSimplifiable.second;
        } else if (v.IsExponentiation()) {
            auto& vexpo = v.as<Exponentiation>();
            is.first = vexpo.getBase() == getBase();
            if (is.first) {
                is.second = ebase() ^ (eexp() + vexpo.eexp());
            } // TODO : else if ? (base^2 == v.base)
        } else if (v.IsSimple()) {
            auto& ee = getExponentiation();
            //is = v.IsExponentiationSimplifiable(ee);  // TODO: Implement IsExponentiationSimplifiable
            // FIXME: Until IsExponentiationSimplifiable ready:
            if (ee.IsSimpleFraction()) {
                is.second = v ^ ee.as<Fraction>().Reciprocal(); // v.IsExponentiationSimplifiable(ee)
                is.first = is.second.MultiplyIfSimplifiable(getBase());
                if(is.first){
                    auto copy = *this;
                    copy.updateBase(std::move(is.second));
                    is.second = copy;
                }
            }
            else if (ee == constants::minus_1)
            {
                is.second = v ^ constants::minus_1; // v.IsExponentiationSimplifiable(ee)
                is.first = is.second.MultiplyIfSimplifiable(getBase());
                if(is.first){
                    auto copy = *this;
                    copy.updateBase(std::move(is.second));
                    is.second = copy;
                }
            }
            else if (ee.IsInt() && (ee > constants::zero)) // TODO: ee < 0 too
            {
//                is.second = v ^ (ee ^ constants::minus_1); // v.IsExponentiationSimplifiable(ee)
//                is.first = is.second.MultiplyIfSimplifiable(getBase());
//                if(is.first){
//                    auto copy = *this;
//                    copy.updateBase(std::move(is.second));
//                    is.second = copy;
//                }
            }
//            if (getBase().IsVa()) {
//            } else if (getExponentiation().IsSimpleFraction()) {
//                auto
//                is.first = IsMultiplicationSimplifiable()
//            } else if (getExponentiation().IsSimple()) {
//                is = base::IsMultiplicationSimplifiable(v);
//            } else {
//                IMPLEMENT
//            }
        } else if (v.IsVa()) {
            // covered by (v==base()) case
        } else if (v.IsProduct()) {
            is=v.IsMultiplicationSimplifiable(*this);
        } else if (v.IsSum()) {
            auto& sum=v.as<Sum>();
            is.first=sum==getBase()||-sum==getBase();
            if(is.first){
                is.second=*this*sum;
            }else{
                is=sum.IsMultiplicationSimplifiable(*this);
            }
        } else {
#ifndef NDEBUG
            std::cout << "IsMultiplication simplifiable?: " << str() << " * " << v.str() << std::endl;
#endif
        }
        return is;
    }

    bool Exponentiation::SumIfSimplifiable(const Valuable& v)
    {
        auto is = !v.IsSimple() && !v.IsFraction() && !v.IsExponentiation();
        if(is){
            auto sumIfSimplifiable = v.IsSummationSimplifiable(*this);
            is = sumIfSimplifiable.first;
            if (is)
                Become(std::move(sumIfSimplifiable.second));
        }
        return is;
    }

    std::pair<bool,Valuable> Exponentiation::IsSummationSimplifiable(const Valuable& v) const
    {
        std::pair<bool,Valuable> is;
        is.first = operator==(v);
        if (is.first) {
            is.second = *this * 2;
        } else if ((is.first = operator==(-v))) {
                is.second = 0;
        } else if (v.IsSimple()
                || v.IsExponentiation()
                || v.IsVa()
                || v.IsFraction())
        {
        } else {
            is = v.IsSummationSimplifiable(*this);
        }
        return is;
    }

    Valuable& Exponentiation::operator /=(const Valuable& v)
    {
        auto isMultival = IsMultival()==YesNoMaybe::Yes;
        auto vIsMultival = v.IsMultival()==YesNoMaybe::Yes;
        if(isMultival && vIsMultival) {
            solutions_t vals, thisValues;
            Values([&](auto& thisVal){
                thisValues.insert(thisVal);
                return true;
            });

            v.Values([&](auto&vVal){
                for(auto& tv:thisValues)
                    vals.insert(tv/vVal);
                return true;
            });

            return Become(Valuable(std::move(vals)));
        }
        else if (v.IsExponentiation())
        {
            auto& e = v.as<Exponentiation>();
            if(ebase() == e.ebase() && (ebase().IsVa() || !ebase().IsMultival()))
            {
                eexp() -= e.eexp();
            }
            else
            {
                Become(Fraction(*this, v));
                return *this;
            }
        }
        else if(v.IsFraction())
        {
            *this *= v.as<Fraction>().Reciprocal();
            return *this;
        }
        else if(ebase() == v)
        {
            --eexp();
        }
        else
        {
            Become(Fraction(*this, v));
            return *this;
        }

        optimized={};
        optimize();
        return *this;
    }

    Valuable& Exponentiation::operator^=(const Valuable& v)
    {
        eexp() *= v;
        optimized={};
        optimize();
        return *this;
    }

    bool Exponentiation::operator ==(const Valuable& v) const
    {
        auto eq = v.IsExponentiation() && Hash()==v.Hash();
        if(eq){
            auto& e = v.as<Exponentiation>();
            eq = _1.Hash() == e._1.Hash()
                && _2.Hash() == e._2.Hash()
                && _1 == e._1
                && _2 == e._2;
        } else if (v.IsFraction()) {
            eq = eexp().IsInt()
                 && eexp() < 0
                 && ebase() == (v.as<Fraction>().getDenominator() ^ (-eexp()));
        }
        return eq;
    }

    Exponentiation::operator double() const
    {
        return std::pow(static_cast<double>(ebase()), static_cast<double>(eexp()));
    }

    Valuable& Exponentiation::d(const Variable& x)
    {
        optimized={};
        bool bhx = ebase().HasVa(x);
        bool ehx = eexp().HasVa(x);
        if(ehx) {
            IMPLEMENT
            if(bhx){

            }else{

            }
        } else if (bhx) {
            if(ebase() == x)
                Become(eexp() * (ebase() ^ (eexp()-1)));
            else
                IMPLEMENT
        } else
            Become(0_v);
        optimize();
        return *this;
    }

    void Exponentiation::integral(const Variable& x, const Variable& C)
    {
        if ((eexp().IsInt() || eexp().IsSimpleFraction()) && ebase()==x) {
            ++eexp();
            operator/=(eexp());
            operator+=(C);
        } else {
            IMPLEMENT
        }

        optimize();
    }

    bool Exponentiation::operator <(const Valuable& v) const
    {
        if (v.IsExponentiation())
        {
            auto& e = v.as<Exponentiation>();
            if (e.getBase() == getBase())
                return getExponentiation() < e.getExponentiation();
            if (e.getExponentiation() == getExponentiation())
                return getBase() < e.getBase();
        }

        return base::operator <(v);
    }

    std::ostream& Exponentiation::print_sign(std::ostream& out) const
    {
        return out << "^";
    }

    Valuable::YesNoMaybe Exponentiation::IsMultival() const
    {
        auto is = _1.IsMultival() || _2.IsMultival();
        if (is != YesNoMaybe::Yes && _2.IsFraction())
            is = _2.as<Fraction>().getDenominator().IsEven() || is;
        return is;
    }

    void Exponentiation::Values(const std::function<bool(const Valuable&)>& fun) const
    {
        if (fun) {
            auto cache = optimized; // TODO: multival caching (inspect all optimized and optimization transisions) auto isCached =

            std::set<Valuable> vals;
            {
            std::deque<Valuable> d1;
            _1.Values([&](auto& v){
                d1.push_back(v);
                return true;
            });

            _2.Values([&](auto& v){
                auto vIsFrac = v.IsFraction();
                const Fraction* f;
                if(vIsFrac)
                    f = &v.template as<Fraction>();
                auto vMakesMultival = vIsFrac && f->getDenominator().IsEven()==YesNoMaybe::Yes;

                for(auto& item1:d1){
                    if(vMakesMultival){
                        static const Variable x;
                        auto& dn = f->getDenominator();
                        auto solutions = (x ^ dn).Equals(*this ^ dn).Solutions(x);
                        for(auto&& s:solutions)
                            vals.insert(s);
                    } else {
                        auto value=item1^v;
                        if(value.IsMultival()==YesNoMaybe::No)
                            vals.insert(value);
                        else {
                            IMPLEMENT
                        }
                    }
                }
                return true;
            });
            }

            for(auto& v:vals)
                fun(v);
        }
    }

    std::ostream& Exponentiation::code(std::ostream& out) const
    {
        if(!getExponentiation().IsInt())
            IMPLEMENT;

        out << "(1";
        for (auto i=getExponentiation(); i-->0;) {
            out << '*' << getBase();
        }
        out << ')';

        return out;
    }

    bool Exponentiation::IsComesBefore(const Valuable& v) const
    {
        auto mve = getMaxVaExp();
        auto vmve = v.getMaxVaExp();
        auto is = mve > vmve;
        if (mve != vmve)
        {}
        else if (v.IsExponentiation())
        {
            auto& e = v.as<Exponentiation>();
            bool baseIsVa = getBase().IsVa();
            bool vbaseIsVa = e.getBase().IsVa();
            if (baseIsVa && vbaseIsVa)
                is = getExponentiation() == e.getExponentiation() ? getBase().IsComesBefore(e.getBase()) : getExponentiation() > e.getExponentiation();
            else if(baseIsVa)
                is = false;
            else if(vbaseIsVa)
                is = true;
            else if(getBase() == e.ebase())
                is = getExponentiation().IsComesBefore(e.getExponentiation());
            else if(getExponentiation() == e.getExponentiation())
                is = getBase().IsComesBefore(e.getBase());
            else
            {
                auto c = Complexity();
                auto ec = e.Complexity();
                if (c != ec)
                    is = c > ec;
                else {
                    is = getBase().IsComesBefore(e.getBase()) ||
                        (!e.ebase().IsComesBefore(ebase()) && getExponentiation().IsComesBefore(e.getExponentiation())); //  || str().length() > e->str().length();
    //                auto expComesBefore = eexp().IsComesBefore(e->eexp());
    //                auto ebase()ComesBefore = ebase().IsComesBefore(e->ebase());
    //                is = expComesBefore==ebase()ComesBefore || str().length() > e->str().length();
                }
            }
        }
        else if(v.IsProduct())
        {
            is = !(v.IsComesBefore(*this) || operator==(v));
        }
        else if(v.IsInt())
            is = true;
//        else if(v.IsFraction())
//        {is=}
        else if(v.IsVa())
            is = !!FindVa();
        else if(v.IsSum())
            is = IsComesBefore(*v.as<Sum>().begin());
        else
            IMPLEMENT

        return is;
    }

    Valuable Exponentiation::calcFreeMember() const
    {
        Valuable c;
        if(getBase().IsSum() && getExponentiation().IsInt()){
            c = getBase().calcFreeMember() ^ getExponentiation();
        } else if(getBase().IsVa()) {
            c = 0_v;
        } else
            IMPLEMENT;
        return c;
    }

    Valuable & Exponentiation::sq()
    {
        eexp() *= 2;
        optimized = {};
        optimize();
        return *this;
    }

    const Valuable::vars_cont_t& Exponentiation::getCommonVars() const
    {
#ifndef NDEBUG
        auto& b = ebase();
        if (b.IsVa()) { // TODO: FindVa too
            auto va = b.as<Variable>();
            auto it = v.find(va);
            if (it != v.end() && !it->second.Same(eexp()))
                LOG_AND_IMPLEMENT(*this << " Exponentiation::getCommonVars not ready");
        }
#endif
        return v;
    }

    Valuable Exponentiation::InCommonWith(const Valuable& v) const
    {
        auto c = 1_v;
        if (v.IsProduct()) {
            for(auto& m: v.as<Product>()){
                c = InCommonWith(m);
                if (c != 1_v) {
                    break;
                }
            }
        } else if (v.IsExponentiation()) {
            auto& e = v.as<Exponentiation>();
            if (e.getBase() == getBase()) {
                if (e.getExponentiation() == getExponentiation()) {
                    c = e;
                } else if (getExponentiation().IsSimple() && e.getExponentiation().IsSimple()) {
                    if (getExponentiation() > 0 || e.getExponentiation() > 0) {
                        if (e.getExponentiation() >= getExponentiation()) {
                            c = *this;
                        } else
                            c = e;
                    } else if (getExponentiation() < 0 || e.getExponentiation() < 0) {
                        if (e.getExponentiation() >= getExponentiation()) {
                            c = e;
                        } else
                            c = *this;
                    } else {
                        IMPLEMENT
                    }
                } else if (getExponentiation().IsSimpleFraction() && e.getExponentiation().IsSimpleFraction()) {
                    if (getExponentiation()<0 == e.getExponentiation()<0) {
                        c = getBase() ^ getExponentiation().InCommonWith(e.getExponentiation());
                    }
                } else if (getExponentiation().IsSum()) {
                    auto sz = getExponentiation().as<Sum>().size();
                    auto diff = getExponentiation() - e.getExponentiation();
                    if (!diff.IsSum() || diff.as<Sum>().size() < sz)
                        c = v;
                } else if (e.getExponentiation().IsSum()) {
                    c = e.InCommonWith(*this);
                } else if (e.getExponentiation().IsProduct()) {
                    c = ebase() ^ e.eexp().InCommonWith(eexp());
                } else {
                    IMPLEMENT
                }
            }
        } else if (getExponentiation().IsInt()) {
            if(getExponentiation() > 0)
                c = getBase().InCommonWith(v);
        } else if (getExponentiation().IsFraction()) {
        } else if (v.IsVa()) {
            c = v.InCommonWith(*this);
        } else if (v.IsInt() || v.IsSimpleFraction()) {
        } else if (getExponentiation().IsVa()) {
        } else {
            IMPLEMENT
        }
        return c;
    }

    Valuable Exponentiation::operator()(const Variable& va) const
    {
        return operator()(va, 0_v);
    }

    Valuable Exponentiation::operator()(const Variable& v, const Valuable& augmentation) const
    {
        if (!getExponentiation().FindVa() && getExponentiation()!=0 && augmentation==0) {
            return getBase()(v,augmentation);
        } else if (getExponentiation().IsSimpleFraction()) {
            auto& f = getExponentiation().as<Fraction>();
            return (getBase()^f.getNumerator())(v,augmentation^f.getDenominator());
        } else {
            IMPLEMENT
        }
    }

    Valuable::solutions_t Exponentiation::Distinct() const
    {
        solutions_t branches;
        if (eexp().IsSimpleFraction()){
            auto& f = eexp().as<Fraction>();
            auto& denom = f.denominator();
            if (denom.IsEven() == YesNoMaybe::Yes) {
                // TODO : de-recoursefy:
//                auto branchesSz = boost::multiprecision::msb(denom); // the largest bit
//                branches.reserve(branchesSz);
//                ...
                auto& exponentiationBase = ebase();
                if(!exponentiationBase.IsSimple()){
                    if (!exponentiationBase.FindVa() && exponentiationBase.IsMultival() == YesNoMaybe::Yes) {
                        for (auto&& branch : ebase().Distinct()) {
                            auto branchDistinct = (branch ^ eexp()).Distinct();
                            branches.insert(branchDistinct.begin(), branchDistinct.end());
                        }
                    } else {
                        LOG_AND_IMPLEMENT("Distinct for " << *this);
                    }
                } else {
                    auto b = ebase() ^ f.numerator();
                    auto d = denom;
                    do {
                        b.sqrt();
                        d.shr();
                    } while(d.IsEven() == Valuable::YesNoMaybe::Yes);
                    auto _ = b ^ (1_v / d);
                    auto distinct = _.Distinct();
                    if(denom==2)
                        for (auto& branch : distinct)
                        {
                            branches.emplace(-branch);
                            branches.emplace(std::move(const_cast<decltype(distinct)::reference>(branch)));
                        }
                    else if(denom==4)
                        for (auto& branch : distinct)
                        {
                            branches.emplace(-branch * constants::i);
                            branches.emplace(branch * constants::i);
                            branches.emplace(-branch);
                            branches.emplace(std::move(const_cast<decltype(distinct)::reference>(branch)));
                        }
                    else
                        LOG_AND_IMPLEMENT("Implement support for " << denom << " dimmensions");
                }
            }
        } else {
            branches.emplace(*this);
        }
        return branches;
    }
}}


#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <tuple>
#include <stdbool.h>
#include <bitset>
#include <string>
#include <boost/multiprecision/cpp_int.hpp>

namespace mp = boost::multiprecision;
using namespace std;

int main(void) {
    mp::cpp_int n, m;
    cin >> n; // 店の件数
    cin >> m; // ドリンク本数
    map<mp::cpp_int,mp::cpp_int> drinks; // price -> counts

    for(mp::cpp_int i = 0 ; i < n ; ++i) {
        mp::cpp_int  a, b;
        cin >> a;
        cin >> b;
        drinks[a] = b;
    }
    // countが残り本数, sum=合計金額
    mp::cpp_int count = m;
    mp::cpp_int sum = 0;
    for(const auto& d: drinks) {
        if(d.second < count) {
            // 買い占め
            count -= d.second;
            sum += d.first * d.second;
        } else {
            sum += d.first * count;
            count = 0;
            break;
        }
    }
    cout << sum.str() << endl;
    return 0;
}


#include <fstream>
#include <string>
#include <math.h>
#include <time.h>
#include "model.h"
#include <stdlib.h>
#include <armadillo>
#include "model_fancy.h"

using namespace arma;

int initStateFile(char *file)
{
        std::ofstream out(file,std::ios_base::trunc);

        if (!out)
        {
            perror("output file error\n");
                return -1;
        }

        out.close();

        return 0;
}

void addState(char *file, fmat u)
{
        std::ofstream out(file,std::ios_base::app);
        for(unsigned int i=0; i < u.n_rows; ++i){
                out << u[i] << " ";
        }
        out << std::endl;
        out.close();
}

int main (int argc, char* argv[]){
        int num = 0;
        if (argc<3){
                perror("Error");
                printf("Please call program like:\n");
                printf("./datagen filename_original filename_measurement numberOfStates\n");
                return -1;
        }
        char *file_original = argv[1];
        char *file_out = argv[2];
        num = atoi(argv[3]);

        Model *process = new ModelFancy();
        process->initialize();

        printf("Dimension of state: %d\n",process->getProcessDimension());

        fmat state(process->getProcessDimension(),1);

        fmat measurement(process->getMeasurementDimension(),1);

        state.zeros();

        initStateFile(file_out);	// initialize output file
        initStateFile(file_original);

        addState(file_out, state);

        for (int k=0; k<num;k++)
        {
            state = process->ffun(&state);
            printf("x: %e  y: %e\n",state(0),state(1));
            addState(file_original,state);
            measurement = process->hfun(&state);
            printf("x: %e  y: %e\n\n",measurement(0),measurement(1));
            addState(file_out, measurement);
        }

        printf("Done!");

        return 0;
}


/*!
 * Copyright (C) tkornuta, IBM Corporation 2015-2019
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*!
 * @file: MatrixTests.cpp
 * @Author: Tomasz Kornuta <tkornut@us.ibm.com>
 * @Date:   Nov 22, 2016
 *
 * Copyright (c) 2016, IBM Corporation. All rights reserved.
 *
 */

#include <gtest/gtest.h>

#include <fstream>
// Include headers that implement a archive in simple text format
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

// Redefine word "public" so every class field/method will be accessible for tests.
#define private public
#include <types/Matrix.hpp>

/*!
 * Tests whether matrix has proper dimensions (2x5).
 */
TEST(Matrix, Dimensions2x5) {
	// Default sizes of matrices.
	const size_t N = 2;
	const size_t M = 5;

	mic::types::Matrix<float> nm(N, M);

	ASSERT_EQ(nm.rows(), N);
	ASSERT_EQ(nm.cols(), M);

}

/*!
 * Tests matrix serialization.
 */
TEST(Matrix, Serialization) {
	// Default sizes of matrices.
	const size_t N = 2;
	const size_t M = 5;

	mic::types::Matrix<float> nm(N, M);
	nm.randn();

	const char* fileName = "saved.txt";
	// Save data
	{
		// Create an output archive
		std::ofstream ofs(fileName);
		boost::archive::text_oarchive ar(ofs);
		// Write data
		ar & nm;
//		std::cout << "Saved matrix = " << nm << std::endl;
	}

	// Restore data
	mic::types::Matrix<float> restored_mat;
	restored_mat.randn();

	{
		// Create and input archive
		std::ifstream ifs(fileName);
		boost::archive::text_iarchive ar(ifs);
		// Load data
		ar & restored_mat;
//		std::cout << "Restored matrix = " << restored_mat << std::endl;
	}

	for (size_t i =0; i< (size_t)nm.size(); i++)
		ASSERT_EQ(nm(i), restored_mat(i));

}

/*!
 * Tests assignment operator.
 */
TEST(Matrix, OperatorAssignmentFloat) {
	// Default sizes of matrices.
	const size_t N = 2;
	const size_t M = 5;

	mic::types::Matrix<float> nm(N, M);
	for (size_t i= 0; i<N; i++)
		for (size_t j= 0; j<M; j++)
			nm(i,j) = i*j;

	mic::types::Matrix<float> nm2 = nm;
	for (size_t i= 0; i<N; i++)
		for (size_t j= 0; j<M; j++)
			ASSERT_EQ(nm2(i,j), i*j);

}

/*!
 * Tests enumeration.
 */
TEST(Matrix, Enumeration2x3) {
	// Default sizes of matrices.
	const size_t N = 2;
	const size_t M = 3;

	mic::types::Matrix<float> nm(N, M);
	nm.enumerate();

	for (size_t i =0; i< N*M; i++)
		ASSERT_EQ(nm(i), i);

	/*for(size_t row=0; row<N; row++) {
		for(size_t col=0; col<M; col++)
			std::cout << " nm(" << row << "," << col << ") = " << nm(row,col);
		std::cout << std::endl;
	}//: for*/

	ASSERT_EQ(nm(0,0), 0);
	ASSERT_EQ(nm(0,1), 2);
	ASSERT_EQ(nm(0,2), 4);
	ASSERT_EQ(nm(1,0), 1);
	ASSERT_EQ(nm(1,1), 3);
	ASSERT_EQ(nm(1,2), 5);

	nm.resize(M,N);

	/*for(size_t row=0; row<M; row++) {
		for(size_t col=0; col<N; col++)
			std::cout << " nm(" << row << "," << col << ") = " << nm(row,col);
		std::cout << std::endl;
	}//: for*/

}

/*!
 * Tests functions added to make Eigen-derived Matrix as much compatible to Armadillo-derived Matrix as possible.
 */
TEST(Matrix, ArmadilloCompatibilityTest) {
	// Default sizes of matrices.
	const size_t N = 4;
	const size_t M = 5;

	mic::types::Matrix<float> nm(N, M);
	nm.zeros();

	for (size_t i =0; i< N*M; i++)
		ASSERT_EQ(nm[i], 0);
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}


#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <functional>
#include <Eigen/Dense>
#include "../include/model.h"
#include "../include/numerical_gradient.h"

namespace MyDL
{

    using namespace Eigen;
    using std::cout;
    using std::endl;
    using std::make_shared;
    using std::shared_ptr;
    using std::string;
    using std::unordered_map;
    using std::vector;

    TwoLayerMLP::TwoLayerMLP(int input_size, int hidden_size, int output_size, double weight_init_std)
    {
        // 内部保持パラメータ
        _input_size = input_size;
        _hidden_size = hidden_size;
        _output_size = output_size;
        _weight_init_std = weight_init_std;

        // Affine Layer用のパラメータ -> スマートポインタで保持し、それをパラメータのリストに格納
        auto W1 = make_shared<MatrixXd>(input_size, hidden_size);
        auto W2 = make_shared<MatrixXd>(hidden_size, output_size);
        auto b1 = make_shared<MatrixXd>(1, hidden_size);
        auto b2 = make_shared<MatrixXd>(1, output_size);

        *W1 = weight_init_std * MatrixXd::Random(input_size, hidden_size);
        *W2 = weight_init_std * MatrixXd::Random(hidden_size, output_size);
        *b1 = MatrixXd::Zero(1, hidden_size);
        *b2 = MatrixXd::Zero(1, output_size);

        // Layer作成 -> スマートポインタで実装(コンストラクタを抜けた時に、実体が消されないようにするため)
        // 左辺の型はautoにしてはいけない(BaseLayerで統一し、コンテナに格納する ※ポリモーフィズムの実現)
        shared_ptr<BaseLayer> affine1 = make_shared<MyDL::Affine>(W1, b1);
        shared_ptr<BaseLayer> affine2 = make_shared<MyDL::Affine>(W2, b2);
        shared_ptr<BaseLayer> relu1 = make_shared<ReLU>();
        shared_ptr<BaseLayer> last_layer = make_shared<SoftmaxWithLoss>(); // predictではaffine2の出力、lossではlastlayerの出力を使うので分ける

        // 生のポインタを格納すると実体がスコープ外となり解放されてしまうので、shared_ptrで対処
        _layers["Affine1"] = affine1;
        _layers["ReLU1"] = relu1;
        _layers["Affine2"] = affine2;
        _last_layer = last_layer;

        // 各パラメータへのポインタを格納
        if (auto cast_affine1 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine1"]))
        {
            params["W1"] = cast_affine1->pW;
            params["b1"] = cast_affine1->pb;
        }
        if (auto cast_affine2 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine2"]))
        {
            params["W2"] = cast_affine2->pW;
            params["b2"] = cast_affine2->pb;
        }

        // unordered_mapでは追加順が保存されないので、別途順番通り名称を格納したコンテナを用意
        _layer_list.push_back("Affine1");
        // _layer_list.push_back("BatchNorm"); // for batchnorm debug 21/03/21追加
        _layer_list.push_back("ReLU1");
        _layer_list.push_back("Affine2");

        // ------------------------------------------------
        // for batchnorm debug 21/03/21追加
        // ------------------------------------------------
        // auto gamma = make_shared<MatrixXd>(1, hidden_size);
        // auto beta = make_shared<MatrixXd>(1, hidden_size);
        // *gamma = weight_init_std * MatrixXd::Random(1, hidden_size);
        // *beta = MatrixXd::Zero(1, hidden_size);
        // shared_ptr<BaseLayer> batch_norm = make_shared<BatchNorm>(gamma, beta);
        // _layers["BatchNorm"] = batch_norm;
        // if (auto cast_batchnorm = std::dynamic_pointer_cast<BatchNorm>(_layers["BatchNorm"]))
        // {
        //     params["gamma"] = cast_batchnorm->pgamma;
        //     params["beta"] = cast_batchnorm->pbeta;
        // }
    }

    vector<MatrixXd> TwoLayerMLP::predict(vector<MatrixXd> inputs)
    {
        // inputのバリデーションをしておくか？
        vector<MatrixXd> X = inputs; // 入力もvectorなので、そのまま受ければOK
        vector<MatrixXd> tmp_X;

        // mapのrange-forは内部的にstd::pairが返される
        for (auto layer : _layer_list)
        {
            // cout << layer << endl;
            tmp_X = _layers[layer]->forward(X);
            X.swap(tmp_X); // 中身入れ替え
        }
        return X;
    }

    vector<MatrixXd> TwoLayerMLP::loss(vector<MatrixXd> inputs, MatrixXd& t)
    {
        vector<MatrixXd> pred_input, pred_out, loss_inputs, loss_output;
        pred_input.push_back(inputs[0]);
        pred_out = predict(pred_input);

        loss_inputs.push_back(pred_out[0]);
        loss_inputs.push_back(t);

        loss_output = _last_layer->forward(loss_inputs);
        return loss_output;
    }

    double TwoLayerMLP::accuracy(vector<MatrixXd> inputs, MatrixXd& t)
    {
        vector<MatrixXd> pred_out;
        pred_out = predict(inputs);

        MatrixXd y;
        y = pred_out[0];
        double batch_size = t.rows();
        double accuracy = 0;

        MatrixXd::Index y_row, y_col, t_row, t_col;
        for (int i = 0; i < batch_size; i++)
        {
            y.row(i).maxCoeff(&y_row, &y_col);
            t.row(i).maxCoeff(&t_row, &t_col);

            accuracy += (double)(y_col == t_col);
        }

        return accuracy / batch_size;
    }

    unordered_map<string, MatrixXd> TwoLayerMLP::gradient(vector<MatrixXd> inputs, MatrixXd& t)
    {
        // Forward
        vector<MatrixXd> output;
        output = loss(inputs, t); // forward -> 逆伝播計算に必要な情報を各レイヤにキャッシュ

        // Backward
        vector<MatrixXd> dout, tmp_dout;
        dout.push_back(MatrixXd::Ones(1, 1));

        dout = _last_layer->backward(dout);

        // 逆順ループ → Boostライブラリのboost::adaptors::reverse()を使う方がEasyではある
        for (auto it = _layer_list.rbegin(); it != _layer_list.rend(); it++)
        {
            string layer = *it;
            tmp_dout = _layers[layer]->backward(dout);
            dout.swap(tmp_dout);
        }

        unordered_map<string, MatrixXd> grads;
        // _layersに格納している変数はBaseLayerにアップキャストしているので、ダウンキャストが必要 -> nullptrのときは実行しないようにする
        if (shared_ptr<MyDL::Affine> affine1 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine1"]))
        {
            grads["W1"] = affine1->dW;
            grads["b1"] = affine1->db;
        }
        if (shared_ptr<MyDL::Affine> affine2 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine2"]))
        {
            grads["W2"] = affine2->dW;
            grads["b2"] = affine2->db;
        }

        // for batchnorm debug 21/03/21追加
        if (auto batchnorm = std::dynamic_pointer_cast<BatchNorm>(_layers["BatchNorm"]))
        {
            grads["gamma"] = batchnorm->dgamma;
            grads["beta"] = batchnorm->dbeta;
        }

        return grads;
    }

    // unordered_map<string, MatrixXd> TwoLayerMLP::numerical_gradient(vector<MatrixXd> inputs)
    // {
    //     // [&]は、スコープ外の変数を参照するというキャプチャー(ここではthisポインタを使うために指定)
    //     std::function<vector<MatrixXd>(MatrixXd)> loss_W = [this, &inputs](MatrixXd W) -> vector<MatrixXd> { return this->loss(inputs); };
    //     std::function<vector<MatrixXd>(VectorXd)> loss_W2 = [this, &inputs](VectorXd W) -> vector<MatrixXd> { return this->loss(inputs); };
    //     unordered_map<string, MatrixXd> grads;

    //     MatrixXd dW1, dW2, db1, db2;

    //     // 直接内部のレイヤのパラメータにアクセスするので、ダウンキャストが必要
    //     if (shared_ptr<MyDL::Affine> affine1 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine1"]))
    //     {
    //         dW1 = MyDL::numerical_gradient(loss_W, affine1->_W);
    //         db1 = MyDL::numerical_gradient(loss_W2, affine1->_b);
    //     }
    //     if (shared_ptr<MyDL::Affine> affine2 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine2"]))
    //     {
    //         dW2 = MyDL::numerical_gradient(loss_W, affine2->_W);
    //         db2 = MyDL::numerical_gradient(loss_W2, affine2->_b);
    //     }

    //     grads["dW1"] = dW1;
    //     grads["dW2"] = dW2;
    //     grads["db1"] = db1;
    //     grads["db2"] = db2;

    //     return grads;
    // }

    // -----------------------------------------------------------
    // MultiLayerModel: Weight Decay の検証用
    // 【21/04/06】
    // とりあえず動作することを目指すので、最初はreluで構成
    // 後ほどsigmoid含めて動くように変更。初期値も XavierとHeの両方を選択できるようにする。
    // -----------------------------------------------------------

    MultiLayerModel::MultiLayerModel(const int input_size,
                                     const vector<int> hidden_size,
                                     const int output_size,
                                     const double weight_decay_lambda,
                                     string activation,
                                     const string weight_initializer,
                                     const bool use_dropout,
                                     const double dropout_ratio,
                                     const bool use_batchnorm)
    {
        _input_size = input_size;
        _hidden_size_list = hidden_size;
        _output_size = output_size;
        _weight_decay_lambda = weight_decay_lambda;

        _all_size_list.insert(_all_size_list.end(), {_input_size});
        _all_size_list.insert(_all_size_list.end(), _hidden_size_list.begin(), _hidden_size_list.end());
        _all_size_list.insert(_all_size_list.end(), {_output_size});

        std::transform(activation.begin(), activation.end(), activation.begin(), ::tolower);

        // Create Layers
        for (int i = 1; i < _hidden_size_list.size()+1; i++)
        {
            string tmp_num_str = std::to_string(i);
            _layers["Affine" + tmp_num_str] = make_shared<MyDL::Affine>(_all_size_list[i-1], _all_size_list[i]);
            _layer_list.push_back("Affine" + tmp_num_str);

            // BatchNormalization
            if (use_batchnorm)
            {
                _layers["BatchNorm" + tmp_num_str] = make_shared<BatchNorm>(_all_size_list[i], 0.9); // パラメータも指定できるようにする？
                _layer_list.push_back("BatchNorm" + tmp_num_str);
            }

            // Activation
            if (activation == "relu")
            {
                _layers["ReLU" + tmp_num_str] = make_shared<ReLU>();
                _layer_list.push_back("ReLU" + tmp_num_str);
            }
            else if(activation == "sigmoid")
            {
                _layers["Sigmoid" + tmp_num_str] = make_shared<Sigmoid>();
                _layer_list.push_back("Sigmoid" + tmp_num_str);
            }

            // Dropout
            if (use_dropout)
            {
                _layers["Dropout" + tmp_num_str] = make_shared<Dropout>(dropout_ratio);
                _layer_list.push_back("Dropout" + tmp_num_str);
            }

        }
        int last_num = _hidden_size_list.size()+1;
        string last_num_str = std::to_string(last_num);
        _layers["Affine" + last_num_str] = make_shared<MyDL::Affine>(_all_size_list[last_num-1], _all_size_list[last_num]);
        _layer_list.push_back("Affine" + last_num_str);

        _last_layer = make_shared<SoftmaxWithLoss>(); // Loss Layer

        // Get pointer to Layer Parameters
        for (int layer_num = 1; layer_num < _all_size_list.size(); layer_num++)
        {
            if (auto cast_affine = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine" + std::to_string(layer_num)]))
            {
                params["W" + std::to_string(layer_num)] = cast_affine->pW;
                params["b" + std::to_string(layer_num)] = cast_affine->pb;
            }
        }

        // Affine Layerの変数初期化
        this->_init_weight(weight_initializer);

    }

    void MultiLayerModel::_init_weight(string weight_initializer)
    {
        // weight_initializerの文字列を小文字に変換
        std::transform(weight_initializer.begin(),
                       weight_initializer.end(),
                       weight_initializer.begin(),
                       ::tolower);

        double scale = 1;

        if (weight_initializer == "relu" or weight_initializer == "he")
        {
            for (int layer_num = 1; layer_num <= _hidden_size_list.size()+1; layer_num++)
            {
                scale = sqrt(2.0 / _all_size_list[layer_num-1]);
                *(params["W" + std::to_string(layer_num)]) = scale * MatrixXd::Random(_all_size_list[layer_num-1], _all_size_list[layer_num]);
            }
        }
        else if (weight_initializer == "sigmoid" or weight_initializer == "xavier")
        {
            for (int layer_num = 1; layer_num <= _hidden_size_list.size() + 1; layer_num++)
            {
                scale = sqrt(1.0 / _all_size_list[layer_num - 1]);
                *(params["W" + std::to_string(layer_num)]) = scale * MatrixXd::Random(_all_size_list[layer_num - 1], _all_size_list[layer_num]);
            }
        }
    }

    vector<MatrixXd> MultiLayerModel::predict(vector<MatrixXd> inputs)
    {
        vector<MatrixXd> X = inputs;
        vector<MatrixXd> tmp_X;

        for (auto layer : _layer_list)
        {
            tmp_X = _layers[layer]->forward(X);
            X.swap(tmp_X);
        }
        return X;
    }

    vector<MatrixXd> MultiLayerModel::loss(vector<MatrixXd> inputs, MatrixXd &t)
    {
        vector<MatrixXd> pred_input, pred_out, loss_inputs, loss_output;
        pred_input.push_back(inputs[0]);
        pred_out = predict(pred_input);

        loss_inputs.push_back(pred_out[0]);
        loss_inputs.push_back(t);

        loss_output = _last_layer->forward(loss_inputs);

        // あとは Weight Decay の項も計算してLossに加える
        double weight_decay = 0;
        for (auto param : params)
        {
            weight_decay += 0.5 * _weight_decay_lambda * (*(param.second)).sum();
        }

        loss_output[0](0) = loss_output[0](0) + weight_decay;

        return loss_output;
    }

    double MultiLayerModel::accuracy(vector<MatrixXd> inputs, MatrixXd &t)
    {
        vector<MatrixXd> pred_out;
        pred_out = predict(inputs);

        MatrixXd y;
        y = pred_out[0];
        double batch_size = t.rows();
        double accuracy = 0;

        MatrixXd::Index y_row, y_col, t_row, t_col;
        for (int i = 0; i < batch_size; i++)
        {
            y.row(i).maxCoeff(&y_row, &y_col);
            t.row(i).maxCoeff(&t_row, &t_col);

            accuracy += (double)(y_col == t_col);
        }

        return accuracy / batch_size;
    }

    unordered_map<string, MatrixXd> MultiLayerModel::gradient(vector<MatrixXd> inputs, MatrixXd &t)
    {
        // Forward
        vector<MatrixXd> output;
        output = loss(inputs, t);

        // Backward
        vector<MatrixXd> dout, tmp_dout;
        dout.push_back(MatrixXd::Ones(1, 1));

        dout = _last_layer->backward(dout);

        for (auto it = _layer_list.rbegin(); it != _layer_list.rend(); it++)
        {
            string layer = *it;
            tmp_dout = _layers[layer]->backward(dout);
            dout.swap(tmp_dout);
        }

        unordered_map<string, MatrixXd> grads;

        for (int i = 1; i <= _hidden_size_list.size() + 1; i++)
        {
            if (auto tmp_affine = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine" + std::to_string(i)]))
            {
                grads["W" + std::to_string(i)] = tmp_affine->dW + _weight_decay_lambda * (*(tmp_affine->pW));
                grads["b" + std::to_string(i)] = tmp_affine->db;
            }
        }

        return grads;
    }

    // -----------------------------------------------------------
    // SimpleConvModel: Convolutionの検証用
    // -----------------------------------------------------------

    SimpleConvModel::SimpleConvModel(const int input_channels,
                                     const int input_height,
                                     const int input_width,
                                     const int filter_num,
                                     const int filter_size,
                                     const int pad,
                                     const int stride,
                                     const int hidden_size,
                                     const int output_size,
                                     const double weight_init_std)
    : _C(input_channels), _H(input_height), _W(input_width), _filter_num(filter_num), _filter_size(filter_size), _pad(pad), _stride(stride), _hidden_size(hidden_size), _output_size(output_size)
    {
        int Oh = (_pad*2 + _H - _filter_size) / _stride + 1;
        int Ow = (_pad*2 + _W - _filter_size) / _stride + 1;
        int Ph = 2;
        int Pw = 2; // Poolingのサイズは固定
        int p_stride = 2;
        int p_pad = 0;

        int pool_output_size = _filter_num * (Oh / 2) * (Ow / 2);

        _layers["Conv1"] = make_shared<Conv2D>(_C, _H, _W, _filter_size, _filter_size, _filter_num, _stride, _pad, weight_init_std);
        _layers["Relu1"] = make_shared<ReLU>();
        _layers["Pool1"] = make_shared<Pooling>(_filter_num, Oh, Ow, Ph, Pw, p_stride, p_pad);
        _layers["Affine1"] = make_shared<MyDL::Affine>(pool_output_size, _hidden_size, weight_init_std);
        _layers["Affine2"] = make_shared<MyDL::Affine>(hidden_size, output_size, weight_init_std);

        _last_layer = make_shared<SoftmaxWithLoss>();

        _layer_list.push_back("Conv1");
        _layer_list.push_back("Relu1");
        _layer_list.push_back("Pool1");
        _layer_list.push_back("Affine1");
        _layer_list.push_back("Affine2");

        if (auto cast_conv = std::dynamic_pointer_cast<Conv2D>(_layers["Conv1"]))
        {
            params["W1"] = cast_conv->pW;
            params["b1"] = cast_conv->pb;
        }

        if (auto cast_affine1 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine1"]))
        {
            params["W2"] = cast_affine1->pW;
            params["b2"] = cast_affine1->pb;
        }

        if (auto cast_affine2 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine2"]))
        {
            params["W3"] = cast_affine2->pW;
            params["b3"] = cast_affine2->pb;
        }

    }

    vector<MatrixXd> SimpleConvModel::predict(vector<MatrixXd> inputs)
    {
        vector<MatrixXd> X = inputs;
        vector<MatrixXd> tmp_X;

        for (auto layer : _layer_list)
        {
            tmp_X = _layers[layer]->forward(X);
            X.swap(tmp_X);
        }
        return X;
    }

    vector<MatrixXd> SimpleConvModel::loss(vector<MatrixXd> inputs, MatrixXd &t)
    {
        vector<MatrixXd> pred_input, pred_out, loss_inputs, loss_output;
        pred_input.push_back(inputs[0]);
        pred_out = predict(pred_input);

        loss_inputs.push_back(pred_out[0]);
        loss_inputs.push_back(t);

        loss_output = _last_layer->forward(loss_inputs);

        return loss_output;
    }

    double SimpleConvModel::accuracy(vector<MatrixXd> inputs, MatrixXd &t)
    {
        vector<MatrixXd> pred_out;
        pred_out = predict(inputs);

        MatrixXd y;
        y = pred_out[0];
        double batch_size = t.rows();
        double accuracy = 0;

        MatrixXd::Index y_row, y_col, t_row, t_col;
        for (int i = 0; i < batch_size; i++)
        {
            y.row(i).maxCoeff(&y_row, &y_col);
            t.row(i).maxCoeff(&t_row, &t_col);

            accuracy += (double)(y_col == t_col);
        }

        return accuracy / batch_size;
    }

    unordered_map<string, MatrixXd> SimpleConvModel::gradient(vector<MatrixXd> inputs, MatrixXd &t)
    {
        // Forward
        vector<MatrixXd> output;
        output = loss(inputs, t);

        // Backward
        vector<MatrixXd> dout, tmp_dout;
        dout.push_back(MatrixXd::Ones(1, 1));

        dout = _last_layer->backward(dout);

        for (auto it = _layer_list.rbegin(); it != _layer_list.rend(); it++)
        {
            string layer = *it;
            tmp_dout = _layers[layer]->backward(dout);
            dout.swap(tmp_dout);
        }

        unordered_map<string, MatrixXd> grads;

        if (auto cast_conv = std::dynamic_pointer_cast<Conv2D>(_layers["Conv1"]))
        {
            grads["W1"] = cast_conv->dW;
            grads["b1"] = cast_conv->db;
        }

        if (auto cast_affine1 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine1"]))
        {
            grads["W2"] = cast_affine1->dW;
            grads["b2"] = cast_affine1->db;
        }

        if (auto cast_affine2 = std::dynamic_pointer_cast<MyDL::Affine>(_layers["Affine2"]))
        {
            grads["W3"] = cast_affine2->dW;
            grads["b3"] = cast_affine2->db;
        }

        return grads;
    }
}


#pragma once

#pragma warning(push)
#pragma warning(disable: 4819)
#include <boost/geometry/core/access.hpp>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/geometry/geometries/multi_point.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>

#include <boost/geometry/strategies/strategies.hpp>
#pragma warning(pop)

namespace ompu { namespace geo {

namespace bg = boost::geometry;

using Point = bg::model::d2::point_xy<double>;
using MultiPoint = bg::model::multi_point<Point>;

using Polygon = bg::model::polygon<Point>;
using MultiPolygon = bg::model::multi_polygon<Polygon>;
using Box = bg::model::box<Point>;

struct Size
{
    unsigned w, h;
};

struct CircleSize
{
    bg::strategy::buffer::distance_symmetric<double>
    radius;
};

}} // ompu


//==================================================================================================
/*
  Copyright 2017 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)
*/
//==================================================================================================
//! [direct_trigonometric]
#include <boost/simd/trigonometric.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/function/enumerate.hpp>
#include <boost/simd/constant/pi.hpp>
#include <iostream>

namespace bs =  boost::simd;
using pack_ft =  bs::pack <float, 8>;

int main()
{
  pack_ft p_pi = bs::enumerate<pack_ft>(-1.0f, 0.25f);
  pack_ft p_ra = p_pi*bs::Pi<pack_ft>();
  pack_ft p_dg = bs::indeg(p_ra);
  std::cout << " p_ra =  " << p_ra << std::endl
            << " p_pi =  " << p_pi << std::endl
            << " p_dg =  " << p_dg << std::endl
            << " -> bs::cos(p_ra) =   " << bs::cos(p_ra)   << std::endl
            << " -> bs::cospi(p_pi) = " << bs::cospi(p_pi) << std::endl
            << " -> bs::cosd(p_dg) =  " << bs::cosd(p_dg)  << std::endl;
  return 0;
}
//! [direct_trigonometric]


#include "gr/algorithms/match4pcsBase.h"
#include "gr/algorithms/FunctorSuper4pcs.h"
#include "gr/utils/geometry.h"
#include <gr/algorithms/PointPairFilter.h>

#include <Eigen/Dense>

int main(int argc, char **argv) {
  using namespace gr;
  using namespace std;

  using TrVisitor = gr::DummyTransformVisitor;

  using MatcherType = gr::Match4pcsBase<gr::FunctorSuper4PCS, gr::Point3D<float>, TrVisitor, gr::AdaptivePointFilter, gr::AdaptivePointFilter::Options>;
  using OptionType  = typename MatcherType::OptionsType;
  using SamplerType = gr::UniformDistSampler<gr::Point3D<float> >;

  vector<Point3D<float> > set1, set2;
  vector<Eigen::Matrix2f> tex_coords1, tex_coords2;
  vector<typename Point3D<float>::VectorType> normals1, normals2;
  vector<std::string> mtls1, mtls2;

  // dummy calls, to test symbols accessibility
  // check availability of the Utils functions
  Utils::CleanInvalidNormals(set1, normals1);

  // Our matcher.
  OptionType options;

  // Set parameters.
  typename MatcherType::MatrixType mat;
  double overlap (1);
  options.configureOverlap(overlap);

  typename Point3D<float>::Scalar score = 0;

  constexpr Utils::LogLevel loglvl = Utils::Verbose;
  Utils::Logger logger(loglvl);
  SamplerType sampler;
  TrVisitor visitor;

  MatcherType matcher(options, logger);
  score = matcher.ComputeTransformation(set1, set2, mat, sampler, visitor);

  logger.Log<Utils::Verbose>( "Score: ", score );

  return 0;
}


#include <string>
#include <cassert>
#include <map>
#include <cstdint>
#include <vector>
#include <boost/algorithm/string.hpp>
#include "crypto/crypto.h"  // for declaration of crypto::secret_key

#include "crypto/electrum-words.h"

namespace crypto
{
  namespace ElectrumWords
  {

    /* convert words to bytes, 3 words -> 4 bytes
     * returns:
     *    false if not a multiple of 3 words, or if a words is not in the
     *    words list
     *
     *    true otherwise
     */
    bool words_to_bytes(const std::string& words, crypto::secret_key& dst)
    {
      int n = NUMWORDS; // hardcoded because this is what electrum uses

      std::vector<std::string> wlist;

      boost::split(wlist, words, boost::is_any_of(" "));

      // error on non-compliant word list
      if (wlist.size() != 12 && wlist.size() != 24) return false;

      for (unsigned int i=0; i < wlist.size() / 3; i++)
      {
        uint32_t val;
        uint32_t w1, w2, w3;

        // verify all three words exist in the word list
        if (wordsMap.count(wlist[i*3]) == 0 ||
            wordsMap.count(wlist[i*3 + 1]) == 0 ||
            wordsMap.count(wlist[i*3 + 2]) == 0)
        {
          return false;
        }

        w1 = wordsMap.at(wlist[i*3]);
        w2 = wordsMap.at(wlist[i*3 + 1]);
        w3 = wordsMap.at(wlist[i*3 + 2]);

        val = w1 + n * (((n - w1) + w2) % n) + n * n * (((n - w2) + w3) % n);

        if (!(val % n == w1)) return false;

        memcpy(dst.data + i * 4, &val, 4);  // copy 4 bytes to position
      }

      std::string wlist_copy = words;
      if (wlist.size() == 12)
      {
        memcpy(dst.data, dst.data + 16, 16);  // if electrum 12-word seed, duplicate
        wlist_copy += ' ';
        wlist_copy += words;
      }

      return true;
    }

    /* convert bytes to words, 4 bytes-> 3 words
     * returns:
     *    false if wrong number of bytes (shouldn't be possible)
     *    true otherwise
     */
    bool bytes_to_words(const crypto::secret_key& src, std::string& words)
    {
      int n = NUMWORDS; // hardcoded because this is what electrum uses

      if (sizeof(src.data) % 4 != 0) return false;

      // 8 bytes -> 3 words.  8 digits base 16 -> 3 digits base 1626
      for (unsigned int i=0; i < sizeof(src.data)/4; i++, words += ' ')
      {
        uint32_t w1, w2, w3;

        uint32_t val;

        memcpy(&val, (src.data) + (i * 4), 4);

        w1 = val % n;
        w2 = ((val / n) + w1) % n;
        w3 = (((val / n) / n) + w2) % n;

        words += wordsArray[w1];
        words += ' ';
        words += wordsArray[w2];
        words += ' ';
        words += wordsArray[w3];
      }
      return false;
    }

  }  // namespace ElectrumWords

}  // namespace crypto


// clang++ -std=c++11 main.cpp -I include -I $LIBIGL/include -I /usr/local/libigl/external/eigen/ -framework OpenGL -L/usr/local/lib/ -lglfw && ./a.out
//

// make sure the modern opengl headers are included before any others
#include "gl.h"
#define GLFW_INCLUDE_GLU
#include <GLFW/glfw3.h>

#include "read_json.h"
#include "icosahedron.h"
#include "mesh_to_vao.h"
#include "print_opengl_info.h"
#include "get_seconds.h"
#include "report_gl_error.h"
#include "create_shader_program_from_files.h"
#include "last_modification_time.h"
#ifdef USE_SOLUTION
#  include "find_and_replace_all.h"
#endif

#include <Eigen/Core>
#include <Eigen/Geometry>

#include <thread>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <vector>

// Default width and height
bool wire_frame = false;
bool mouse_down = false;
bool is_animating = true;
double last_time = get_seconds();
double animation_seconds = 0;
int width =  640;
int height = 360;
// Whether display has high dpi (e.g., Mac retinas)
int highdpi = 1;
GLuint prog_id=0;

Eigen::Affine3f view =
  Eigen::Affine3f::Identity() *
  Eigen::Translation3f(Eigen::Vector3f(0,0,-10));
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();

GLuint VAO;
// Mesh data: RowMajor is important to directly use in OpenGL
Eigen::Matrix< float,Eigen::Dynamic,3,Eigen::RowMajor> V;
Eigen::Matrix<GLuint,Eigen::Dynamic,3,Eigen::RowMajor> F;

int main(int argc, char * argv[])
{

  std::vector<std::string> vertex_shader_paths;
  std::vector<std::string> tess_control_shader_paths;
  std::vector<std::string> tess_evaluation_shader_paths;
  std::vector<std::string> fragment_shader_paths;

  // Initialize glfw window
  if(!glfwInit())
  {
    std::cerr<<"Could not initialize glfw"<<std::endl;
     return EXIT_FAILURE;
  }
  const auto & error = [] (int error, const char* description)
  {
    std::cerr<<description<<std::endl;
  };
  glfwSetErrorCallback(error);
  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  GLFWwindow* window = glfwCreateWindow(width, height, "shader-pipeline", NULL, NULL);
  if(!window)
  {
    glfwTerminate();
    std::cerr<<"Could not create glfw window"<<std::endl;
    return EXIT_FAILURE;
  }
  std::cout<<R"(
Usage:
  [Click and drag]  to orbit view
  [Scroll]  to translate view in and out
  A,a  toggle animation
  L,l  toggle wireframe rending
  Z,z  reset view to look along z-axis
)";
  glfwSetWindowPos(window,0,0);
  glfwMakeContextCurrent(window);
  // Load OpenGL and its extensions
  if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
  {
    std::cerr<<"Failed to load OpenGL and its extensions"<<std::endl;
    return EXIT_FAILURE;
  }
  print_opengl_info(window);
  igl::opengl::report_gl_error("init");

  icosahedron(V,F);
  mesh_to_vao(V,F,VAO);
  igl::opengl::report_gl_error("mesh_to_vao");

  const auto & reshape = [](
    GLFWwindow* window,
    int _width,
    int _height)
  {
    ::width=_width,::height=_height;

    // augh, windows can't handle variables named near and far.
    float nearVal = 0.01;
    float farVal = 100;
    float top = tan(35./360.*M_PI)*nearVal;
    float right = top * (double)::width/(double)::height;
    float left = -right;
    float bottom = -top;
    proj.setConstant(4,4,0.);
    proj(0,0) = (2.0 * nearVal) / (right - left);
    proj(1,1) = (2.0 * nearVal) / (top - bottom);
    proj(0,2) = (right + left) / (right - left);
    proj(1,2) = (top + bottom) / (top - bottom);
    proj(2,2) = -(farVal + nearVal) / (farVal - nearVal);
    proj(3,2) = -1.0;
    proj(2,3) = -(2.0 * farVal * nearVal) / (farVal - nearVal);
  };
  // Set up window resizing
  glfwSetWindowSizeCallback(window,reshape);
  {
    int width_window, height_window;
    glfwGetWindowSize(window, &width_window, &height_window);
    reshape(window,width_window,height_window);
  }

  // Close the window if user presses ESC or CTRL+C
  glfwSetKeyCallback(
    window,
    [](GLFWwindow* window, int key, int scancode, int action, int mods)
    {
      if(key == 256 || (key == 67 && (mods & GLFW_MOD_CONTROL)))
      {
        glfwSetWindowShouldClose(window,true);
      }
    });
  glfwSetCharModsCallback(
    window,
    [](GLFWwindow* window, unsigned int codepoint, int modifier)
    {
      switch(codepoint)
      {
        case 'A':
        case 'a':
          is_animating ^= 1;
          if(is_animating)
          {
            last_time = get_seconds();
          }
          break;
        case 'L':
        case 'l':
          wire_frame ^= 1;
          if (wire_frame) {
            glDisable(GL_CULL_FACE);
          } else {
            glEnable(GL_CULL_FACE);
          }
          break;
        case 'Z':
        case 'z':
          view.matrix().block(0,0,3,3).setIdentity();
          break;
        default:
          std::cout<<"Unrecognized key: "<<(unsigned char) codepoint<<std::endl;
          break;
      }
    });
  glfwSetMouseButtonCallback(
    window,
    [](GLFWwindow * window, int button, int action, int mods)
    {
      mouse_down = action == GLFW_PRESS;
    });
  glfwSetCursorPosCallback(
    window,
    [](GLFWwindow * window, double x, double y)
    {
      static double mouse_last_x = x;
      static double mouse_last_y = y;
      double dx = x-mouse_last_x;
      double dy = y-mouse_last_y;
      if(mouse_down)
      {
        // Two axis valuator with fixed up
        float factor = std::abs(view.matrix()(2,3));
        view.rotate(
          Eigen::AngleAxisf(
            dx*factor/float(width),
            Eigen::Vector3f(0,1,0)));
        view.rotate(
          Eigen::AngleAxisf(
            dy*factor/float(height),
            view.matrix().topLeftCorner(3,3).inverse()*Eigen::Vector3f(1,0,0)));
      }
      mouse_last_x = x;
      mouse_last_y = y;
    });
  glfwSetScrollCallback(window,
    [](GLFWwindow * window, double xoffset, double yoffset)
    {
      view.matrix()(2,3) =
        std::min(std::max(view.matrix()(2,3)+(float)yoffset,-100.0f),-2.0f);
    });

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  // Force compilation on first iteration through loop
  double time_of_last_shader_compilation = 0;
  double time_of_last_json_load = 0;
  const auto any_changed =
    [](
        const std::vector<std::string> &paths,
        const double time_of_last_shader_compilation
        )->bool
  {
    for(const auto & path : paths)
    {
      if(last_modification_time(path) > time_of_last_shader_compilation)
      {
        std::cout<<path<<" has changed since last compilation attempt."<<std::endl;
        return true;
      }
    }
    return false;
  };

  float start_time = get_seconds();
  // Main display routine
  while (!glfwWindowShouldClose(window))
  {
    double tic = get_seconds();

    if(any_changed({argv[1]},time_of_last_json_load))
    {
      std::cout<<"-----------------------------------------------"<<std::endl;
      time_of_last_json_load = get_seconds();
      if(!read_json(argv[1],
            vertex_shader_paths,
            tess_control_shader_paths,
            tess_evaluation_shader_paths,
            fragment_shader_paths))
      {
        std::cerr<<"Failed to read "<<argv[1]<<std::endl;
      }
#ifdef USE_SOLUTION
      {
        const auto replace_all = [](std::vector<std::string> & paths)
        {
          for(auto & path : paths)
          {
            find_and_replace_all("/src/","/solution/",path);
          }
        };
        replace_all(vertex_shader_paths);
        replace_all(tess_control_shader_paths);
        replace_all(tess_evaluation_shader_paths);
        replace_all(fragment_shader_paths);
      }
#endif
      // force reload of shaders
      time_of_last_shader_compilation = 0;
    }
    if(
      any_changed(vertex_shader_paths         ,time_of_last_shader_compilation) ||
      any_changed(tess_control_shader_paths   ,time_of_last_shader_compilation) ||
      any_changed(tess_evaluation_shader_paths,time_of_last_shader_compilation) ||
      any_changed(fragment_shader_paths       ,time_of_last_shader_compilation))
    {
      std::cout<<"-----------------------------------------------"<<std::endl;
      // remember the time we tried to compile
      time_of_last_shader_compilation = get_seconds();
      if(
          !create_shader_program_from_files(
            vertex_shader_paths,
            tess_control_shader_paths,
            tess_evaluation_shader_paths,
            fragment_shader_paths,
            prog_id))
      {
        // Force null shader to visually indicate failure
        glDeleteProgram(prog_id);
        prog_id = 0;
        std::cout<<"-----------------------------------------------"<<std::endl;
      }
    }

    // clear screen and set viewport
    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glfwGetFramebufferSize(window, &::width, &::height);
    glViewport(0,0,::width,::height);
    // select program
    glUseProgram(prog_id);
    // Attach uniforms
    {
      if(is_animating)
      {
        double now = get_seconds();
        animation_seconds += now - last_time;
        last_time = now;
      }
      glUniform1f(glGetUniformLocation(prog_id,"animation_seconds"),animation_seconds);
    }
    glUniformMatrix4fv(
      glGetUniformLocation(prog_id,"proj"),1,false,proj.data());
    glUniformMatrix4fv(
      glGetUniformLocation(prog_id,"view"),1,false,view.matrix().data());
    // Draw mesh as wireframe
    if(wire_frame)
    {
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }else
    {
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    for(int i = 0;i<2;i++)
    {
      glUniform1i(glGetUniformLocation(prog_id, "is_moon"), i==1);
      glBindVertexArray(VAO);
      glDrawElements(GL_PATCHES, F.size(), GL_UNSIGNED_INT, 0);
      glBindVertexArray(0);
    }

    glfwSwapBuffers(window);

    // 60 fps
    {
      glfwPollEvents();
      // In microseconds
      double duration = 1000000.*(get_seconds()-tic);
      const double min_duration = 1000000./60.;
      if(duration<min_duration)
      {
        std::this_thread::sleep_for(std::chrono::microseconds((int)(min_duration-duration)));
      }
    }
  }

  // Graceful exit
  glfwDestroyWindow(window);
  glfwTerminate();
  return EXIT_SUCCESS;
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Contributed and/or modified by Tinko Bartels,
//   as part of Google Summer of Code 2019 program.

// Use, modification and distribution is 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)

#include <geometry_test_common.hpp>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/extensions/triangulation/strategies/cartesian/side_robust.hpp>

template <typename P>
void test_all()
{
    typedef bg::strategy::side::side_robust<double, 3> side3;
    typedef bg::strategy::side::side_robust<double, 2> side2;
    typedef bg::strategy::side::side_robust<double, 1> side1;
    typedef bg::strategy::side::side_robust<double, 0> side0;

    P col1(1.0, 1.0), col2(2.0, 2.0), col3(3.0, 3.0);
    int col3r = side3::apply(col1, col2, col3);
    BOOST_CHECK_EQUAL(0, col3r);
    int col2r = side2::apply(col1, col2, col3);
    BOOST_CHECK_EQUAL(0, col2r);
    int col1r = side1::apply(col1, col2, col3);
    BOOST_CHECK_EQUAL(0, col1r);
    int col0r = side0::apply(col1, col2, col3);
    BOOST_CHECK_EQUAL(0, col0r);

    P easy1(0.0, 0.0), easy2(1.0, 1.0), easy3(0.0, 1.0);
    int easy3r = side3::apply(easy1, easy2, easy3);
    BOOST_CHECK_GT(easy3r, 0);
    int easy2r = side2::apply(easy1, easy2, easy3);
    BOOST_CHECK_GT(easy2r, 0);
    int easy1r = side1::apply(easy1, easy2, easy3);
    BOOST_CHECK_GT(easy1r, 0);
    int easy0r = side0::apply(easy1, easy2, easy3);
    BOOST_CHECK_GT(easy0r, 0);

    P medium1(1.0, 1.0), medium2(1.0e20, 1.0e20), medium3(1.0, 2.0);
    int medium3r = side3::apply(medium1, medium2, medium3);
    BOOST_CHECK_GT(medium3r, 0);
    int medium2r = side2::apply(medium1, medium2, medium3);
    BOOST_CHECK_GT(medium2r, 0);

    P hard1(1.0e-20, 1.0e-20), hard2(1.0e20, 1.0e20), hard3(1.0, 2.0);
    int hard3r = side3::apply(hard1, hard2, hard3);
    BOOST_CHECK_GT(hard3r, 0);
}

int test_main(int, char* [])
{
    test_all<bg::model::d2::point_xy<double> >();
    return 0;
}


#include "FileIO.h"
#include <math.h>
#include "Types.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <vector>
#include <iostream>
#include <sstream>

/**
 * Class for loading and saving for 3d modeler
 *
 * @author Ryan Hamilton
 */

/**
 * Parses geometric vertex from line of OBJ file
 *
 * @param str - string containing geometric vertex
 *        refGeometricVertices - referance to a 2d vector for storing geometric vertex
 */
void FileIO::mParseGeometricVertex( std::string str, std::vector< std::vector< double > > &refGeometricVertices )
{
	double x, y, z;
	std::stringstream ss( str );
	char temp;
	ss >> temp;
	ss >> x;
	ss >> y;
	ss >> z;

	refGeometricVertices[ refGeometricVertices.size( ) - 1 ].push_back( x );
	refGeometricVertices[ refGeometricVertices.size( ) - 1 ].push_back( y );
	refGeometricVertices[ refGeometricVertices.size( ) - 1 ].push_back( z );
}

 /**
 * Parses texture coordinates from line of OBJ file
 *
 * @param str - string containing texture coordinates
 *        refTextureCoordinates - referance to a 2d vector for storing texture coordinates
 */
 void FileIO::mParseTextureCoordinates( std::string str, std::vector< std::vector< double > > &refTextureCoordinates )
{
	double x, y;
	std::stringstream ss( str );
	char temp;
	ss >> temp;
	ss >> temp;
	ss >> x;
	ss >> y;

	refTextureCoordinates[ refTextureCoordinates.size( ) - 1 ].push_back( x );
	refTextureCoordinates[ refTextureCoordinates.size( ) - 1 ].push_back( y );
}

/**
 * Parses Normal Vertex from line of OBJ file
 *
 * @param str - string containing normal vertex
 *        refNormalVertices - referance to a 2d vector for storing Gnormal Vertices
 */
void FileIO::mParseNormalVertex( std::string str, std::vector< std::vector< double > > &refNormalVertices )
{
	double x, y, z;
	std::stringstream ss( str );
	char temp;
	ss >> temp;
	ss >> temp;
	ss >> x;
	ss >> y;
	ss >> z;

	refNormalVertices[ refNormalVertices.size( ) - 1 ].push_back( x );
	refNormalVertices[ refNormalVertices.size( ) - 1 ].push_back( y );
	refNormalVertices[ refNormalVertices.size( ) - 1 ].push_back( z );

}

/**
 * Parses face elements from line of OBJ file
 *
 * @param str - string containingface elements
 *        refFaceElements - referance to a 2d vector for storing face elements
 */
 void FileIO::mParseFaceElements( std::string str, std::vector< std::vector< std::vector< int > > > &refFaceElements )
{
	int v1, v2, v3;
	std::stringstream ss( str );
	uint FirstIndexOfLine = refFaceElements.size();
	uint index = 0;
	char temp;
	ss >> temp;
	while( ss.tellg() != -1 )
	{
		if(index > 2 || index == 0)
		{
			std::vector< int > vertexSet1;
			std::vector< int > vertexSet2;
			std::vector< int > vertexSet3;

			std::vector< std::vector< int > > faceVertices;

			faceVertices.push_back( vertexSet1 );
			faceVertices.push_back( vertexSet2 );
			faceVertices.push_back( vertexSet3 );

			refFaceElements.push_back( faceVertices );
		}
			ss >> v1;

			if( ss.peek( ) == '/' )
				ss >> temp;

			if( ss.peek( ) != '/' && ss.peek( ) != ' ' )
				ss >> v2;
			else
				v2 = 0;

			if( ss.peek( ) == '/' )
				ss >> temp;

			if( ss.peek( ) != '/' && ss.peek( ) != ' ' )
				ss >> v3;
			else
				v3 = 0;

			if(index > 2)
			{
				refFaceElements[refFaceElements.size( ) - 1][0].push_back(refFaceElements[FirstIndexOfLine][0].at(0));
				refFaceElements[refFaceElements.size( ) - 1][0].push_back(refFaceElements[FirstIndexOfLine][0].at(1));
				refFaceElements[refFaceElements.size( ) - 1][0].push_back(refFaceElements[FirstIndexOfLine][0].at(2));

				refFaceElements[refFaceElements.size( ) - 1][1].push_back(refFaceElements[refFaceElements.size( ) - 2][2].at(0));
				refFaceElements[refFaceElements.size( ) - 1][1].push_back(refFaceElements[refFaceElements.size( ) - 2][2].at(1));
				refFaceElements[refFaceElements.size( ) - 1][1].push_back(refFaceElements[refFaceElements.size( ) - 2][2].at(2));

				refFaceElements[ refFaceElements.size( ) - 1 ][ 2 ].push_back( v1 );
				refFaceElements[ refFaceElements.size( ) - 1 ][ 2 ].push_back( v2 );
				refFaceElements[ refFaceElements.size( ) - 1 ][ 2 ].push_back( v3 );

				index = index + 1;
			}
			else
			{
				refFaceElements[ refFaceElements.size( ) - 1 ][ index ].push_back( v1 );
				refFaceElements[ refFaceElements.size( ) - 1 ][ index ].push_back( v2 );
				refFaceElements[ refFaceElements.size( ) - 1 ][ index ].push_back( v3 );

				index = index + 1;
			}

	}
}

 /**
  * Save 3d model as a .obj
  *
  * @param p - A path
  */
 void FileIO::SaveObj(std::string filename)
 {
	 // Adds the obj file extension to the filename
	  	std::string file = filename + ".obj";
	  	boost::filesystem::ofstream outputFile;
	  	// Opens the output file for writing
	  	outputFile.open(file);

	  	// Check to see if any vertices were read in, if not then skips outputing this line
	  	if ( (*mGeometricVertices).size() > 0 ) {
	  		outputFile << "# Vertices \n";
	  	}

	  	// Vertices written to a .obj file.
	  	for ( uint i = 0; i < (*mGeometricVertices).size(); i++ ) {
	  		outputFile << "v " << (*mGeometricVertices)[i].at(0) << " " << (*mGeometricVertices)[i].at(1) << " " << (*mGeometricVertices)[i].at(2) << "\n";
	  	}

	  	// Check to see if any textures were read in, if not then skips outputing this line
	  	if ( (*mTextureCoordinates).size() > 0 ) {
	  		outputFile << "\n";
	  		outputFile << "# Texture Coordinates \n";
	  	}

	  	// Texture Coordinates written to a .obj file.
	  	for ( uint i = 0; i < (*mTextureCoordinates).size(); i++ ) {
	  		outputFile << "vt " << (*mTextureCoordinates)[i].at(0) << " " << (*mTextureCoordinates)[i].at(1) << "\n";
	  	}

	  	// Check to see if any normals were read in, if not then skips outputing this line
	  	if ( (*mNormalVertices).size() > 0 ) {
	  		outputFile << "\n";
	  		outputFile << "# Normals \n";
	  	}

	  	// Normal vertices written to a .obj file.
	  	for ( uint i = 0; i < (*mNormalVertices).size(); i++ ) {
	  		outputFile << "vn " << (*mNormalVertices)[i].at(0) << " " << (*mNormalVertices)[i].at(1) << " " << (*mNormalVertices)[i].at(2) << "\n";
	  	}

	  	// Check to see if any faces were read in, if not then skips outputing this line
	  	if ( (*mFaceElements).size() > 0 ) {
	  		outputFile << "\n";
	  		outputFile << "# Faces (vertex/texcoord/normal) \n";
	  	}

	  	// Faces written to a .obj file.
	  	for ( uint i = 0; i < (*mFaceElements).size(); i++ ) {
	  		// Cycles through each row of faces
	  		outputFile << "f ";
	  		for ( uint j = 0; j < 3; j++ ) {
	  			// Cycles through each elements of a line of faces
	  			// IF statements to check for zeros in face vectors
	  			if ( (*mFaceElements)[i][j].at(1) == 0 && (*mFaceElements)[i][j].at(2) == 0 && j < 2) {
	  				outputFile << (*mFaceElements)[i][j].at(0) << " ";
	  			} else if ( (*mFaceElements)[i][j].at(1) == 0 && (*mFaceElements)[i][j].at(2) == 0 ) {
	  				outputFile << (*mFaceElements)[i][j].at(0);
	  			} else if ( (*mFaceElements)[i][j].at(1) == 0 && j < 2 ) {
	  				outputFile << (*mFaceElements)[i][j].at(0) << "//" << (*mFaceElements)[i][j].at(2) << " ";
	  			} else if ( (*mFaceElements)[i][j].at(1) == 0 ) {
	  				outputFile << (*mFaceElements)[i][j].at(0) << "//" << (*mFaceElements)[i][j].at(2);
	  			} else if ( j < 2 ){ // Prevents extra space at end of line
	  				outputFile << (*mFaceElements)[i][j].at(0) << "/" << (*mFaceElements)[i][j].at(1) << "/" << (*mFaceElements)[i][j].at(2) << " ";
	  			} else {
	  				outputFile << (*mFaceElements)[i][j].at(0) << "/" << (*mFaceElements)[i][j].at(1) << "/" << (*mFaceElements)[i][j].at(2);
	  			}
	  		}
	  			outputFile << "\n";
	  	}

	  	// Closes the output file buffer
	  	outputFile.close();
 }

/**
 * Loads a .obj file and parses the file for the geometric vertices, texture coordinates, normal vertices, and face elements from the .obj file
 *
 * @param p - path of .obj file
 */
 /*
void FileIO::LoadObj(boost::filesystem::path p)
{
	boost::filesystem::ifstream File(p);
	std::string FileLine;

	//loops through entire .obj file
	while(std::getline(File, FileLine))
	{
		//std::cout<<FileLine<<"\n";
		if(FileLine.size() > 2){

			//checks for Geometric vertex coordinates
			if(FileLine.at(0) == 'v' && FileLine.at(1) == ' ')
			{
				double X, Y, Z;
				std::stringstream S(FileLine);
				char Temp;
				S >> Temp;
				S >> X;
				S >> Y;
				S >> Z;

				std::vector<double> GeometricVector;
				GeometricVector.push_back(X);
				GeometricVector.push_back(Y);
				GeometricVector.push_back(Z);

				mGeometricVertices.push_back(GeometricVector);
			}

			//checks for texture coordinates
			if(FileLine.at(0) == 'v' && FileLine.at(1) == 't')
			{
				double X, Y;
				std::stringstream S(FileLine);
				char Temp;
				char Temp2;
				S >> Temp;
				S >> Temp2;
				S >> X;
				S >> Y;

				std::vector<double> TextureVector;

				TextureVector.push_back(X);
				TextureVector.push_back(Y);

				mTextureCoordinates.push_back(TextureVector);
			}

			//checks for normal vertex coordinates
			if(FileLine.at(0) == 'v' && FileLine.at(1) == 'n')
			{
				double X, Y, Z;
				std::stringstream S(FileLine);
				char Temp;
				char Temp2;
				S >> Temp;
				S >> Temp2;
				S >> X;
				S >> Y;
				S >> Z;

				std::vector<double> NormalVector;

				NormalVector.push_back(X);
				NormalVector.push_back(Y);
				NormalVector.push_back(Z);

				mNormalVertices.push_back(NormalVector);
			}

			//checks for face elements
			if(FileLine.at(0) == 'f' && FileLine.at(1) == ' ')
			{
				int V1, V2, V3;
				std::stringstream S(FileLine);
				char Temp;
				S >> Temp;
				std::vector<int> VerticeVector1;
				std::vector<std::vector<int>> FaceVector;

				//parses through first set of vertices
				S >> V1;
				VerticeVector1.push_back(V1);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V2;
					VerticeVector1.push_back(V2);
				}
				else
					VerticeVector1.push_back(0);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V3;
					VerticeVector1.push_back(V3);
				}
				else
					VerticeVector1.push_back(0);

				FaceVector.push_back(VerticeVector1);
				std::vector<int> VerticeVector2;

				// parses through second set of vertices
				S >> V1;
				VerticeVector2.push_back(V1);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V2;
					VerticeVector2.push_back(V2);
				}
				else
					VerticeVector2.push_back(0);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V3;
					VerticeVector2.push_back(V3);
				}
				else
					VerticeVector2.push_back(0);

				FaceVector.push_back(VerticeVector2);
				std::vector<int> VerticeVector3;

				//parses through the third set of vertices
				S >> V1;
				VerticeVector3.push_back(V1);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V2;
					VerticeVector3.push_back(V2);
				}
				else
					VerticeVector3.push_back(0);

				if(S.peek() == '/')
					S >> Temp;

				if(S.peek() != '/' || S.peek() != ' ')
				{
					S >> V3;
					VerticeVector3.push_back(V3);
				}
				else
					VerticeVector3.push_back(0);

				FaceVector.push_back(VerticeVector3);
				mFaceElements.push_back(FaceVector);
			}
		}
	}
}
*/

/**
 * Loads a .obj file and parses the file for the geometric vertices, texture coordinates, normal vertices, and face elements from the .obj file
 *
 * @param p - path of .obj file
		  refGeometricVertices
		  refTextureCoordinates
		  refNormalVertices
		  refFaceElements
 */
void FileIO::LoadObj2( boost::filesystem::path p, std::vector< std::vector< double > > &refGeometricVertices,
		std::vector< std::vector< double > > &refTextureCoordinates, std::vector< std::vector<double > > &refNormalVertices,
		std::vector< std::vector< std::vector< int > > > &refFaceElements )
{
		boost::filesystem::ifstream file( p );
		std::string fileLine;

		while( std::getline( file, fileLine ) )
		{
			if( fileLine.size( ) > 2 )
			{

				if( fileLine.at( 0 ) == 'v' && fileLine.at( 1 ) == ' ' )
				{
					std::vector< double > geometricVertex;
					refGeometricVertices.push_back( geometricVertex );
					mParseGeometricVertex( fileLine, refGeometricVertices );
				}

				if( fileLine.at( 0 ) == 'v' && fileLine.at( 1 ) == 't' )
				{
					std::vector< double > TextureCoordinate;
					refTextureCoordinates.push_back( TextureCoordinate );
					mParseTextureCoordinates( fileLine, refTextureCoordinates );
				}

				if( fileLine.at( 0 ) == 'v' && fileLine.at( 1 ) == 'n' )
				{
					std::vector< double > NormalVertex;
					refNormalVertices.push_back( NormalVertex );
					mParseNormalVertex( fileLine, refNormalVertices );
				}

				if( fileLine.at( 0 ) == 'f' && fileLine.at( 1 ) == ' ' )
				{
					mParseFaceElements( fileLine, refFaceElements );
				}
			}
		}
}