#include "ale.h"

#include <nlohmann/json.hpp>

#include <gsl/gsl_interp.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_poly.h>

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

#include <iostream>
#include <Python.h>

#include <string>
#include <iostream>
#include <stdexcept>

using json = nlohmann::json;
using namespace std;

namespace ale {

  // Position Data Functions
  vector<double> getPosition(vector<vector<double>> coords, vector<double> times, double time,
                             interpolation interp) {
    // Check that all of the data sizes are okay
    // TODO is there a cleaner way to do this? We're going to have to do this a lot.
    if (coords.size() != 3) {
      throw invalid_argument("Invalid input positions, expected three vectors.");
    }

    // GSL setup
    vector<double> coordinate = {0.0, 0.0, 0.0};

    coordinate = { interpolate(coords[0], times, time, interp, 0),
                   interpolate(coords[1], times, time, interp, 0),
                   interpolate(coords[2], times, time, interp, 0) };

    return coordinate;
  }

  vector<double> getVelocity(vector<vector<double>> coords, vector<double> times,
                             double time, interpolation interp) {
    // Check that all of the data sizes are okay
    // TODO is there a cleaner way to do this? We're going to have to do this a lot.
    if (coords.size() != 3) {
     throw invalid_argument("Invalid input positions, expected three vectors.");
    }

    // GSL setup
    vector<double> coordinate = {0.0, 0.0, 0.0};

    coordinate = { interpolate(coords[0], times, time, interp, 1),
                   interpolate(coords[1], times, time, interp, 1),
                   interpolate(coords[2], times, time, interp, 1) };

    return coordinate;
  }

  // Postion Function Functions
  // vector<double> coeffs = [[cx_0, cx_1, cx_2 ..., cx_n],
  //                          [cy_0, cy_1, cy_2, ... cy_n],
  //                          [cz_0, cz_1, cz_2, ... cz_n]]
  // The equations evaluated by this function are:
  //                x = cx_n * t^n + cx_n-1 * t^(n-1) + ... + cx_0
  //                y = cy_n * t^n + cy_n-1 * t^(n-1) + ... + cy_0
  //                z = cz_n * t^n + cz_n-1 * t^(n-1) + ... + cz_0
  vector<double> getPosition(vector<vector<double>> coeffs, double time) {

    if (coeffs.size() != 3) {
      throw invalid_argument("Invalid input coeffs, expected three vectors.");
    }

    vector<double> coordinate = {0.0, 0.0, 0.0};
    coordinate[0] = evaluatePolynomial(coeffs[0], time, 0); // X
    coordinate[1] = evaluatePolynomial(coeffs[1], time, 0); // Y
    coordinate[2] = evaluatePolynomial(coeffs[2], time, 0); // Z

    return coordinate;
  }

  // Velocity Function
  // Takes the coefficients from the position equation
  vector<double> getVelocity(vector<vector<double>> coeffs, double time) {

    if (coeffs.size() != 3) {
      throw invalid_argument("Invalid input coeffs, expected three vectors.");
    }

    vector<double> coordinate = {0.0, 0.0, 0.0};
    coordinate[0] = evaluatePolynomial(coeffs[0], time, 1); // X
    coordinate[1] = evaluatePolynomial(coeffs[1], time, 1); // Y
    coordinate[2] = evaluatePolynomial(coeffs[2], time, 1); // Z

    return coordinate;
  }

  // Rotation Data Functions
  vector<double> getRotation(vector<vector<double>> rotations,
                             vector<double> times, double time,  interpolation interp) {
    // Check that all of the data sizes are okay
    // TODO is there a cleaner way to do this? We're going to have to do this a lot.
    if (rotations.size() != 4) {
     throw invalid_argument("Invalid input rotations, expected four vectors.");
    }

    // Alot of copying and reassignment becuase conflicting data types
    // probably should rethink our vector situation to guarentee contiguous
    // memory. Should be easy to switch to a contiguous column-major format
    // if we stick with Eigen.
    for (size_t i = 0; i<rotations[0].size(); i++) {
      Eigen::Quaterniond quat(rotations[0][i], rotations[1][i], rotations[2][i], rotations[3][i]);
      quat.normalize();

      rotations[0][i] = quat.w();
      rotations[1][i] = quat.x();
      rotations[2][i] = quat.y();
      rotations[3][i] = quat.z();
    }

    // GSL setup
    vector<double> coordinate = {0.0, 0.0, 0.0, 0.0};

    coordinate = { interpolate(rotations[0], times, time, interp, 0),
                   interpolate(rotations[1], times, time, interp, 0),
                   interpolate(rotations[2], times, time, interp, 0),
                   interpolate(rotations[3], times, time, interp, 0)};

    // Eigen::Map to ensure the array isn't copied, only the pointer is
    Eigen::Map<Eigen::MatrixXd> quat(coordinate.data(), 4, 1);
    quat.normalize();
    return coordinate;
  }

  vector<double> getAngularVelocity(vector<vector<double>> rotations,
                                    vector<double> times, double time,  interpolation interp) {
    // Check that all of the data sizes are okay
    // TODO is there a cleaner way to do this? We're going to have to do this a lot.
    if (rotations.size() != 4) {
     throw invalid_argument("Invalid input rotations, expected four vectors.");
    }

    double data[] = {0,0,0,0};
    for (size_t i = 0; i<rotations[0].size(); i++) {
      Eigen::Quaterniond quat(rotations[0][i], rotations[1][i], rotations[2][i], rotations[3][i]);
      quat.normalize();
      rotations[0][i] = quat.w();
      rotations[1][i] = quat.x();
      rotations[2][i] = quat.y();
      rotations[3][i] = quat.z();
    }

    // GSL setup

    Eigen::Quaterniond quat(interpolate(rotations[0], times, time, interp, 0),
                            interpolate(rotations[1], times, time, interp, 0),
                            interpolate(rotations[2], times, time, interp, 0),
                            interpolate(rotations[3], times, time, interp, 0));
    quat.normalize();

    Eigen::Quaterniond dQuat(interpolate(rotations[0], times, time, interp, 1),
                             interpolate(rotations[1], times, time, interp, 1),
                             interpolate(rotations[2], times, time, interp, 1),
                             interpolate(rotations[3], times, time, interp, 1));

     Eigen::Quaterniond avQuat = quat.conjugate() * dQuat;

     vector<double> coordinate = {-2 * avQuat.x(), -2 * avQuat.y(), -2 * avQuat.z()};
     return coordinate;
  }

  // Rotation Function Functions
  std::vector<double> getRotation(vector<vector<double>> coeffs, double time) {

    if (coeffs.size() != 3) {
      throw invalid_argument("Invalid input coefficients, expected three vectors.");
    }

    vector<double> rotation = {0.0, 0.0, 0.0};

    rotation[0] = evaluatePolynomial(coeffs[0], time, 0); // X
    rotation[1] = evaluatePolynomial(coeffs[1], time, 0); // Y
    rotation[2] = evaluatePolynomial(coeffs[2], time, 0); // Z

    Eigen::Quaterniond quat;
    quat = Eigen::AngleAxisd(rotation[0] * M_PI / 180, Eigen::Vector3d::UnitZ())
                * Eigen::AngleAxisd(rotation[1] * M_PI / 180, Eigen::Vector3d::UnitX())
                * Eigen::AngleAxisd(rotation[2] * M_PI / 180, Eigen::Vector3d::UnitZ());

    quat.normalize();

    vector<double> rotationQ = {quat.w(), quat.x(), quat.y(), quat.z()};
    return rotationQ;
  }

  vector<double> getAngularVelocity(vector<vector<double>> coeffs, double time) {

    if (coeffs.size() != 3) {
      throw invalid_argument("Invalid input coefficients, expected three vectors.");
    }

    double phi = evaluatePolynomial(coeffs[0], time, 0); // X
    double theta = evaluatePolynomial(coeffs[1], time, 0); // Y
    double psi = evaluatePolynomial(coeffs[2], time, 0); // Z

    double phi_dt = evaluatePolynomial(coeffs[0], time, 1);
    double theta_dt = evaluatePolynomial(coeffs[1], time, 1);
    double psi_dt = evaluatePolynomial(coeffs[2], time, 1);

    Eigen::Quaterniond quat1, quat2;
    quat1 = Eigen::AngleAxisd(phi * M_PI / 180, Eigen::Vector3d::UnitZ());
    quat2 =  Eigen::AngleAxisd(theta * M_PI / 180, Eigen::Vector3d::UnitX());

    Eigen::Vector3d velocity =  phi_dt * Eigen::Vector3d::UnitZ();
    velocity += theta_dt * (quat1 *  Eigen::Vector3d::UnitX());
    velocity += psi_dt * (quat1 * quat2 *  Eigen::Vector3d::UnitZ());

    return {velocity[0], velocity[1], velocity[2]};
  }

  // Polynomial evaluation helper function
  // The equation evaluated by this function is:
  //                x = cx_0 + cx_1 * t^(1) + ... + cx_n * t^n
  // The d parameter is for which derivative of the polynomial to compute.
  // Supported options are
  //   0: no derivative
  //   1: first derivative
  //   2: second derivative
  double evaluatePolynomial(vector<double> coeffs, double time, int d){
    if (coeffs.empty()) {
      throw invalid_argument("Invalid input coeffs, must be non-empty.");
    }

    if (d < 0) {
      throw invalid_argument("Invalid derivative degree, must be non-negative.");
    }

    vector<double> derivatives(d + 1);
    gsl_poly_eval_derivs(coeffs.data(), coeffs.size(), time,
                         derivatives.data(), derivatives.size());

    return derivatives.back();
  }

 double interpolate(vector<double> points, vector<double> times, double time, interpolation interp, int d) {
   size_t numPoints = points.size();
   if (numPoints < 2) {
     throw invalid_argument("At least two points must be input to interpolate over.");
   }
   if (points.size() != times.size()) {
     throw invalid_argument("Invalid gsl_interp_type data, must have the same number of points as times.");
   }
   if (time < times.front() || time > times.back()) {
     throw invalid_argument("Invalid gsl_interp_type time, outside of input times.");
   }

   // convert our interp enum into a GSL one,
   // should be easy to add non GSL interp methods here later
   const gsl_interp_type *interp_methods[] = {gsl_interp_linear, gsl_interp_cspline};

   gsl_interp *interpolator = gsl_interp_alloc(interp_methods[interp], numPoints);
   gsl_interp_init(interpolator, &times[0], &points[0], numPoints);
   gsl_interp_accel *acc = gsl_interp_accel_alloc();

   // GSL evaluate
   double result;
   switch(d) {
     case 0:
       result = gsl_interp_eval(interpolator, &times[0], &points[0], time, acc);
       break;
     case 1:
       result = gsl_interp_eval_deriv(interpolator, &times[0], &points[0], time, acc);
       break;
     case 2:
       result = gsl_interp_eval_deriv2(interpolator, &times[0], &points[0], time, acc);
       break;
     default:
       throw invalid_argument("Invalid derivitive option, must be 0, 1 or 2.");
       break;
   }

   // GSL clean up
   gsl_interp_free(interpolator);
   gsl_interp_accel_free(acc);

   return result;
 }

 std::string getPyTraceback() {
    PyObject* err = PyErr_Occurred();
    if (err != NULL) {
        PyObject *ptype, *pvalue, *ptraceback;
        PyObject *pystr, *module_name, *pyth_module, *pyth_func;
        char *str;
        char *full_backtrace;
        char *error_description;

        PyErr_Fetch(&ptype, &pvalue, &ptraceback);
        pystr = PyObject_Str(pvalue);
        str = PyBytes_AS_STRING(PyUnicode_AsUTF8String(pystr));
        error_description = strdup(str);

        // See if we can get a full traceback
        module_name = PyUnicode_FromString("traceback");
        pyth_module = PyImport_Import(module_name);
        Py_DECREF(module_name);

        if (pyth_module == NULL) {
            throw runtime_error("getPyTraceback - Failed to import Python traceback Library");
        }

        pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
        PyObject *pyth_val;
        pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);

        pystr = PyObject_Str(pyth_val);
        str = PyBytes_AS_STRING(PyUnicode_AsUTF8String(pystr));
        full_backtrace = strdup(str);
        Py_DECREF(pyth_val);

        std::string join_cmd = "trace = ''.join(list(" + std::string(full_backtrace) + "))";
        PyRun_SimpleString(join_cmd.c_str());

        PyObject *evalModule = PyImport_AddModule( (char*)"__main__" );
        PyObject *evalDict = PyModule_GetDict( evalModule );
        PyObject *evalVal = PyDict_GetItemString( evalDict, "trace" );
        full_backtrace = PyBytes_AS_STRING(PyUnicode_AsUTF8String(evalVal));

        return std::string(error_description) + "\n" + std::string(full_backtrace);
    }

    // no traceback to return
    return "";
 }

 std::string loads(std::string filename, std::string props, std::string formatter, bool verbose) {
     static bool first_run = true;
     if(first_run) {
         // Initialize the Python interpreter but only once.
         first_run = !first_run;
         Py_Initialize();
         atexit(Py_Finalize);
     }

     // Import the file as a Python module.
     PyObject *pModule = PyImport_Import(PyUnicode_FromString("ale"));
     if(!pModule) {
       throw runtime_error("Failed to import ale. Make sure the ale python library is correctly installed.");
     }
     // Create a dictionary for the contents of the module.
     PyObject *pDict = PyModule_GetDict(pModule);

     // Get the add method from the dictionary.
     PyObject *pFunc = PyDict_GetItemString(pDict, "loads");
     if(!pFunc) {
       // import errors do not set a PyError flag, need to use a custom
       // error message instead.
       throw runtime_error("Failed to import ale.loads function from Python."
                           "This Usually indicates an error in the Ale Python Library."
                           "Check if Installed correctly and the function ale.loads exists.");
     }

     // Create a Python tuple to hold the arguments to the method.
     PyObject *pArgs = PyTuple_New(3);
     if(!pArgs) {
       throw runtime_error(getPyTraceback());
     }

     // Set the Python int as the first and second arguments to the method.
     PyObject *pStringFileName = PyUnicode_FromString(filename.c_str());
     PyTuple_SetItem(pArgs, 0, pStringFileName);

     PyObject *pStringProps = PyUnicode_FromString(props.c_str());
     PyTuple_SetItem(pArgs, 1, pStringProps);

     PyObject *pStringFormatter = PyUnicode_FromString(formatter.c_str());
     PyTuple_SetItem(pArgs, 2, pStringFormatter);

     // Call the function with the arguments.
     PyObject* pResult = PyObject_CallObject(pFunc, pArgs);

     if(!pResult) {
        throw invalid_argument("No Valid instrument found for label.");
     }

     PyObject *pResultStr = PyObject_Str(pResult);
     PyObject *temp_bytes = PyUnicode_AsUTF8String(pResultStr); // Owned reference

     if(!temp_bytes){
       throw invalid_argument(getPyTraceback());
     }
     std::string cResult;
     char *temp_str = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer
     cResult = temp_str; // copy into std::string

     Py_DECREF(pResultStr);
     Py_DECREF(pStringFileName);
     Py_DECREF(pStringProps);
     Py_DECREF(pStringFormatter);

     return cResult;
 }

 json load(std::string filename, std::string props, std::string formatter, bool verbose) {
   std::string jsonstr = loads(filename, props, formatter, verbose);
   return json::parse(jsonstr);
 }
}


/*
 * Copyright (c) 2015 Claus Christmann <hcc |ä| gatech.edu>.
 *
 * Licensed under the Apache Lice*nse, 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.
 *
 */

#include <Eigen/Geometry>
#include <Eigen/Dense>

#include <iostream>
#include <cmath>

#include "amg.hpp"

using namespace Eigen;

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

  using namespace AMG;

  FrameOfReference* ecef = new FrameOfReference;
  CoSy::setECEF(ecef);

  { std::cout << "\nWGS 84:\n"
              <<   "=======" << std::endl;

    using namespace AMG::GIS;

    std::cout
    << "WGS84\n"
    << "|- a     : " << WGS84::a << "\n"
    << "|- finv  : " << WGS84::finv << "\n"
    << "|- omega : " << WGS84::omega << "\n"
    << "|- GM    : " << WGS84::GM << "\n"
    << "|\n"
    << "|- f     : " << WGS84::f << "\n"
    << "|- b     : " << WGS84::b << "\n"
    << "|- e2    : " << WGS84::e2 << "\n"
    << "|- ep2   : " << WGS84::ep2 << "\n"
    << "|- g0    : " << WGS84::g0 << "\n"
    << std::endl;

  }

  { std::cout << "\nGeodetic to ECEF conversions:\n"
              <<   "=============================" << std::endl;

    auto g2e = [] (double lat_deg, double lon_deg, double alt_m)
    {
      double x_ecef, y_ecef, z_ecef;

      GIS::geodetic2ecef(Units::degree2radian(lon_deg),
                        Units::degree2radian( lat_deg),
                        alt_m,x_ecef,y_ecef,z_ecef);

      std::cout << "Lat,Lon,Alt :" << lat_deg << ", "
                                   << lon_deg << ", "
                                   << alt_m   << "\n"
                << "X,Y,Z       :" << x_ecef << ", " << y_ecef << ", " << z_ecef
      << std::endl;
    };

    g2e(0,0,0);
    g2e(0,0,1000);
    g2e(45,45,0);
    g2e(90,90,0);
    g2e(33.772022,-84.396188,0);
  }

  { std::cout << "\nECEF to Geodetic conversions:\n"
              <<   "=============================" << std::endl;

    auto e2g = [] (double x_ecef, double y_ecef, double z_ecef)
    {
      double lat_rad, lon_rad, alt_m;

      GIS::ecef2geodetic( x_ecef,y_ecef,z_ecef,lon_rad,lat_rad,alt_m);

      std::cout << "X,Y,Z       :" << x_ecef << ", " << y_ecef << ", " << z_ecef << "\n"
                << "Lat,Lon,Alt :" << Units::radian2degree(lat_rad) << ", "
                                   << Units::radian2degree(lon_rad) << ", "
                                   << alt_m
      << std::endl;
    };

    e2g(518259,-5.28199e+06, 3.52545e+06);

  }

  FrameOfReference* datum = CoSy::newNorthEastDown<Units::degree>( -84.396188, 33.772022 );
  CoSy::setDatumFrame(datum);
  datum->outputRelativePositionToParent();
  std::cout
    << "Datum attitude (roll, pitch, yaw; in degrees) : "
    << datum->attitude<Units::degree>().transpose() << "\n"
    << std::endl;

  double x_ecef,y_ecef,z_ecef;

  GIS::geodetic2ecef(Units::degree2radian(-84.396188),
                      Units::degree2radian( 33.772022),
                      0,x_ecef,y_ecef,z_ecef);
  Vector southCorner(x_ecef,y_ecef,z_ecef,CoSy::getECEF());

  double lat,lon,alt;
  GIS::ecef2geodetic(southCorner.coords(0),southCorner.coords(1),southCorner.coords(2),
                     lon,lat,alt);

  std::cout
  << "South Corner in Lat, Lon, Alt  : " << Units::radian2degree(lat) << ", "
                                         << Units::radian2degree(lon) << ", "
                                         << alt << "\n"
  << "South Corner in ECEF           : "
  << southCorner.coords().transpose() << "\n"
  << "South Corner in absolute Datum : "
  << southCorner.absoluteCoordsIn(datum).transpose() << "\n"
  << std::endl;

  std::cout << AMG::GIS::geodeticPositionString(southCorner) << std::endl;

  GIS::geodetic2ecef(Units::degree2radian(-84.396188),
                      Units::degree2radian( 33.772111),
                      0,x_ecef,y_ecef,z_ecef);
  Vector northCorner(x_ecef,y_ecef,z_ecef,CoSy::getECEF());
  std::cout
  << "North Corner in ECEF           : "
  << northCorner.coords().transpose() << "\n"
  << "North Corner in absolute Datum : "
  << northCorner.absoluteCoordsIn(datum).transpose() << "\n"
  << std::endl;

  std::cout << AMG::GIS::geodeticPositionString(northCorner) << std::endl;

  Vector delta = northCorner - southCorner;

  std::cout
  << "Delta Corner in ECEF           : "
  << delta.coords().transpose() << "\n"
  << "Delta Corner in relative Datum : "
  << delta.coordsIn(datum).transpose() << "\n"
  << std::endl;

  AMG::RigidBody body;
  body.outputRelativePositionToParent();

  body.setPositionTo<Units::degree>(-84.396188 , 33.772111 , 0.0 );
  body.outputRelativePositionToParent();

  body.rotate<Units::degree>(90.0,0,0);
  body.outputRelativePositionToParent();

  std::cout
    << "Body Euler attitude (phi, theat, psi):\n"
    << "(degree) : " << body.attitude<Units::degree>().transpose() << "\n"
    << "(radian) : " << body.attitude<Units::radian>().transpose() << "\n"
    << "(quaternions) : " << body.attitude().transpose() << "\n"
    << "yaw (deg) : " << body.yaw<Units::degree>() << "\n"
    << "pitch (deg) : " << body.pitch<Units::degree>() << "\n"
    << "roll (deg) : " << body.roll<Units::degree>() << "\n"
    << std:: endl;

  {
    using namespace AMG::GIS;

    std:: cout << "isInLeftHalfCircle( 0,   1) = " << isInLeftHalfCircle(0,    1) << std::endl;
    std:: cout << "isInLeftHalfCircle( 0,   0) = " << isInLeftHalfCircle(0,    0) << std::endl;
    std:: cout << "isInLeftHalfCircle( 0,  -1) = " << isInLeftHalfCircle(0,   -1) << std::endl;
    std:: cout << "isInLeftHalfCircle(10, 190) = " << isInLeftHalfCircle(10, 190) << std::endl;
    std:: cout << "isInLeftHalfCircle(10,-190) = " << isInLeftHalfCircle(10,-190) << std::endl;
    std:: cout << "isInLeftHalfCircle(10,-170) = " << isInLeftHalfCircle(10,-170) << std::endl;
    std:: cout << "isInLeftHalfCircle(10,-175) = " << isInLeftHalfCircle(10,-175) << std::endl;
    std:: cout << "isInLeftHalfCircle(10,-165) = " << isInLeftHalfCircle(10,-165) << std::endl;
    std:: cout << "isInLeftHalfCircle(10, 370) = " << isInLeftHalfCircle(10, 370) << std::endl;
    std:: cout << "isInLeftHalfCircle(10, 365) = " << isInLeftHalfCircle(10, 365) << std::endl;

  }

  { std::cout << "Testing \"headingToCompassPoint()\""<< std::endl;
    for( double hdg = 0; hdg <=360; hdg += 5 )
    { std::cout << hdg << "° => "<< AMG::GIS::headingToCompassPoint(hdg) << std::endl; }
  }

  return 0;
}


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

/*
 Copyright (C) 2016, Sebastian Schlenkrich

*/

/*! \file mcscriptT.hpp
    \brief script payoffs for MC simulation

*/

#ifndef quantlib_templatemcscript_hpp
#define quantlib_templatemcscript_hpp

#include <ql/experimental/templatemodels/montecarlo/mcpayoffT.hpp>

#include <ql/experimental/templatemodels/montecarlo/scripting/flexbisondriver.hpp>
#include <ql/experimental/templatemodels/montecarlo/scripting/expression.hpp>

#include <ql/time/date.hpp>

#include <boost/regex.hpp>

namespace QuantLib {

    template <class DateType, class PassiveType, class ActiveType>
    class MCScriptT : public MCPayoffT<DateType, PassiveType, ActiveType> {
    protected:
        // easy use of templated types
        typedef MCSimulationT<DateType, PassiveType, ActiveType>        SimulationType;
        typedef MCPayoffT<DateType, PassiveType, ActiveType>            PayoffType;
        typedef BasePayoffT<DateType, PassiveType, ActiveType>          MCBase;
        typedef typename MCSimulationT<DateType, PassiveType, ActiveType>::Path  PathType;

    private:
        std::map<std::string, ext::shared_ptr<PayoffType>>   payoffs_;      // the actual payoffs which may be accessed
        std::vector<std::string>                             expressions_;  // resulting expressions after parsing the script but before syntactic analysis
        std::vector<std::string>                             scriptLog_;    // log messages when parsing the script
        ext::shared_ptr<PayoffType>                          result_;       // result payoff for MCPayoff interface implementation
    public:
        MCScriptT(const std::vector<std::string>&                    keys,
                  const std::vector <ext::shared_ptr<PayoffType>>&   payoffs,
                  const std::vector<std::string>&                    script,
                  const bool                                         overwrite=true) : MCPayoffT<DateType, PassiveType, ActiveType>(0.0) {
            QL_REQUIRE(keys.size()==payoffs.size(), "MCScript error: key vs. value size missmatch");
            for (Size k = 0; k < keys.size(); ++k) { // initialize map
                typename std::map< std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find(keys[k]);
                if (it == payoffs_.end()) { // insert a new element
                    payoffs_.insert( std::make_pair(keys[k], payoffs[k]) );
                } else { // potentially overwrite existing element
                    QL_REQUIRE(overwrite, "MCScript error: overwrite not allowed");
                    it->second = payoffs[k];
                }
            }
            if ((script.size() > 0) && (script[0].compare("NonRecursive") == 0)) {
                //parseScript(script, overwrite);           // deprecated and for debugging purpose
            } else {
                parseFlexBisonScript(script, overwrite);  // for briefty we delegate parsing to separate method
            }
            QL_REQUIRE(payoffs_.size()>0, "MCScript error: no payoffs stored.");
            result_ = payoffs_.rbegin()->second; // pick the last element as fall back

            // we need to find a 'result' payoff
            typename std::map< std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find("payoff");
            if (it != payoffs_.end()) result_ = it->second;
            else result_ = payoffs_.rbegin()->second; // pick the last element as fall back
            PayoffType::observationTime_ = result_->observationTime();
        }

        inline virtual ActiveType at(const ext::shared_ptr<PathType>& p) {
            return result_->at(p);
        }

        // inspector
        inline const std::map<std::string, ext::shared_ptr<PayoffType>>&  payoffs()     { return payoffs_;   }
        inline const std::vector<std::string>&                            expressions() { return expressions_; }
        inline const std::vector<std::string>&                            scriptLog()   { return scriptLog_; }

        // return all keys
        std::vector<std::string> payoffsKeys() {
            std::vector<std::string> keyVector;
            for (typename std::map<std::string, ext::shared_ptr<PayoffType>>::iterator it = payoffs_.begin(); it != payoffs_.end(); ++it) {
                keyVector.push_back(it->first);
            }
            return keyVector;
        }

        // return all payoffs (values in map)
        std::vector < ext::shared_ptr<PayoffType> > payoffValues() {
            std::vector < ext::shared_ptr<PayoffType> > payoffVector;
            for (typename std::map<std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.begin(); it != payoffs_.end(); ++it) {
                payoffVector.push_back(it->second);
            }
            return payoffVector;
        }

        std::vector<DateType> observationTimes(const std::vector<std::string>& keys) {
            std::vector<ext::shared_ptr<PayoffType>> payoffs = findPayoffs(keys);
            std::set<DateType> s;
            for (size_t k = 0; k < payoffs.size(); ++k) s = PayoffType::unionTimes(s, payoffs[k]->observationTimes());
            return std::vector<DateType>(s.begin(), s.end());
        }

        // MC valuation
        inline std::vector<ActiveType> NPV(const ext::shared_ptr<SimulationType>&    simulation,
                                           const std::vector<std::string>&           keys) {
            std::vector<ext::shared_ptr<PayoffType>> payoffs = findPayoffs(keys);
            std::vector<ActiveType> npv(payoffs.size(), (ActiveType)0.0);
            for (Size n = 0; n < simulation->nPaths(); ++n) {
                const ext::shared_ptr<PathType> p(simulation->path(n));
                for (Size k = 0; k < payoffs.size(); ++k) {
                    npv[k] += payoffs[k]->discountedAt(p);
                }
            }
            for (Size k = 0; k < payoffs.size(); ++k) npv[k] /= simulation->nPaths();
            return npv;
        }

        // some helper functions to simplify Asset payoff handling
        inline static bool add_FixingTimes_to_Asset(
            ext::shared_ptr<PayoffType>        payoff,
            const std::vector<DateType>&       fixingTimes,
            const std::vector<PassiveType>&    fixingValues) {
            ext::shared_ptr<typename MCBase::Asset>  assetPayoff =
                boost::dynamic_pointer_cast<typename MCBase::Asset>(payoff);
            QL_REQUIRE(assetPayoff, "Payoff is no Asset");
            QL_REQUIRE(fixingTimes.size() == fixingValues.size(), "fixingTimes.size()==fixingValues.size() required");
            std::vector< std::pair<DateType, PassiveType> > history;
            for (size_t k = 0; k < fixingTimes.size(); ++k)
                history.push_back(std::make_pair(fixingTimes[k], fixingValues[k]));
            assetPayoff->addFixings(history);
            return true;
        }

        inline static bool add_FixingDates_to_Asset(
            ext::shared_ptr<PayoffType>        payoff,
            const std::vector<Date>&           fixingDates,
            const std::vector<PassiveType>&    fixingValues) {
            std::vector<DateType> fixingTimes(fixingDates.size());
            Date today = Settings::instance().evaluationDate();
            for (size_t k = 0; k < fixingDates.size(); ++k)
                fixingTimes[k] = (DateType)(((fixingDates[k].serialNumber() - today.serialNumber()) / 365.0));
            return add_FixingTimes_to_Asset(payoff, fixingTimes, fixingValues);
        }

    private:

        // convert string to number
        inline static bool to_Number(const std::string str, ActiveType& number) {
            double res;
            std::string::size_type sz;
            try {
                res = std::stod(str, &sz);
            }
            catch (std::exception e) {
                return false;
            }
            number = res;
            return true;
        }

        // convert a Date to number
        inline static DateType date_to_Number(const Date& d) {
            Date today = Settings::instance().evaluationDate();
            DateType number = (DateType)(((d.serialNumber() - today.serialNumber()) / 365.0));
            return number;
        }

        // convert date string with format ddmmmyyyy to number
        inline static bool date_to_Number(const std::string str, ActiveType& number) {
            if (str.length() != 9) return false;
            std::string::size_type sz;
            try {
                Day  day  = std::stol(str.substr(0,2), &sz);
                Year year = std::stol(str.substr(5,4), &sz);
                Month month;
                std::string s = str.substr(2, 3);
                if (s.compare("Jan") == 0) month = Month::Jan;
                else if (s.compare("Feb") == 0) month = Month::Feb;
                else if (s.compare("Mar") == 0) month = Month::Mar;
                else if (s.compare("Apr") == 0) month = Month::Apr;
                else if (s.compare("May") == 0) month = Month::May;
                else if (s.compare("Jun") == 0) month = Month::Jun;
                else if (s.compare("Jul") == 0) month = Month::Jul;
                else if (s.compare("Aug") == 0) month = Month::Aug;
                else if (s.compare("Sep") == 0) month = Month::Sep;
                else if (s.compare("Oct") == 0) month = Month::Oct;
                else if (s.compare("Nov") == 0) month = Month::Nov;
                else if (s.compare("Dec") == 0) month = Month::Dec;
                else return false;
                Date d(day, month, year);
                Date today = Settings::instance().evaluationDate();
                number = (ActiveType)(((d.serialNumber() - today.serialNumber()) / 365.0));
            }
            catch (std::exception e) {
                return false;
            }
            return true;
        }

        // we define that helper function to simplify code in forthcoming expression parsing
        inline bool hasChilds(const ext::shared_ptr<Scripting::Expression> tree, Size nArgs, Size lineNr) {
            // make sure we can actually do something with the tree
            if (!tree) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": Empty expression tree."));
                return false;
            }
            if (tree->childs().size() != nArgs) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": " + std::to_string(nArgs) + " child expressions expected, but " + std::to_string(tree->childs().size()) + " found." ));
                return false;
            }
            return true;
        }

        // we define that helper function to simplify code in forthcoming expression parsing
        inline bool hasChildsInRange(const ext::shared_ptr<Scripting::Expression> tree, Size nArgsMin, Size nArgsMax, Size lineNr) {
            // make sure we can actually do something with the tree
            if (!tree) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": Empty expression tree."));
                return false;
            }
            if ((tree->childs().size() < nArgsMin)||(tree->childs().size() > nArgsMax)) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": [" + std::to_string(nArgsMin) + ", " + std::to_string(nArgsMax) +
                    "] child expressions expected, but " + std::to_string(tree->childs().size()) + " found."));
                return false;
            }
            return true;
        }

        // we define that helper function to simplify code in forthcoming expression parsing
        inline bool hasLeafs(const ext::shared_ptr<Scripting::Expression> tree, Size nArgs, Size lineNr) {
            // make sure we can actually do something with the tree
            if (!tree) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": Empty expression tree."));
                return false;
            }
            if (tree->leafs().size() != nArgs) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": " + std::to_string(nArgs) + " leafs expected, but " + std::to_string(tree->leafs().size()) + " found."));
                return false;
            }
            return true;
        }

        // we define that helper function to simplify code in forthcoming expression parsing
        inline bool hasLeafsInRange(const ext::shared_ptr<Scripting::Expression> tree, Size nArgsMin, Size nArgsMax, Size lineNr) {
            // make sure we can actually do something with the tree
            if (!tree) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": Empty expression tree."));
                return false;
            }
            if ((tree->leafs().size()<nArgsMin)|| (tree->leafs().size()>nArgsMax)) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": [" + std::to_string(nArgsMin) + ", " + std::to_string(nArgsMax) +
                    "] leafs expected, but " + std::to_string(tree->leafs().size()) + " found."));
                return false;
            }
            return true;
        }

        // check if a list of payoffs exists before doing some computationally expensive stuff with them
        std::vector< ext::shared_ptr<PayoffType> > findPayoffs(const std::vector<std::string>& keys, bool throwException = true) {
            std::vector<ext::shared_ptr<PayoffType>> payoffs;
            for (Size k = 0; k < keys.size(); ++k) {
                typename std::map<std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find(keys[k]);
                if (it != payoffs_.end()) {
                    payoffs.push_back(it->second);
                    continue; // all done for this key
                }
                if (throwException) QL_FAIL("MCScript error: payoff '" + keys[k] + "' not found");
            }
            return payoffs;
        }

        // convert an abstract expression tree into a payoff
        // this function does the actual work...
        ext::shared_ptr<PayoffType> payoff(const ext::shared_ptr<Scripting::Expression> tree, Size k) {
            // make sure we can actually do something with the tree
            if (!tree) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Empty expression tree."));
                QL_FAIL("Cannot interprete payoff");
            }
            // check any possible expression
            switch (tree->type()) {
            // expressions based on tokens
            case Scripting::Expression::NUMBER: {
                if (!hasChilds(tree, 0, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k)) 	QL_FAIL("Cannot interprete payoff");
                ActiveType number;
                if (to_Number(tree->leafs()[0], number)) {
                    return ext::shared_ptr<PayoffType>(new typename MCBase::FixedAmount(number));
                }
                scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[0] + " to number."));
                QL_FAIL("Cannot interprete payoff");
            }
            case Scripting::Expression::IDENTIFIER: {
                if (!hasChilds(tree, 0, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k))  QL_FAIL("Cannot interprete payoff");
                // check for existing payoff in map
                typename std::map<std::string, ext::shared_ptr<PayoffType>>::iterator it = payoffs_.find(tree->leafs()[0]);
                if (it != payoffs_.end()) {
                    scriptLog_.push_back(std::string("Payoff line " + std::to_string(k) + ": '" + tree->leafs()[0] + "' is in map"));
                    return it->second;
                }
                // if we end up here no conversion was successfull
                scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": '" + tree->leafs()[0] + "' is no payoff"));
                QL_FAIL("Cannot interprete payoff");
            }
            // expressions basen on unary operators
            case Scripting::Expression::UNARYPLUS: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return this->payoff(tree->childs()[0], k);
            }
            case Scripting::Expression::UNARYMINUS: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(-1.0, payoff(tree->childs()[0], k), 0));
            }
            case Scripting::Expression::PLUS: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(1.0, payoff(tree->childs()[0], k), payoff(tree->childs()[1], k)));
            }
            case Scripting::Expression::MINUS: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(-1.0, payoff(tree->childs()[1], k), payoff(tree->childs()[0], k)));
            }
            case Scripting::Expression::MULT: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Mult(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k)));
            }
            case Scripting::Expression::DIVISION: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Division(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k)));
            }
            case Scripting::Expression::IFTHENELSE: {
                if (!hasChilds(tree, 3, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::IfThenElse(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k), payoff(tree->childs()[2], k)));
            }
            case Scripting::Expression::MIN: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Min(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k)));
            }
            case Scripting::Expression::MAX: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Max(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k)));
            }
            case Scripting::Expression::EXPONENTIAL: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Exponential(payoff(tree->childs()[0], k)));
            }
            case Scripting::Expression::LOGARITHM: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Logarithm(payoff(tree->childs()[0], k)));
            }
            case Scripting::Expression::SQUAREROOT: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Squareroot(payoff(tree->childs()[0], k)));
            }
            case Scripting::Expression::LOGICAL: {
                if (!hasChilds(tree, 2, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Logical(payoff(tree->childs()[0], k), payoff(tree->childs()[1], k), tree->leafs()[0]));
            }
            case Scripting::Expression::PAY: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k))  QL_FAIL("Cannot interprete payoff");
                ActiveType number;
                if (!to_Number(tree->leafs()[0], number)) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[0] + " to number."));
                    QL_FAIL("Cannot interprete payoff");
                }
                return ext::shared_ptr<PayoffType>(new typename MCBase::Pay(payoff(tree->childs()[0], k), number));
            }
            case Scripting::Expression::PAY_WITHDATE: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k))  QL_FAIL("Cannot interprete payoff");
                ActiveType number;
                if (!date_to_Number(tree->leafs()[0], number)) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[0] + " to number."));
                    QL_FAIL("Cannot interprete payoff");
                }
                return ext::shared_ptr<PayoffType>(new typename MCBase::Pay(payoff(tree->childs()[0], k), number));
            }
            case Scripting::Expression::CACHE: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 0, k))  QL_FAIL("Cannot interprete payoff");
                return ext::shared_ptr<PayoffType>(new typename MCBase::Cache(payoff(tree->childs()[0], k)));
            }
            case Scripting::Expression::PAYOFFAT: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafsInRange(tree, 1, 2, k)) QL_FAIL("Cannot interprete payoff");
                ActiveType number;
                if (!to_Number(tree->leafs()[0], number)) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[0] + " to number."));
                    QL_FAIL("Cannot interprete payoff");
                }
                ActiveType sign = 1.0;
                if (tree->leafs().size()>1) { // we expect a negative number
                    if (tree->leafs()[1].compare("-") != 0) {
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[1] + tree->leafs()[0] + " to negative number."));
                        QL_FAIL("Cannot interprete payoff");
                    }
                    sign = -1.0;
                }
                ext::shared_ptr<PayoffType> p = payoff(tree->childs()[0], k);
                if (p) return p->at(sign*number);
                QL_FAIL("Cannot interprete payoff");
            }
            case Scripting::Expression::PAYOFFAT_WITHDATE: {
                if (!hasChilds(tree, 1, k)) QL_FAIL("Cannot interprete payoff");
                if (!hasLeafs(tree, 1, k))  QL_FAIL("Cannot interprete payoff");
                ActiveType number;
                if (!date_to_Number(tree->leafs()[0], number)) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": cannot convert " + tree->leafs()[0] + " to number."));
                    QL_FAIL("Cannot interprete payoff");
                }
                ext::shared_ptr<PayoffType> p = payoff(tree->childs()[0], k);
                if (p) return p->at(number);
                QL_FAIL("Cannot interprete payoff");
            }
            // we don't need a default because we returned in each of the previous cases
            } // finished all switch types
            // if we end up here there is an expression which we didn't interprete
            scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": unknown expression type."));
            QL_FAIL("Cannot interprete payoff");
            return 0; // this should never be reached
        }

        // parse the script and set up payoffs
        inline void parseFlexBisonScript(const std::vector<std::string>&  script,
                                         const bool                       overwrite = true) {
            for (Size k = 0; k < script.size(); ++k) {  // first line should equal 'FlexBison' and is skipped anyway
                Scripting::FlexBisonDriver driver(script[k], false, false);
                // in any case we want to know the parsing result
                if (driver.expressionTree()) expressions_.push_back("L" + std::to_string(k) + ":" + driver.expressionTree()->toString());
                if (driver.returnValue() == 0) {
                    if (!driver.expressionTree()) {
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Empty expression tree."));
                        continue;
                    }
                    if (driver.expressionTree()->type() != Scripting::Expression::ASSIGNMENT ) {
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Assignment expected."));
                        continue;
                    }
                    if (!hasChilds(driver.expressionTree(), 1, k)) continue;
                    if (!hasLeafs(driver.expressionTree(), 1, k)) continue;
                    // interprete right side of assignment
                    ext::shared_ptr<PayoffType> p;
                    try {
                        p = payoff(driver.expressionTree()->childs()[0], k);
                    }
                    catch (std::exception e) {  // something went wrong, for details check scriptLog_
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Exception caught: " + e.what()));
                        continue;
                    }
                    if (!p) {
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": No payoff found."));
                        continue;
                    }
                    // just define an abbreviation
                    std::string var = driver.expressionTree()->leafs()[0];
                    if (var.compare("") == 0) {
                        scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Non-empty identifier expected."));
                        continue;
                    }
                    // now we have a payoff which we may store in the map
                    typename std::map<std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find(var);
                    if (it == payoffs_.end()) { // insert a new element
                        payoffs_.insert(std::make_pair(var, p));
                        scriptLog_.push_back(std::string("Insert line " + std::to_string(k) + ": '" + script[k] + "'"));
                        continue;
                    }
                    if (overwrite) {
                        it->second = p;
                        scriptLog_.push_back(std::string("Replace line " + std::to_string(k) + ": '" + script[k] + "'"));
                        continue;
                    }
                    else scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": Cannot replace line '" + script[k] + "'"));
                }
                else {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": " + driver.errorMsg()));
                    continue;
                }
            }
        }

        /*

        we implement the following non-recursive grammar

        line  =  var '=' expr
        var   =  [a-zA-Z][a-zA-Z0-9]*           { RegEx }
        expr  =  operator | function | payoff   { apply from left to right }

        operator   =  operator1 | operator2
        operator1  =  ['+' | '-'] payoff
        operator2  =  payoff ['+' | '-' | '*' | == | != | < | <= | > | >= | && | || ] payoff

        function   =  function3 | function2 | function1
        function3  =  fname3 '(' payoff ',' payoff ',' payoff ')'
        function2  =  fname2 '(' payoff ',' payoff ')'
        function1  =  fname1 '(' payoff ')'

        fname3     =  'IfThenElse'
        fname2     =  'Min' | 'Max | Pay'
        fname1     =  'Cache'

        payoff  =  number | string              { try double conversion and lookup in map }

        */

        // parse the script and set up payoffs
        inline void parseScript(const std::vector<std::string>&  script,
            const bool                       overwrite = true) {
            for (Size k = 0; k < script.size(); ++k) {  // parse lines
                std::string line = boost::regex_replace(script[k], boost::regex(" "), ""); // remove whitespaces
                boost::smatch what;

                bool isAssignment = boost::regex_match(line, what, boost::regex("([a-zA-Z][a-zA-Z0-9]*)(=)(.+)"));
                if (!isAssignment) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": '" + line + "' is no valid assignment"));
                    continue; // move to next item in script
                }
                std::string var(what[1]), expr(what[3]);

                ext::shared_ptr<PayoffType> p;

                if (boost::regex_match(expr, what, boost::regex("(\\+|-)(.+)"))) {
                    p = operator1(std::string(what[1]), std::string(what[2]), k);
                }
                else if (boost::regex_match(expr, what, boost::regex("(.+)(\\+|-|\\*|==|!=|<=|<|>=|>|&&|\\|\\|)(.+)"))) {
                    p = operator2(std::string(what[2]), std::string(what[1]), std::string(what[3]), k);
                }
                else if (boost::regex_match(expr, what, boost::regex("([a-zA-Z]+)\\((.+),(.+),(.+)\\)"))) {
                    p = function3(std::string(what[1]), std::string(what[2]), std::string(what[3]), std::string(what[4]), k);
                }
                else if (boost::regex_match(expr, what, boost::regex("([a-zA-Z]+)\\((.+),(.+)\\)"))) {
                    p = function2(std::string(what[1]), std::string(what[2]), std::string(what[3]), k);
                }
                else if (boost::regex_match(expr, what, boost::regex("([a-zA-Z]+)\\((.+)\\)"))) {
                    p = function1(std::string(what[1]), std::string(what[2]), k);
                }
                else p = payoff(expr, k); // action of last resort

                if (!p) {
                    scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": '" + expr + "' is no valid expression"));
                    continue; // move to next item in script
                }

                // now we have a payoff which we may store in the map
                typename std::map<std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find(var);
                if (it == payoffs_.end()) { // insert a new element
                    payoffs_.insert(std::make_pair(var, p));
                    scriptLog_.push_back(std::string("Insert line " + std::to_string(k) + ": '" + line + "'"));
                    continue;
                }
                if (overwrite) {
                    it->second = p;
                    scriptLog_.push_back(std::string("Replace line " + std::to_string(k) + ": '" + line + "'"));
                    continue;
                }
                // if we end up here we have a valid payoff but are not allowed to overwrite existing map entry
                scriptLog_.push_back(std::string("Error line " + std::to_string(k) + ": '" + var + "' can not be replaced"));
            }
            if (script.size() == 0) { // in this case the previous loop was not executed and we just print some help details
                scriptLog_.push_back(std::string("we implement the following non-recursive grammar                                     "));
                scriptLog_.push_back(std::string("                                                                                "));
                scriptLog_.push_back(std::string("line  =  var '=' expr                                                                "));
                scriptLog_.push_back(std::string("var   =  [a-zA-Z][a-zA-Z0-9]*           { RegEx }                                    "));
                scriptLog_.push_back(std::string("expr  =  operator | function | payoff   { apply from left to right }                 "));
                scriptLog_.push_back(std::string("                                                                                "));
                scriptLog_.push_back(std::string("operator   =  operator1 | operator2                                                  "));
                scriptLog_.push_back(std::string("operator1  =  ['+' | '-'] payoff                                                     "));
                scriptLog_.push_back(std::string("operator2  =  payoff ['+' | '-' | '*' |                                              "));
                scriptLog_.push_back(std::string("                      '==' | '!=' | '<=' |'<' | '>=' | '>' | '&&' | '||' ] payoff    "));
                scriptLog_.push_back(std::string("                                                                                "));
                scriptLog_.push_back(std::string("function   =  function3 | function2 | function1                                      "));
                scriptLog_.push_back(std::string("function3  =  fname3 '(' payoff ',' payoff ',' payoff ')'                            "));
                scriptLog_.push_back(std::string("function2  =  fname2 '(' payoff ',' payoff ')'                                       "));
                scriptLog_.push_back(std::string("function1  =  fname1 '(' payoff ')'                                                  "));
                scriptLog_.push_back(std::string("                                                                                "));
                scriptLog_.push_back(std::string("fname3     =  'IfThenElse'                                                           "));
                scriptLog_.push_back(std::string("fname2     =  'Min' | 'Max' | 'Pay'                                                  "));
                scriptLog_.push_back(std::string("fname1     =  'Cache'                                                                "));
                scriptLog_.push_back(std::string("                                                                                "));
                scriptLog_.push_back(std::string("payoff  =  number | string              { try double conversion and lookup in map }  "));
            }
        }

        // compile fixed cash flow or lookup in map
        inline ext::shared_ptr<PayoffType> payoff(const std::string expr, const Size lineNr) {
            ActiveType amount;
            bool isFixed = to_Number(expr, amount);
            if (isFixed) {
                scriptLog_.push_back(std::string("Payoff line " + std::to_string(lineNr) + ": '" + boost::lexical_cast<std::string>(amount) + "' is fixed amount"));
                return ext::shared_ptr<PayoffType>(new typename MCBase::FixedAmount(amount));
            }
            typename std::map<std::string, ext::shared_ptr<PayoffType> >::iterator it = payoffs_.find(expr);
            if (it != payoffs_.end()) {
                scriptLog_.push_back(std::string("Payoff line " + std::to_string(lineNr) + ": '" + expr + "' is in map"));
                return it->second;
            }
            // if we end up here no conversion was successfull
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + expr + "' is no payoff"));
            return 0;
        }

        // compile single operand function
        inline ext::shared_ptr<PayoffType> function1(const std::string fname, const std::string operand, const Size lineNr) {
            ext::shared_ptr<PayoffType> p = payoff(operand, lineNr);
            if (!p) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + operand + "' is no valid operand"));
                return 0;
            }
            boost::smatch what;
            if (boost::regex_match(fname, what, boost::regex("Cache")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Cache(p));
            // if we end up here the function name is not valid
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + fname + "' is no valid unary function name"));
            return 0;
        }

        // compile dual operand function
        inline ext::shared_ptr<PayoffType> function2(const std::string fname, const std::string oper1, const std::string oper2, const Size lineNr) {
            ext::shared_ptr<PayoffType> p1 = payoff(oper1, lineNr);
            if (!p1) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper1 + "' is no valid operand"));
                return 0;
            }
            ext::shared_ptr<PayoffType> p2 = payoff(oper2, lineNr);
            if (!p2) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper2 + "' is no valid operand"));
                return 0;
            }
            boost::smatch what;
            if (boost::regex_match(fname, what, boost::regex("Min")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Min(p1, p2));
            if (boost::regex_match(fname, what, boost::regex("Max")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Max(p1, p2));
            if (boost::regex_match(fname, what, boost::regex("Pay"))) {
                DateType t;
                if (to_Number(oper2, t)) return ext::shared_ptr<PayoffType>(new typename MCBase::Pay(p1, t)); // usual application
                return ext::shared_ptr<PayoffType>(new typename MCBase::Pay(p1, p2->observationTime())); // fall back
            }
            // if we end up here the function name is not valid
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + fname + "' is no valid binary function name"));
            return 0;
        }

        // compile three operand function
        inline ext::shared_ptr<PayoffType> function3(const std::string fname, const std::string oper1, const std::string oper2, const std::string oper3, const Size lineNr) {
            ext::shared_ptr<PayoffType> p1 = payoff(oper1, lineNr);
            if (!p1) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper1 + "' is no valid operand"));
                return 0;
            }
            ext::shared_ptr<PayoffType> p2 = payoff(oper2, lineNr);
            if (!p2) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper2 + "' is no valid operand"));
                return 0;
            }
            ext::shared_ptr<PayoffType> p3 = payoff(oper3, lineNr);
            if (!p3) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper3 + "' is no valid operand"));
                return 0;
            }
            boost::smatch what;
            if (boost::regex_match(fname, what, boost::regex("IfThenElse")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::IfThenElse(p1, p2, p3));
            // if we end up here the function name is not valid
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + fname + "' is no valid function name"));
            return 0;
        }

        // compile unary operators
        inline ext::shared_ptr<PayoffType> operator1(const std::string opname, const std::string operand, const Size lineNr) {
            ext::shared_ptr<PayoffType> p = payoff(operand, lineNr);
            if (!p) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + operand + "' is no valid operand"));
                return 0;
            }
            boost::smatch what;
            if (boost::regex_match(opname, what, boost::regex("\\+"))) return p;
            if (boost::regex_match(opname, what, boost::regex("-"))) return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(-1.0, p, 0));
            // if we end up here the function name is not valid
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + opname + "' is no valid unary operator name"));
            return 0;
        }

        // compile binary operators
        inline ext::shared_ptr<PayoffType> operator2(const std::string opname, const std::string oper1, const std::string oper2, const Size lineNr) {
            ext::shared_ptr<PayoffType> p1 = payoff(oper1, lineNr);
            if (!p1) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper1 + "' is no valid operand"));
                return 0;
            }
            ext::shared_ptr<PayoffType> p2 = payoff(oper2, lineNr);
            if (!p2) {
                scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + oper2 + "' is no valid operand"));
                return 0;
            }
            boost::smatch what;
            if (boost::regex_match(opname, what, boost::regex("\\+")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(1.0, p1, p2));
            if (boost::regex_match(opname, what, boost::regex("-")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Axpy(-1.0, p2, p1));
            if (boost::regex_match(opname, what, boost::regex("\\*")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Mult(p1, p2));
            if (boost::regex_match(opname, what, boost::regex("==|!=|<|<=|>|>=|&&|\\|\\|")))
                return ext::shared_ptr<PayoffType>(new typename MCBase::Logical(p1, p2, opname));
            // if we end up here the function name is not valid
            scriptLog_.push_back(std::string("Error line " + std::to_string(lineNr) + ": '" + opname + "' is no valid binary operator name"));
            return 0;
        }

    };

}

#endif  /* ifndef quantlib_templatemcscript_hpp */


//
// Copyright 2005-2007 Adobe Systems Incorporated
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
#ifndef BOOST_GIL_EXTENSION_NUMERIC_AFFINE_HPP
#define BOOST_GIL_EXTENSION_NUMERIC_AFFINE_HPP

#include <boost/gil/point.hpp>

namespace boost { namespace gil {

////////////////////////////////////////////////////////////////////////////////////////
///
/// Simple matrix to do 2D affine transformations. It is actually 3x3 but the last column is [0 0 1]
///
////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
class matrix3x2 {
public:
    matrix3x2() : a(1), b(0), c(0), d(1), e(0), f(0) {}
    matrix3x2(T A, T B, T C, T D, T E, T F) : a(A),b(B),c(C),d(D),e(E),f(F) {}
    matrix3x2(const matrix3x2& mat) : a(mat.a), b(mat.b), c(mat.c), d(mat.d), e(mat.e), f(mat.f) {}
    matrix3x2& operator=(const matrix3x2& m)           { a=m.a; b=m.b; c=m.c; d=m.d; e=m.e; f=m.f; return *this; }

    matrix3x2& operator*=(const matrix3x2& m)          { (*this) = (*this)*m; return *this; }

    static matrix3x2 get_rotate(T rads)                { T c=std::cos(rads); T s=std::sin(rads); return matrix3x2(c,s,-s,c,0,0); }
    static matrix3x2 get_translate(point<T> const& t)
    {
        return matrix3x2(1, 0, 0, 1, t.x, t.y);
    }
    static matrix3x2 get_translate(T x, T y)           { return matrix3x2(1  ,0,0,1  ,x,  y  ); }
    static matrix3x2 get_scale(point<T> const& s)
    {
        return matrix3x2(s.x, 0, 0, s.y, 0, 0);
    }
    static matrix3x2 get_scale(T x, T y)           { return matrix3x2(x,  0,0,y,  0  ,0  ); }
    static matrix3x2 get_scale(T s)                { return matrix3x2(s  ,0,0,s  ,0  ,0  ); }

    T a,b,c,d,e,f;
};

template <typename T> BOOST_FORCEINLINE
matrix3x2<T> operator*(const matrix3x2<T>& m1, const matrix3x2<T>& m2) {
    return matrix3x2<T>(
                m1.a * m2.a + m1.b * m2.c,
                m1.a * m2.b + m1.b * m2.d,
                m1.c * m2.a + m1.d * m2.c,
                m1.c * m2.b + m1.d * m2.d,
                m1.e * m2.a + m1.f * m2.c + m2.e,
                m1.e * m2.b + m1.f * m2.d + m2.f );
}

template <typename T, typename F>
BOOST_FORCEINLINE
point<F> operator*(point<T> const& p, matrix3x2<F> const& m)
{
    return { m.a*p.x + m.c*p.y + m.e, m.b*p.x + m.d*p.y + m.f };
}

////////////////////////////////////////////////////////////////////////////////////////
/// Define affine mapping that transforms the source coordinates by the affine transformation
////////////////////////////////////////////////////////////////////////////////////////
/*
template <typename MapFn>
concept MappingFunctionConcept {
    typename mapping_traits<MapFn>::result_type;   where PointNDConcept<result_type>;

    template <typename Domain> { where PointNDConcept<Domain> }
    result_type transform(MapFn&, const Domain& src);
};
*/

template <typename T> struct mapping_traits;

template <typename F>
struct mapping_traits<matrix3x2<F>>
{
    using result_type =  point<F>;
};

template <typename F, typename F2>
BOOST_FORCEINLINE
point<F> transform(matrix3x2<F> const& mat, point<F2> const& src)
{
    return src * mat;
}

}} // namespace boost::gil

#endif


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

#include "precomp.hpp"

// Eigen
#include <Eigen/Core>

// OpenCV
#include <opencv2/core/eigen.hpp>
#include <opencv2/sfm/robust.hpp>
#include <opencv2/sfm/numeric.hpp>

// libmv headers
#include "libmv/multiview/robust_fundamental.h"

using namespace std;

namespace cv
{
namespace sfm
{

// TODO: unify algorithms
template<typename T>
double
fundamentalFromCorrespondences8PointRobust( const Mat_<T> &_x1,
                                            const Mat_<T> &_x2,
                                            const double max_error,
                                            Mat_<T> _F,
                                            std::vector<int> &_inliers,
                                            const double outliers_probability )
{
  libmv::Mat x1, x2;
  libmv::Mat3 F;
  libmv::vector<int> inliers;

  cv2eigen( _x1, x1 );
  cv2eigen( _x2, x2 );

  T solution_error =
    libmv::FundamentalFromCorrespondences8PointRobust( x1, x2, max_error, &F, &inliers, outliers_probability );

  eigen2cv( F, _F );

  // transform from libmv::vector to std::vector
  int n = inliers.size();
  _inliers.resize(n);
  for( int i=0; i < n; ++i )
  {
    _inliers[i] = inliers.at(i);
  }

  return static_cast<double>(solution_error);
}

double
fundamentalFromCorrespondences8PointRobust( InputArray _x1,
                                            InputArray _x2,
                                            double max_error,
                                            OutputArray _F,
                                            OutputArray _inliers,
                                            double outliers_probability )
{
  const Mat x1 = _x1.getMat(), x2 = _x2.getMat();
  const int depth =  x1.depth();
  CV_Assert(x1.size() == x2.size() && (depth == CV_32F || depth == CV_64F));

  _F.create(3, 3, depth);

  Mat F = _F.getMat();
  std::vector<int>& inliers = *(std::vector<int>*)_inliers.getObj();

  double solution_error = 0.0;

  // type
  if( depth == CV_32F )
  {
    solution_error =
      fundamentalFromCorrespondences8PointRobust<float>(
        x1, x2, max_error, F, inliers, outliers_probability);
  }
  else
  {
    solution_error =
      fundamentalFromCorrespondences8PointRobust<double>(
        x1, x2, max_error, F, inliers, outliers_probability);
  }

  return solution_error;
}

template<typename T>
double
fundamentalFromCorrespondences7PointRobust( const Mat_<T> &_x1,
                                            const Mat_<T> &_x2,
                                            const double max_error,
                                            Mat_<T> _F,
                                            std::vector<int> &_inliers,
                                            const double outliers_probability )
{
  libmv::Mat x1, x2;
  libmv::Mat3 F;
  libmv::vector<int> inliers;

  cv2eigen( _x1, x1 );
  cv2eigen( _x2, x2 );

  T solution_error =
    libmv::FundamentalFromCorrespondences7PointRobust( x1, x2, max_error, &F, &inliers, outliers_probability );

  eigen2cv( F, _F );

  // transform from libmv::vector to std::vector
  int n = inliers.size();
  _inliers.resize(n);
  for( int i=0; i < n; ++i )
  {
    _inliers[i] = inliers.at(i);
  }

  return static_cast<double>(solution_error);
}

double
fundamentalFromCorrespondences7PointRobust( InputArray _x1,
                                            InputArray _x2,
                                            double max_error,
                                            OutputArray _F,
                                            OutputArray _inliers,
                                            double outliers_probability )
{
  const Mat x1 = _x1.getMat(), x2 = _x2.getMat();
  const int depth =  x1.depth();
  CV_Assert(x1.size() == x2.size() && (depth == CV_32F || depth == CV_64F));

  _F.create(3, 3, depth);

  Mat F = _F.getMat();
  std::vector<int>& inliers = *(std::vector<int>*)_inliers.getObj();

  double solution_error = 0.0;

  // type
  if( depth == CV_32F )
  {
    solution_error =
      fundamentalFromCorrespondences7PointRobust<float>(
        x1, x2, max_error, F, inliers, outliers_probability);
  }
  else
  {
    solution_error =
      fundamentalFromCorrespondences7PointRobust<double>(
        x1, x2, max_error, F, inliers, outliers_probability);
  }

  return solution_error;
}

} /* namespace sfm */
} /* namespace cv */


#include <Engine/MeshEdit/CParameterize.h>
#include <Engine/MeshEdit/ARAP.h>
#include <Engine/Primitive/TriMesh.h>
#include <Engine/Scene/CmptGeometry.h>
#include <Engine/Scene/CmptMaterial.h>
#include <Engine/Material/BSDF_Frostbite.h>
#include <Eigen/SVD>

using namespace Ubpa;
using namespace Eigen;

ARAP::ARAP(Ptr<SObj> triMeshObj, Ptr<TriMesh> triMesh, bool is_tex)
	: Paramaterize(triMeshObj, triMesh, is_tex, 0, 1)/* ASAP(triMeshObj, triMesh, true)*/ // Circle boundary and Naive mode
{
	this->is_tex = is_tex;
	Init(triMeshObj, triMesh);
}

void ARAP::Clear()
{
	ARAP_coeff.clear();
	Lt_array.clear();
	triangle_points.clear();
}

bool ARAP::Init(Ptr<SObj> triMeshObj, Ptr<TriMesh> triMesh)
{
	ARAP_mat_A = SparseMatrix<double>(nV , nV);
	ARAP_mat_A.setZero();
	ARAP_mat_b = MatrixXd(nV, 2);
	ARAP_mat_b.setZero();
	//Initialize 1.map 3D triangles to 2d
	CongruentMapping2D();

	// Initialize 2. Select anchor points
	auto triangle = heMesh->Polygons().back();
	auto v1 = triangle->BoundaryVertice()[0];
	anchor_v1_idx = heMesh->Index(v1);
	auto v2 = triangle->BoundaryVertice()[1];
	anchor_v2_idx = heMesh->Index(v2);
	size_t tri_idx = heMesh->Index(triangle);
	//anchor_pos1 = pointf2(points2d[tri_idx][v1][0], points2d[tri_idx][v1][1]);
	//anchor_pos2 = pointf2(points2d[tri_idx][v2][0], points2d[tri_idx][v2][1]);
	anchor_pos2 = pointf2(1, 1);
	// Initialize 3. set coefficients of A
	setCoefficientA(anchor_v1_idx, anchor_v2_idx);

	//Initialize 4. get initial parameterization
	this->Run();

	return true;
}

bool ARAP::RunARAP(int iter_n, double error_threshold = 0.01, int debug=5)
{
	// set debug mode
	if (debug < 50) this->is_debug = false;
	else this->is_debug = true;

	// do local/global iteration
	cout <<"iter:" << iter_n << endl;

	for (int i = 0; i < iter_n; i++)
	{
		localupdate();
		double max_error = globalupdate();
		cout << "iter:" << i << " error: " << max_error << endl;
		if (max_error < error_threshold) break;
	}

	// Finally, half-edge structure -> triangle mesh, end.
	vector<pointf3> positions;
	vector<unsigned> indice;
	positions.reserve(nV);
	indice.reserve(3 * nT);
	for (auto v : heMesh->Vertices())
		positions.push_back(v->pos.cast_to<pointf3>());
	for (auto f : heMesh->Polygons()) { // f is triangle
		for (auto v : f->BoundaryVertice()) // vertices of the triangle
			indice.push_back(static_cast<unsigned>(heMesh->Index(v)));
	}

	if (this->is_tex)
	{
		this->triMesh->Update(texCoor);
	}
	else
	{
		this->triMesh->Update(positions);
	}
	return true;
}

void ARAP::localupdate()
{
	getTrianglePoints();  // get the newest iteration result from trangle_points
	getLt();
}

double ARAP::globalupdate()
{
	// first, set b
	setb(anchor_v1_idx, anchor_pos1, anchor_v2_idx, anchor_pos2);
	double max_error = -1 ;
	// Then solve the equation
	ARAP_solution = ARAP_solver.solve(ARAP_mat_A.transpose() * ARAP_mat_b);

	if (is_debug) {
		//cout << MatrixXd(ARAP_mat_A) << endl << endl; // DEBUG
		//cout << "b:" << endl << ARAP_mat_b << endl << endl;
		//cout << "solu:" << endl << ARAP_solution << endl << endl;
	}

	// End, update textCoordinate
	this->texCoor.clear();
	for (size_t i = 0; i < nV; i++)
	{
		double dist = pointf3::distance(pointf3(ARAP_solution(i, 0), ARAP_solution(i, 1), 0.0), heMesh->Vertices()[i]->pos.cast_to<pointf3>());
		if (max_error < 0)
		{
			max_error = dist;
		}
		else if(max_error < dist)
		{
			max_error = dist;
		}

		heMesh->Vertices()[i]->pos[0] = ARAP_solution(i, 0);
		heMesh->Vertices()[i]->pos[1] = ARAP_solution(i, 1);
		heMesh->Vertices()[i]->pos[2] = 0.0;
		// update tex coordinates
		texCoor.push_back(pointf2(ARAP_solution(i, 0), ARAP_solution(i, 1)));
	}

	return max_error;
}

/* Get newest u per iteration */
void ARAP::getTrianglePoints()
{
	triangle_points.clear();	// remember to clear
	for (auto triangle : heMesh->Polygons())
	{
		if(triangle != nullptr)
		{
			this->triangle_points.push_back(triangle->BoundaryVertice());
		}
	}
}

void ARAP::getLt()
{
	assert(triangle_points.size() == nT);
	Lt_array.clear();

	for (size_t t = 0; t < nT; t++)
	{
		auto vec_u = triangle_points[t];
		auto mapped_u = points2d[t];
		Matrix2d St;
		St.setZero();

		// get St
		for (int i = 0; i < 3; i++)
		{
			V* u0 = vec_u[i];
			V* u1 = vec_u[(i+1)%3]; // % ?
			MatrixXd delta_u(2, 1);
			delta_u <<
				u0->pos[0] - u1->pos[0], u0->pos[1] - u1->pos[1];

			pointf3 x0 = mapped_u[u0];
			pointf3 x1 = mapped_u[u1];
			MatrixXd delta_x(2, 1);
			delta_x <<
				x0[0] - x1[0], x0[1] - x1[1];

			double cot = getCotan(t, vec_u[(i + 2) % 3]);

			St += cot * delta_u * delta_x.transpose();
		}

		// Do SVD Composition on St
		JacobiSVD<MatrixXd> svd(St, ComputeThinU | ComputeThinV);

		Matrix2d Lt = svd.matrixU() * svd.matrixV().transpose(); // Lt = U * V^T
		/*cout << svd.matrixV().transpose() << endl << endl;*/ // DEBUG
		//if (Lt.determinant() < 0 )
		//{
		//	if (is_debug) {
		//		//cout << "before" << Lt.determinant() << endl;
		//	}
		//	Matrix2d newV;
		//	newV <<
		//		svd.matrixV().transpose()(0, 0), svd.matrixV().transpose()(0, 1),
		//		-(svd.matrixV().transpose()(1, 0)), -(svd.matrixV().transpose()(1, 1));
		//	if (is_debug) {
		//		//cout << newV << endl;
		//	}
		//	Lt = svd.matrixU() * newV;
		//	assert(Lt.determinant() > 0);
		//}

		if (is_debug)
		{
			cout << Lt.determinant() << endl;
			//cout << svd.singularValues() << endl << endl;
		}

		//cout << Lt << endl; // DEBUG

		//Lt << 1, 0, 0, 1; // DEBUG

		Lt_array.push_back(Lt);

	}
	assert(Lt_array.size() == nT);
}

void ARAP::setb(size_t idx1, pointf2 pos1, size_t idx2, pointf2 pos2)
{
	ARAP_mat_b.setZero();

	// two anchor points
	//ARAP_mat_b(idx1, 0) = pos1[0];  // x
	//ARAP_mat_b(idx1, 1) = pos1[1]; // y

	ARAP_mat_b(idx2, 0) = pos2[0];  // x
	ARAP_mat_b(idx2, 1) = pos2[1]; // y

	// Other non-anchor points
	for (size_t i = 0; i < nV; i++)
	{
		if (/*i != idx1 && */i != idx2) {
			auto v = heMesh->Vertices()[i];
			MatrixXd b(2, 1);
			b.setZero();
			//cout << "v idx: " << heMesh->Index(v) << endl<<endl;
			// traverse adjecent vertrices
			for (auto adj_v : v->AdjVertices())
			{
				size_t adj_idx = heMesh->Index(adj_v);  // get index of adj_v
				// get adjacent triangles
				auto e = v->EdgeWith(adj_v);
				auto he1 = e->HalfEdge();
				auto he2 = e->HalfEdge()->Pair();

				// get Lt and cot
				auto triangle1 = he1->Polygon();
				if (triangle1 != nullptr)
				{
					auto tri_v1 = he1->Next()->End();
					assert(heMesh->Index(tri_v1) != heMesh->Index(v)
						&& heMesh->Index(tri_v1) != heMesh->Index(adj_v));

					size_t tri_idx = heMesh->Index(triangle1);
					double cot1 = getCotan(tri_idx, tri_v1);
					map<V*, pointf3> mapped_v = this->points2d[tri_idx]; // congruent mapping of triangle1
					MatrixXd Lt = Lt_array[tri_idx];
					MatrixXd delta_x(2, 1);
					delta_x <<
						mapped_v[v][0] - mapped_v[adj_v][0],
						mapped_v[v][1] - mapped_v[adj_v][1];
					/*cout << "cot1:" << cot1 << endl;
					cout << "mapped_v[v]:" << mapped_v[v][0] <<"," << mapped_v[v][1] << endl;
					cout << "mapped_[adj_v]:" << mapped_v[adj_v][0] << "," << mapped_v[adj_v][1] << endl;
					cout << "cot1 * Lt * delta_x:" << cot1 * Lt * delta_x << endl;
					cout << endl;*/

					b += cot1 * Lt * delta_x;
				}

				auto triangle2 = he2->Polygon();
				if (triangle2 != nullptr)
				{
					auto tri_v2 = he2->Next()->End();
					assert(heMesh->Index(tri_v2) != heMesh->Index(v)
						&& heMesh->Index(tri_v2) != heMesh->Index(adj_v));

					size_t tri_idx = heMesh->Index(triangle2);
					double cot2 = getCotan(tri_idx, tri_v2);
					map<V*, pointf3> mapped_v = this->points2d[tri_idx]; // congruent mapping of triangle1
					MatrixXd Lt = Lt_array[tri_idx];
					MatrixXd delta_x(2, 1);
					delta_x <<
						mapped_v[v][0] - mapped_v[adj_v][0],
						mapped_v[v][1] - mapped_v[adj_v][1];

					/*cout << "cot2:" << cot2 << endl;
					cout << "mapped_v[v]:" << mapped_v[v][0] << "," << mapped_v[v][1] << endl;
					cout << "mapped_v[adj_v]:" << mapped_v[adj_v][0] << "," << mapped_v[adj_v][1] << endl;
					cout << "cot2 * Lt * delta_x:" << cot2 * Lt * delta_x << endl;
					cout << endl;*/
					b += cot2 * Lt * delta_x;
				}
			}
			ARAP_mat_b(i, 0) = b(0); // x
			ARAP_mat_b(i, 1) = b(1); // y
		}
	}
}

void ARAP::setCoefficientA(size_t idx1, size_t idx2)
{
	// Two Anchor points
	//ARAP_coeff.push_back(Eigen::Triplet<double>(idx1, idx1, 1));
	ARAP_coeff.push_back(Eigen::Triplet<double>(idx2, idx2, 1));

	// Other non-anchor points
	for (size_t i = 0; i < nV; i++)
	{
		if (/*i != idx1 &&*/ i != idx2) {
			auto v = heMesh->Vertices()[i];
			double cotan_sum = 0.0;

			// traverse adjacent vertrices
			for (auto adj_v : v->AdjVertices())
			{
				size_t adj_idx = heMesh->Index(adj_v);

				// To get cotan
				auto e = v->EdgeWith(adj_v);
				auto he1 = e->HalfEdge();

				double cot1 = 0.0;
				if (he1->Polygon() != nullptr)
				{
					auto tri1_idx = heMesh->Index(he1->Polygon()); // get index of adjacent triangle
					try
					{
						auto tri_v1 = he1->Next()->End(); // get vertix of adjacent triangle
						assert(heMesh->Index(tri_v1) != heMesh->Index(v)
							&& heMesh->Index(tri_v1) != heMesh->Index(adj_v));

						cot1 = getCotan(tri1_idx, tri_v1);
					}
					catch (const std::exception& e)
					{
						cout << "cannot find cot 1 in map. " << endl;
					}
				}
				auto he2 = e->HalfEdge()->Pair();

				double cot2 = 0.0;
				if (he2->Polygon() != nullptr)
				{
					auto tri2_idx = heMesh->Index(he2->Polygon());
					try
					{
						auto tri_v2 = he2->Next()->End();
						assert(heMesh->Index(tri_v2) != heMesh->Index(v)
							&& heMesh->Index(tri_v2) != heMesh->Index(adj_v));
						cot2 = getCotan(tri2_idx, tri_v2);
					}
					catch (const std::exception & e)
					{
						cout << "cannot find cot 2 in map. " << endl;
					}
				}

				ARAP_coeff.push_back(Eigen::Triplet<double>(i, adj_idx, -(cot1 + cot2)));

				cotan_sum += (cot1 + cot2);
			}

			ARAP_coeff.push_back(Eigen::Triplet<double>(i, i, cotan_sum));
		}
	}
	ARAP_mat_A.setFromTriplets(ARAP_coeff.begin(), ARAP_coeff.end());
	// pre-computation
	ARAP_mat_A.makeCompressed();
	ARAP_solver.compute(ARAP_mat_A.transpose() * ARAP_mat_A);
}


/*
 * This file is part of the Interpolated Polyline (https://github.com/fzi-forschungszentrum-informatik/P3IV),
 * copyright by FZI Forschungszentrum Informatik, licensed under the BSD-3 license (see LICENSE file in main directory)
 */

#pragma once

#include <chrono>
#include <boost/math/interpolators/barycentric_rational.hpp>

namespace p3iv_utils {

std::vector<double> chrono2double(const std::vector<std::chrono::milliseconds>& t) {
    std::vector<double> x;
    x.reserve(t.size());
    for (auto t_ : t) {
        x.push_back(t_.count());
    }
    return x;
}

template <typename T>
inline std::vector<T> interpolate(const std::vector<T>& xBase,
                                  const std::vector<T>& yBase,
                                  const std::vector<T>& xIntrp) {

    boost::math::barycentric_rational<T> interpolant(xBase.data(), yBase.data(), yBase.size());
    std::vector<T> yIntrp;
    yIntrp.reserve(xIntrp.size());

    for (auto& x : xIntrp) {
        yIntrp.push_back(interpolant(x));
    }

    return yIntrp;
}

template <typename T>
inline std::vector<T> interpolate(const std::vector<std::chrono::milliseconds>& tBase,
                                  const std::vector<T>& yBase,
                                  const std::vector<std::chrono::milliseconds>& tIntrp) {

    std::vector<double> xBase = chrono2double(tBase);
    std::vector<double> xIntrp = chrono2double(tIntrp);

    auto yIntrp = interpolate(xBase, yBase, xIntrp);

    return yIntrp;
}

} // namespace p3iv_utils


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2016 Francisca Gil Ureta <gilureta@cs.nyu.edu>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "segment_segment_intersect.h"

#include <Eigen/Geometry>

template<typename DerivedSource, typename DerivedDir>
IGL_INLINE bool igl::segment_segment_intersect(
  const Eigen::MatrixBase <DerivedSource> &p,
  const Eigen::MatrixBase <DerivedDir> &r,
  const Eigen::MatrixBase <DerivedSource> &q,
  const Eigen::MatrixBase <DerivedDir> &s,
  double &a_t,
  double &a_u,
  double eps
)
{
  // http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
  // Search intersection between two segments
  // p + t*r :  t \in [0,1]
  // q + u*s :  u \in [0,1]

  // p + t * r = q + u * s  // x s
  // t(r x s) = (q - p) x s
  // t = (q - p) x s / (r x s)

  // (r x s) ~ 0 --> directions are parallel, they will never cross
  Eigen::Matrix<typename DerivedDir::Scalar, 1, 3> rxs = r.cross(s);
  if (rxs.norm() <= eps)
    return false;

  int sign;

  double u;
  // u = (q − p) × r / (r × s)
  Eigen::Matrix<typename DerivedDir::Scalar, 1, 3> u1 = (q - p).cross(r);
  sign = ((u1.dot(rxs)) > 0) ? 1 : -1;
  u = u1.norm() / rxs.norm();
  u = u * sign;

  double t;
  // t = (q - p) x s / (r x s)
  Eigen::Matrix<typename DerivedDir::Scalar, 1, 3> t1 = (q - p).cross(s);
  sign = ((t1.dot(rxs)) > 0) ? 1 : -1;
  t = t1.norm() / rxs.norm();
  t = t * sign;

  a_t = t;
  a_u = u;

  if ((u - 1.) > eps || u < -eps)
    return false;

  if ((t - 1.) > eps || t < -eps)
    return false;

  return true;
};

#ifdef IGL_STATIC_LIBRARY
template bool igl::segment_segment_intersect<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, double&, double&, double);
#endif


#include <iostream>
#include <armadillo>
#include "../config.hpp"

using namespace std;
using namespace arma;

pair<vec, double> entrenarPerceptron(const mat& datos,
                                     int nEpocas,
                                     double tasaAprendizaje,
                                     double toleranciaError);
double errorPerceptron(const vec& pesos,
                   const mat& patrones,
                   const vec& salidaDeseada);

using Particion = pair<mat, mat>;
vector<Particion> particionar(mat datos, int nParticiones, double porcentajeEnt);

int main()
{
    arma_rng::set_seed_random();
    mat datos;

    // Primera parte del ejercicio (spheres1d)
    {
        datos.load(config::sourceDir + "/guia1/icgtp1datos/spheres1d10.csv");

        // FIXME:
        // 1: Las particiones tendrían que contener los índices a usar, no replicar los datos.
        // 2: Las particiones tendrían que ser generadas una sola vez, guardadas
        //    en un archivo, y luego levantarlas para que de esta manera poder repetir
        //    sesiones de entrenamiento/prueba con distintos clasificadores en
        //    los mismos datos. Incluso fijar la semilla de generación de núeros aleatorios.
        const vector<Particion> particiones = particionar(datos, 5, 80);

        vec errores;
        errores.set_size(particiones.size());
        int i = 0;

        for (const Particion& particion : particiones) {
            vec pesos;
            tie(pesos, ignore) = entrenarPerceptron(particion.first, 100, 0.1, 20);

            const double tasaError = errorPerceptron(pesos,
                                                 particion.second.head_cols(3),
                                                 particion.second.tail_cols(1));
            errores[i++] = tasaError;
        }

        cout << "La validación cruzada del perceptron en spheres1d10 da como error:\n"
             << "Media: " << mean(errores) << '\n'
             << "Varianza: " << var(errores) << endl;
    }

    // Segunda parte del ejercicio (spheres2d)
    vector<string> archivos = {config::sourceDir + "/guia1/icgtp1datos/spheres2d10.csv",
                               config::sourceDir + "/guia1/icgtp1datos/spheres2d50.csv",
                               config::sourceDir + "/guia1/icgtp1datos/spheres2d70.csv"};

    for (string archivo : archivos) {
        datos.load(archivo);
        const vector<Particion> particiones = particionar(datos, 10, 80);

        vec errores;
        errores.set_size(particiones.size());
        int i = 0;

        for (const Particion& particion : particiones) {
            vec pesos;
            tie(pesos, ignore) = entrenarPerceptron(particion.first, 100, 0.1, 1);

            const double tasaError = errorPerceptron(pesos,
                                                 particion.second.head_cols(3),
                                                 particion.second.tail_cols(1));
            errores[i++] = tasaError;
        }

        // FIXME:
        // Para que los resultados sean más informativos habría que proporcionar tambíen lo siguiente:
        // (para cada partición)
        // - Epocas que demoró en converger
        // - Error
        // Esto sirve para detectar (por ejemplo):
        // - Si siempre está terminando el entrenamiento por límite de épocas
        // - Si los errores están dando siempre altos
        //      Esto puede querer decir que la tolerancia de error que le estamos pidiendo es
        //      demasiado alta.
        cout << "La validación cruzada del perceptron en " << archivo << " da como error:\n"
             << "Media: " << mean(errores) << '\n'
             << "Varianza: " << var(errores) << endl;
    }

    return 0;
}

// Lo siguiente es validación cruzada clásica
vector<Particion> particionar(mat datos, int nParticiones, double porcentajeEnt)
{
    vector<Particion> particiones;
    const int nPatronesEnt = datos.n_rows * porcentajeEnt / 100;

    for (int i = 0; i < nParticiones; ++i) {
        datos = shuffle(datos); // Mezcla las filas de los datos
        particiones.push_back({datos.head_rows(nPatronesEnt),
                               datos.tail_rows(datos.n_rows - nPatronesEnt)});
    }

    return particiones;
}

namespace ic {
int sign(double numero)
{
    if (numero >= 0)
        return 1;
    else
        return -1;
}
}

pair<vec, double> epocaPerceptron(const mat& patronesExt,
                                  const vec& salidaDeseada,
                                  double tasaAprendizaje,
                                  const vec& pesos)
{
    //Entrenamiento
    vec nuevosPesos = pesos;

    for (unsigned int i = 0; i < patronesExt.n_rows; ++i) {
        double z = dot(patronesExt.row(i), pesos);
        int y = ic::sign(z);

        // Actualizar pesos
        nuevosPesos += tasaAprendizaje * (salidaDeseada(i) - y) * patronesExt.row(i).t();
    } // Fin ciclo (Entrenamiento)

    // Validacion
    int errores = 0;

    for (unsigned int i = 0; i < patronesExt.n_rows; ++i) {
        double z = dot(patronesExt.row(i), pesos);
        int y = ic::sign(z);

        if (y != salidaDeseada(i))
            ++errores;
    }

    double tasaError = static_cast<double>(errores) / patronesExt.n_rows * 100;

    return {nuevosPesos, tasaError};
} // fin funcion Epoca

double errorPerceptron(const vec& pesos,
                   const mat& patrones,
                   const vec& salidaDeseada)
{
    // Se extiende la matriz de patrones con la entrada correspondiente al umbral
    const mat patronesExt = join_horiz(ones(patrones.n_rows) * (-1), patrones);

    int errores = 0;

    for (unsigned int i = 0; i < patronesExt.n_rows; ++i) {
        double z = dot(patronesExt.row(i), pesos);
        int y = ic::sign(z);

        if (y != salidaDeseada(i))
            ++errores;
    }

    double tasaError = static_cast<double>(errores) / patronesExt.n_rows * 100;

    return tasaError;
}

pair<vec, double> entrenarPerceptron(const mat& datos,
                                     int nEpocas,
                                     double tasaAprendizaje,
                                     double toleranciaError)
{
    const vec salidaDeseada = datos.tail_cols(1);
    // Extender la matriz de patrones con la entrada correspondiente al umbral
    const int nParametros = datos.n_cols - 1;
    const mat patronesExt = join_horiz(ones(datos.n_rows) * (-1), datos.head_cols(nParametros));

    // Inicializar pesos y tasa de error
    vec pesos = randu<vec>(patronesExt.n_cols) - 0.5;
    double tasaError = 0;

    // Ciclo de las epocas
    for (int epoca = 1; epoca <= nEpocas; ++epoca) {
        // Ciclo para una época
        tie(pesos, tasaError) = epocaPerceptron(patronesExt,
                                                salidaDeseada,
                                                tasaAprendizaje,
                                                pesos);

        if (tasaError < toleranciaError)
            break;
    }
    // Fin ciclo (epocas)

    return {pesos, tasaError};
}