#include <compliant_plugin/compliant_plugin.h>
#include <gazebo/common/Plugin.hh>
#include <gazebo/common/common.hh>
#include <gazebo/physics/physics.hh>
#include <boost/bind.hpp>
#include <math.h>
#include <string>
#include <ros/ros.h>
#include <std_msgs/Float64.h>
#include <sensor_msgs/JointState.h>
#include <compliant_plugin/state_info.h>

using namespace gazebo;
using namespace std;

/*
  This code implements the methods of the CompliantActuatorPlugin class.
  For more details please refer to the article "An Open-Source ROS-Gazebo Toolbox for Simulating Robots With Compliant Actuators"
  by Mengacci et. al., published in Frontiers in Robotics and AI in 2021.
*/

// Saturation for input torque
double CompliantActuatorPlugin::saturate(double val, double max_val)
{
    if (val > max_val) {
        val = max_val;
    }
    if (val < -max_val) {
        val = -max_val;
    }
    return val;
}

void CompliantActuatorPlugin::DCMotor::OnUpdate(const double & dT, double & tauMot, const double & tauLoad){

    tauMot = saturate(tauMot, tauMax);

    const double K = 1e3;

    if  (pos - w < -tauFric/K){
        w = pos + tauFric/K;
    }
    else if (pos - w > tauFric/K){
        w = pos - tauFric/K;
    }
    // else w = w;

    double tauFricNow = K *(pos - w);

    effort = tauMot;
    double acc = (effort - tauLoad - tauFricNow - D*vel)/ J;

    vel = vel + acc * dT;

    pos = pos + vel * dT;

    if(pos < minPos){
        pos = minPos;
        vel = 0;
        effort = std::numeric_limits<double>::quiet_NaN();
    }
    else if(pos > maxPos){
        pos = maxPos;
        vel = 0;
        effort = std::numeric_limits<double>::quiet_NaN();
    }

}

void CompliantActuatorPlugin::PIDController::OnUpdate(const double & dT, const double & pos, const double & vel){
    double e = posRef - pos;
    double de = velRef - vel;
    errInt += I * e * dT;

    effort = errInt + P * e + D * de;
}

void CompliantActuatorPlugin::tauElastic(const double & q1_in, const double & q2_in, const double & qL_in, double & t1_out, double & t2_out, double & tL_out, double & sigmaL_out){

    std::string warning_msg = "Make sure to define the virtual function for the CompliantActuatorPlugin!";
    ROS_WARN_STREAM(warning_msg);

};

void CompliantActuatorPlugin::eqPres2Refs(const double & eq_in, const double & pres_in, double & ql_out, double & q2_out){

    std::string warning_msg = "Make sure to define the virtual function for the CompliantActuatorPlugin!";
    ROS_WARN_STREAM(warning_msg);

};

void CompliantActuatorPlugin::InitParams(sdf::ElementPtr _sdf){

    std::string warning_msg = "Make sure to define the virtual function for the CompliantActuatorPlugin!";
    ROS_WARN_STREAM(warning_msg);
}

// Subscriber callbacks references
void CompliantActuatorPlugin::getRef1_callback(const std_msgs::Float64& val_1){

    ref_1 = val_1.data;
}
void CompliantActuatorPlugin::getRef2_callback(const std_msgs::Float64& val_2){

    ref_2 = val_2.data;
}

// Subscriber callback for external torque
void CompliantActuatorPlugin::getExtTau_callback(const std_msgs::Float64& e_tau){

    ext_tau = e_tau.data;
}

// DEBUG PRINT FUNCTION
void printForDebug(double tauEl_1, double tauEl_2, double tEl_L, double mot_1, double mot_2, double qL, double ref_1, double ref_2)
{
    std::cout << "T1 " << tauEl_1 << "\t";
    std::cout << "T2 " << tauEl_2 << "\t";
    std::cout << "TL " << tEl_L << "\t";

    std::cout << "M1 " << mot_1 << "\t";
    std::cout << "M2 " << mot_2 << "\t";

    std::cout << "Q " << qL << "\t";
    std::cout << "R1 " << ref_1 << "\t";
    std::cout << "R2 " << ref_2 << "\n";
}

// OPERATION MODE = 6
void CompliantActuatorPlugin::OnUpdatePIDEqPres(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    //calculate motor references
    double qR1, qR2;
    eqPres2Refs(ref_1, ref_2, qR1, qR2);

    // update controllers
    ctrl_1.posRef = qR1;
    ctrl_2.posRef = qR2;
    ctrl_1.OnUpdate(dT, mot_1.pos, mot_1.vel);
    ctrl_2.OnUpdate(dT, mot_2.pos, mot_2.vel);

    // compute old elastic torque
    double tauEl_1_old, tauEl_2_old, tEl_L_old, sigmaEl_L_old;
    tauElastic(mot_1.pos, mot_2.pos, qL, tauEl_1_old, tauEl_2_old, tEl_L_old, sigmaEl_L_old);

    //update motors
    mot_1.OnUpdate(dT, ctrl_1.effort, tauEl_1_old);
    mot_2.OnUpdate(dT, ctrl_2.effort, tauEl_2_old);

    // compute new elastic torque
    double tauEl_1, tauEl_2, tEl_L, sigmaEl_L;
    tauElastic(mot_1.pos, mot_2.pos, qL, tauEl_1, tauEl_2, tEl_L, sigmaEl_L);

    // update joint effort
    joint->SetForce(0, tEl_L);

    // publish relevant topics
    CompliantActuatorPlugin::Publish(tauEl_1, tauEl_2, tEl_L, sigmaEl_L, qL, dqL, ref_1, ref_2 );
    CompliantActuatorPlugin::PublishMotors(mot_1.pos, mot_1.vel, mot_1.effort, mot_2.pos, mot_2.vel, mot_2.effort);

    // update timer
    oldTime = t;

}

// OPERATION MODE = 5
void CompliantActuatorPlugin::OnUpdatePIDRefs(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    // update controllers
    ctrl_1.posRef = ref_1;
    ctrl_2.posRef = ref_2;
    ctrl_1.OnUpdate(dT, mot_1.pos, mot_1.vel);
    ctrl_2.OnUpdate(dT, mot_2.pos, mot_2.vel);

    // compute old elastic torque
    double tauEl_1_old, tauEl_2_old, tEl_L_old, sigmaEl_L_old;
    tauElastic(mot_1.pos, mot_2.pos, qL, tauEl_1_old, tauEl_2_old, tEl_L_old, sigmaEl_L_old);

    //update motors
    mot_1.OnUpdate(dT, ctrl_1.effort, tauEl_1_old);
    mot_2.OnUpdate(dT, ctrl_2.effort, tauEl_2_old);

    // compute new elastic torque
    double tauEl_1, tauEl_2, tEl_L, sigmaEl_L;
    tauElastic(mot_1.pos, mot_2.pos, qL, tauEl_1, tauEl_2, tEl_L, sigmaEl_L);

    // update joint effort
    joint->SetForce(0, tEl_L);

    // publish relevant topics
    CompliantActuatorPlugin::Publish(tauEl_1, tauEl_2, tEl_L, sigmaEl_L, qL, dqL, ref_1, ref_2 );
    CompliantActuatorPlugin::PublishMotors(mot_1.pos, mot_1.vel, mot_1.effort, mot_2.pos, mot_2.vel, mot_2.effort);

    // update timer
    oldTime = t;

}

// OPERATION MODE = 4
void CompliantActuatorPlugin::OnUpdateMotorTorques(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    // compute elastic torque
    double tauEl_1, tauEl_2, tEl_L, sigmaEl_L;
    tauElastic(mot_1.pos, mot_2.pos, qL, tauEl_1, tauEl_2, tEl_L, sigmaEl_L);

    //update motors
    mot_1.OnUpdate(dT, ref_1, tauEl_1);
    mot_2.OnUpdate(dT, ref_2, tauEl_2);

    // DEBUG
    // printForDebug(mot_1.effort, mot_2.effort, tEl_L, mot_1.pos, mot_2.pos, qL, ref_1, ref_2);
    // END_DEBUG

    // update joint effort
    joint->SetForce(0, tEl_L);

    // publish relevant topics
    CompliantActuatorPlugin::Publish(tauEl_1, tauEl_2, tEl_L, sigmaEl_L, qL, dqL, ref_1, ref_2);
    CompliantActuatorPlugin::PublishMotors(mot_1.pos, mot_1.vel, mot_1.effort, mot_2.pos, mot_2.vel, mot_2.effort);

    // update timer
    oldTime = t;

}

// OPERATION MODE = 3
void CompliantActuatorPlugin::OnUpdateSpringEqPres(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    //calculate motor references
    double qR1, qR2;
    eqPres2Refs(ref_1, ref_2, qR1, qR2);

    // compute new elastic torque
    double tauEl_1, tauEl_2, tEl_L, sigmaEl_L;
    tauElastic(qR1, qR2, qL, tauEl_1, tauEl_2, tEl_L, sigmaEl_L);

    // update joint effort
    joint->SetForce(0, tEl_L);

    // publish relevant topics
    CompliantActuatorPlugin::Publish(tauEl_1, tauEl_2, tEl_L, sigmaEl_L, qL, dqL, ref_1, ref_2 );

    // update timer
    oldTime = t;

}

// OPERATION MODE = 2
void CompliantActuatorPlugin::OnUpdateSpringRefs(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    // compute new elastic torque
    double tauEl_1, tauEl_2, tEl_L, sigmaEl_L;
    tauElastic(ref_1, ref_2, qL, tauEl_1, tauEl_2, tEl_L, sigmaEl_L);

    // update joint effort
    joint->SetForce(0, tEl_L);

    // publish relevant topics
    CompliantActuatorPlugin::Publish(tauEl_1, tauEl_2, tEl_L, sigmaEl_L, qL, dqL, ref_1, ref_2 );

    // update timer
    oldTime = t;

}

// OPERATION MODE = 1
void CompliantActuatorPlugin::OnUpdateLinkPID(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    // update controllers
    ctrl_1.posRef = ref_1;
    ctrl_1.OnUpdate(dT, qL, dqL);

    // update joint effort
    joint->SetForce(0, ctrl_1.effort);

    // publish relevant topics
    CompliantActuatorPlugin::Publish( std::numeric_limits<double>::quiet_NaN(),
                                      std::numeric_limits<double>::quiet_NaN(),
                                      ctrl_1.effort, std::numeric_limits<double>::quiet_NaN(),
                                      qL, dqL, ref_1, std::numeric_limits<double>::quiet_NaN() );

    // update timer
    oldTime = t;

}

// OPERATION MODE = 0
void CompliantActuatorPlugin::OnUpdateLinkTorque(const common::UpdateInfo & info){

    // retrieve simulation time
    common::Time t = info.simTime;
    double dT = (t - oldTime).Double();

    //retrieve joint state
    const double & qL = joint->Position(0);
    const double & dqL = joint->GetVelocity(0);

    // update joint effort
    joint->SetForce(0, ref_1);

    // publish relevant topics
    CompliantActuatorPlugin::Publish( std::numeric_limits<double>::quiet_NaN(),
                                      std::numeric_limits<double>::quiet_NaN(),
                                      ref_1, std::numeric_limits<double>::quiet_NaN(),
                                      qL, dqL, ref_1, std::numeric_limits<double>::quiet_NaN() );

    // update timer
    oldTime = t;

}

void CompliantActuatorPlugin::OnUpdate(const common::UpdateInfo & info){
    switch(operationMode){
    case(LinkTorque):               // operationMode = 0
        OnUpdateLinkTorque(info);
        break;
    case(LinkPID):                  // operationMode = 1
        OnUpdateLinkPID(info);
        break;
    case(SpringRefs):               // operationMode = 2
        OnUpdateSpringRefs(info);
        break;
    case(SpringEqPres):             // operationMode = 3
        OnUpdateSpringEqPres(info);
        break;
    case(MotorTorques):             // operationMode = 4
        OnUpdateMotorTorques(info);
        break;
    case(PIDRefs):                  // operationMode = 5
        OnUpdatePIDRefs(info);
        break;
    case(PIDEqPres):                // operationMode = 6
        OnUpdatePIDEqPres(info);
        break;
    }
}

void CompliantActuatorPlugin::Publish(const double & tauEl_1,
                                      const double & tauEl_2,
                                      const double & tauEl_L,
                                      const double & sigmaEl_L,
                                      const double & q,
                                      const double & dq,
                                      const double & r_1,
                                      const double & r_2 ){
  // Publish elastic torque for driving motors
  if (flag_pub_el_tau)
  {
    elastic_tau1.data = tauEl_1;
    pubEl_1.publish(elastic_tau1);
    elastic_tau2.data = tauEl_2;
    pubEl_2.publish(elastic_tau2);
  }

  // Publish whole joint state
  if (flag_pub_state)
  {
    joint_info.tau = tauEl_L;
    joint_info.stiff = sigmaEl_L;
    joint_info.q = q;
    joint_info.dq = dq;
    joint_info.ref_1 = r_1;
    joint_info.ref_2 = r_2;
    pubL_state.publish(joint_info);
  }

}

void CompliantActuatorPlugin::PublishMotors(double m1_pos, double m1_vel, double m1_eff, double m2_pos, double m2_vel, double m2_eff){
  mot_1_state.position[0] = m1_pos;
  mot_1_state.velocity[0] = m1_vel;
  mot_1_state.effort[0] = m1_eff;
  pub_mot_1_state.publish(mot_1_state);

  mot_2_state.position[0] = m2_pos;
  mot_2_state.velocity[0] = m2_vel;
  mot_2_state.effort[0] = m2_eff;
  pub_mot_2_state.publish(mot_2_state);
}

void CompliantActuatorPlugin::Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
{
  int argc = 0;
  char **argv;
  ros::init(argc, argv, "Compliant_Actuator_Plugin");

  model = _parent;

  // Make sure the ROS node for Gazebo has already been initialized
  if (!ros::isInitialized())
  {
    ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
      << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
    return;
  }

  // Retrieve namespace and control mode from a configuration file
  ros::param::get("namespace", ns_name);
  ros::param::get("T_sample", T_sample);

  // Initialize motor variables
  mot_1_state.position.resize(1);
  mot_1_state.velocity.resize(1);
  mot_1_state.effort.resize(1);
  mot_2_state.position.resize(1);
  mot_2_state.velocity.resize(1);
  mot_2_state.effort.resize(1);

  // Retrieve joint identifier and control mode from urdf tags
  joint_name =_sdf->GetElement("joint")->Get<string>();
  //act_type =_sdf->GetElement("actuator_type")->Get<string>();
  operationMode = (OperationModes) _sdf->GetElement("operation_mode")->Get<int>();
  flag_pub_el_tau =_sdf->GetElement("pub_eltau")->Get<bool>();
  flag_pub_state =_sdf->GetElement("pub_state")->Get<bool>();
  flag_sub_ext_tau =_sdf->GetElement("sub_ext_tau")->Get<bool>();

  // Everything-is-fine message
  std::string ok_msg = "CompliantActuatorPlugin on " + joint_name + " started with operation mode number [" + std::to_string(operationMode) + "]!";
  ROS_WARN_STREAM(ok_msg);

  // Retrieve joint
  joint = model->GetJoint(joint_name);

  // Compose the topic names
  CompliantActuatorPlugin::topicNames(ns_name, joint_name);

  // Initialize motors, controllers and other parameters
  InitParams(_sdf);

  // Subscribers for the joint commands
  sub_1 = n.subscribe(cmd_ref1_name, 10, &CompliantActuatorPlugin::getRef1_callback, this);
  sub_2 = n.subscribe(cmd_ref2_name, 10, &CompliantActuatorPlugin::getRef2_callback, this);
  if (flag_sub_ext_tau)
  {
    sub_ext = n.subscribe(ext_tau_sub_name, 10, &CompliantActuatorPlugin::getExtTau_callback, this);
  }

  // Publishers for the joint states
  if (flag_pub_el_tau)
  {
    pubEl_1 = n.advertise<std_msgs::Float64>(cmd_pub1_name, 500);
    pubEl_2 = n.advertise<std_msgs::Float64>(cmd_pub2_name, 500);
  }

  if ((operationMode == MotorTorques) || (operationMode == PIDRefs) || (operationMode ==  PIDEqPres))
  {
      std::string motor_state_1_name = ns_name + "/" + joint_name + "/motor_1_state";
      pub_mot_1_state = n.advertise<sensor_msgs::JointState>(motor_state_1_name, 500);

      std::string motor_state_2_name = ns_name + "/" + joint_name + "/motor_2_state";
      pub_mot_2_state = n.advertise<sensor_msgs::JointState>(motor_state_2_name, 500);
  }

  if (flag_pub_state)
  {
    pubL_state = n.advertise<compliant_plugin::state_info>(link_pub_name, 500);
  }

  updateConnection = event::Events::ConnectWorldUpdateBegin(boost::bind(&CompliantActuatorPlugin::OnUpdate, this, _1));

}

// Compose topic's names according to the type of actuators
void CompliantActuatorPlugin::topicNames(std::string ns_name, std::string joint_name)
{
  // Compose string name for the publishers removing last words from joint name
  cmd_pub1_name = ns_name + "/" + joint_name + "/torque_el1_state";
  cmd_pub2_name = ns_name + "/" + joint_name + "/torque_el2_state";

  // Compose string name for the state publisher
  link_pub_name = ns_name + "/" + joint_name + "/link_state";

  // Compose string for the subscriber of external torque
  ext_tau_sub_name = ns_name + "/" + joint_name + "/external_torque";

  switch(operationMode){
    case(LinkTorque):
        cmd_ref1_name = ns_name + "/" + joint_name + "/torque_command";
        cmd_ref2_name = ns_name + "/" + joint_name + "/disabled";
        break;
    case(LinkPID):
        cmd_ref1_name = ns_name + "/" + joint_name + "/link_command";
        cmd_ref2_name = ns_name + "/" + joint_name + "/disabled";
        break;
    case(SpringRefs):
    case(PIDRefs):
        cmd_ref1_name = ns_name + "/" + joint_name + "/reference_1";
        cmd_ref2_name = ns_name + "/" + joint_name + "/reference_2";
        break;
    case(SpringEqPres):
    case(PIDEqPres):
        cmd_ref1_name = ns_name + "/" + joint_name + "/equilibrium_position";
        cmd_ref2_name = ns_name + "/" + joint_name + "/stiffness_preset";
        break;
    case(MotorTorques):
        cmd_ref1_name = ns_name + "/" + joint_name + "/torque_m1_command";
        cmd_ref2_name = ns_name + "/" + joint_name + "/torque_m2_command";
        break;
  }

}


#include <iostream>
#include <fstream>
#include <Eigen/Eigen>
//include the bie header files
#include "material.hh"
#include "precomputed_kernel.hh"
#include "bimat_interface.hh"
#include "infinite_boundary.hh"
//include the fem header files
//#include "mesh_Abaqus_multi_faults_withAB.hpp"
#include "mesh_Abaqus_multi_faults.hpp"
#include "bcdof.hpp"
#include "bcdof_ptr.hpp"
#include "cal_ke.hpp"
#include "cal_fe_global_vary_ke.hpp"
#include "cal_ab_force.hpp"
#include "mapglobal.hpp"
#include "maplocal.hpp"
#include "cal_M.hpp"
#include "cal_T_abc.hpp"
#include "cal_Bmat.hpp"
#include "Slip_Weakening_lumpM_with_buffer.hpp"
#include "Rate_and_State_Aging.hpp"
#include "cal_slip_sliprate_angle.hpp"
#include "time_advance.hpp"
#include "BIE_correct.hpp"
#include "cal_fe_global_plastic_vary.cpp"
#include "update_disp_velocity.hpp"
#include <math.h>
using namespace Eigen;
using namespace std;

typedef Eigen::Matrix<int, -1, -1,RowMajor> MatrixXi_rm;

double time_fem=0;
double time_bie;
int main() {

    std::string filename = "wedge_50m.inp";

    std::cout<<filename<<std::endl;
   // double fault_angle  = 0/180.0*M_PI;
    int dim = 2.0;
    double x_max = 10.0e3;
    double x_min = -10.0e3;
    double dx = 50.0;
    double dy = 50.0;
    std::vector<int> BIE_top, BIE_bot;
    //std::vector<int> AB_left, AB_right;
    MatrixXd Node;
    MatrixXi_rm Element;
    // Total number of faults icluding the main fault
    // number of secondary faults + 1
    int num_faults = 1;
    std::vector<std::vector<int>> fault_nodes(2*num_faults);
    mesh_Abaqus_multi_faults(filename, Node, Element, BIE_top, BIE_bot,fault_nodes,num_faults);

    int nx_BIE = BIE_top.size();
    std::cout<<"nx_BIE="<<nx_BIE;
    int n_nodes = Node.rows();
    int n_el = Element.rows();
    int Ndofn = 2;
    int Nnel = Element.cols();
    Node = Node*1e3;
    // Material
    double density = 2670.0;
    double v_s =3.464e3;
    double v_p = 6.0e3;
    double G= pow(v_s,2)*density;
    double Lambda = pow(v_p,2)*density-2.0*G;
    double E  = G*(3.0*Lambda+2.0*G)/(Lambda+G);
    double nu = Lambda/(2.0*(Lambda+G));
    //std::cout<<nu<<std::endl;
    // Slip weakening friction parameters
    double Dc = 0.2;
    double mu_d= 0.3;
    //double mu_s = 0.677;
    // Intialization
    // disp velocity current and next time step (new)
    VectorXd u_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd u_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd a_n = VectorXd::Zero(n_nodes*Ndofn,1);
    // slip and slip-rate
    std::vector<Eigen::Array<double, -1, 1> > delt_u_n(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > delt_v_n(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > T_c(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > T_0(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > tau_s(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > mu_s(num_faults);

    // Vector containing number of elements on each faults (nx)
    std::vector<int> nx_faults(num_faults);
    for (int i=0;i<num_faults;i++)
    {
        nx_faults[i] =fault_nodes[2*i].size();
    }

    for (int i=0;i<num_faults;i++)
    {
        delt_u_n[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        delt_v_n[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        T_c[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        T_0[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        tau_s[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        mu_s[i] = ArrayXd::Zero(nx_faults[i], 1);
    }
    // Making a copy of delt_u and delt_v to calculate the frictional disspation
    std::vector<Eigen::Array<double, -1, 1> > delt_u_n_copy(num_faults);
    std::vector<Eigen::Array<double, -1, 1> > delt_v_n_copy(num_faults);
    for (int i=0;i<num_faults;i++)
    {
        delt_u_n_copy[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
        delt_v_n_copy[i] = ArrayXd::Zero(Ndofn*(nx_faults[i]),1);
    }

    VectorXd F_ext_global = VectorXd::Zero(n_nodes*Ndofn,1);
    // Write the x coordiantes for each fault
    std::vector<double> fault_angle(num_faults);
    double x1 = 0.0;
    double x2 = 0.0;
    double y1 = 0.0;
    double y2 = 0.0;
    for (int j=0;j<num_faults; j++)
    {
        VectorXd x = VectorXd::Zero(nx_faults[j], 1);
        VectorXd y = VectorXd::Zero(nx_faults[j], 1);

        for (int i=0; i<x.size(); i++)
        {
            x(i) = Node(fault_nodes[2*j][i],0);
            y(i) = Node(fault_nodes[2*j][i],1);
        }

        //std::ofstream x_output("results/fault_x_coord.txt");
        std::string  x_fault= "results/x_fault_"+std::to_string(j)+".txt";
        std::ofstream x_output(x_fault);
        x_output<<x;

        x1 = Node(fault_nodes[2*j][0],0);
        x2 = Node(fault_nodes[2*j][nx_faults[j]-1],0);
        y1 = Node(fault_nodes[2*j][0],1);
        y2 = Node(fault_nodes[2*j][nx_faults[j]-1],1);
        fault_angle[j]=std::atan2(y2-y1,x2-x1);
        //std::cout<<x1/1e3<<" "<<x2/1e3<<" "<<y1/1e3<<" "<<y2/1e3<<std::endl;
      }

    //Adding pertubation to the main fault 0
    VectorXd x_main = VectorXd::Zero(nx_faults[0], 1);

    for (int i=0; i<x_main.size(); i++)
    {
        x_main(i) = Node(fault_nodes[0][i],0);
    }

    // Get the index degree of freedome for each element
    VectorXi index_el = VectorXi::Zero(Ndofn*Nnel,1);
    MatrixXi index_store = MatrixXi::Zero(Nnel*Ndofn,n_el);
    for (int i=0;i<n_el;i++)
    {
        bcdof(Element.row(i),dim,index_el);
        index_store.col(i) = index_el;
    }
  //  VectorXi BIE_top_surf_index = VectorXi::Zero(Ndofn*(BIE_top_surf_nodes.size()),1);
   // VectorXi BIE_bot_surf_index = VectorXi::Zero(Ndofn*(BIE_bot_surf_nodes.size()),1);
    VectorXi BIE_top_surf_index = VectorXi::Zero(Ndofn*(BIE_top.size()),1);
    VectorXi BIE_bot_surf_index = VectorXi::Zero(Ndofn*(BIE_bot.size()),1);
    std::vector<Eigen::ArrayXi> Fault_surf_index(num_faults*2);
    for (int i=0; i <num_faults*2; i++)
    {
        Fault_surf_index[i] = ArrayXi::Zero(Ndofn*(fault_nodes[i].size()),1);
    }

//    VectorXi AB_left_surf_index = VectorXi::Zero(Ndofn*(AB_left.size()),1);
//    VectorXi AB_right_surf_index = VectorXi::Zero(Ndofn*(AB_right.size()),1);
//
//    bcdof_ptr(AB_left,dim,AB_left_surf_index.data());
//    bcdof_ptr(AB_right,dim,AB_right_surf_index.data());

    // Getting the faults DOF index
    for (int i=0; i<num_faults*2;i++)
    {
        bcdof_ptr(fault_nodes[i], dim, Fault_surf_index[i].data());
    }
    bcdof_ptr(BIE_top,dim,BIE_top_surf_index.data());
    bcdof_ptr(BIE_bot,dim,BIE_bot_surf_index.data());
    // Calculating the Global Mass Vector (lumped mass)
    // Element stiffness matrix,Element mass
    std::vector<MatrixXd> ke_store(n_el);
    MatrixXd M_el = MatrixXd::Zero(Nnel*Ndofn,Nnel*Ndofn);
    VectorXd M_el_vec = VectorXd::Zero(Nnel*Ndofn,1);
    VectorXd M_global_vec=VectorXd::Zero(n_nodes*Ndofn,1);
    std::vector<double> dx_min_store;
    std::vector<double> detJ(n_el);
  //  std::vector<Eigen::MatrixXd> B_mat(4);
    std::vector<Eigen::MatrixXd> B_mat_temp(4);

    std::vector<std::vector<Eigen::MatrixXd>> B_mat;
    //B_mat[0][0] = MatrixXd::Zero(3, 8);
    for (int i=0; i<n_el;i++)
    {
        MatrixXd coord = MatrixXd::Zero(4,2);
        VectorXi Element_0= Element.row(i);
        coord.row(0) = Node.row(Element_0(0));
        coord.row(1) = Node.row(Element_0(1));
        coord.row(2) = Node.row(Element_0(2));
        coord.row(3) = Node.row(Element_0(3));
        VectorXd dx_cal = VectorXd::Zero(4,1);
        dx_cal(0)=std::sqrt(pow(std::abs(coord(0,0)-coord(1,0)),2)+pow(std::abs(coord(0,1)-coord(1,1)),2));
        dx_cal(1)=std::sqrt(pow(std::abs(coord(1,0)-coord(2,0)),2)+pow(std::abs(coord(1,1)-coord(2,1)),2));
        dx_cal(2)=std::sqrt(pow(std::abs(coord(2,0)-coord(3,0)),2)+pow(std::abs(coord(2,1)-coord(3,1)),2));
        dx_cal(3)=std::sqrt(pow(std::abs(coord(3,0)-coord(0,0)),2)+pow(std::abs(coord(3,1)-coord(0,1)),2));
        double dx_min = dx_cal.minCoeff();
        dx_min_store.push_back(dx_min);
       // std::cout<<dx_cal<<std::endl;
        cal_Bmat(coord, E, nu, B_mat_temp, detJ[i]);
        B_mat.push_back(B_mat_temp);
        cal_ke (coord,E,nu,ke_store[i]);
        cal_M(coord, density,M_el);
        M_el_vec = M_el.rowwise().sum();
        index_el = index_store.col(i);
        mapglobal(index_el,M_global_vec,M_el_vec);

    }
    //
    double dx_min = *std::min_element(std::begin(dx_min_store), std::end(dx_min_store));
    // Time
    double alpha = 0.1;
    //dx_min = 100;
    double dt = alpha*dx_min/v_p;
    std::cout<<"dt="<<dt<<std::endl;
    //dt = 0.001;
    // dt = 5.0e-4;
    // Reyleigh Damping
    double beta =0.1;
    double q = beta*dt;
    double time_run = 6.0;
    int numt = time_run/dt;
    // numt= 1 ;
    //
    std::ofstream time_output("results/time.txt");
    time_output<<time_run<<"\n";
    time_output<<dt<<"\n";
    time_output<<numt<<std::endl;

    //numt = 1;
    VectorXd time = dt*VectorXd::LinSpaced(numt,1,numt);
    // BIE part initiation
    // Setting up the material property for the BIE code
    Material BIE_top_mat = Material(E,nu,density);
    Material BIE_bot_mat = Material(E,nu,density);
    double length = x_max- x_min;
    // infinte bc BIE call infinite_boundary.cc
    PrecomputedKernel h11("kernels/nu_.25_h11.dat");
    PrecomputedKernel h12("kernels/nu_.25_k12.dat");
    PrecomputedKernel h22("kernels/nu_.25_h22.dat");
    InfiniteBoundary BIE_inf_top(length,nx_BIE,1.0,&BIE_top_mat,&h11,&h12,&h22);
    InfiniteBoundary BIE_inf_bot(length,nx_BIE,-1.0,&BIE_bot_mat,&h11,&h12,&h22);
    // BIE setting time step
    BIE_inf_top.setTimeStep(dt);
    BIE_inf_bot.setTimeStep(dt);
    // BIE initialization
    BIE_inf_top.init();
    BIE_inf_bot.init();
    printf("ready to start\n");
    // Output
    ofstream file;
    file.open("results/num_nodes_fault.bin",ios::binary);
    file.write((char*)(nx_faults.data()),nx_faults.size()*sizeof(int));
    file.close();

    file.open("results/u_n.bin",ios::binary);
   // file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
    file.close();
    file.open("results/v_n.bin",ios::binary);
    // file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
    file.close();
    file.open("results/eq_ep_n.bin",ios::binary | ios::app);
    // file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
    file.close();
    file.open("results/E_p.bin",ios::binary | ios::app);
    file.close();

    for (int i=0;i<num_faults;i++)
    {
        std::string slip = "results/slip_"+std::to_string(i)+".bin";
        file.open(slip);
        file.close();
        std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
        file.open(slip_rate);
        file.close();
        std::string shear = "results/shear_"+std::to_string(i)+".bin";
        file.open(shear);
        file.close();
    }
    // output BIE boundary u_n v_n
    VectorXd BIE_top_u_n = VectorXd::Zero(Ndofn*(nx_faults[0]),1);
    VectorXd BIE_bot_u_n = VectorXd::Zero(Ndofn*(nx_faults[0]),1);
    VectorXd BIE_top_v_n = VectorXd::Zero(Ndofn*(nx_faults[0]),1);
    VectorXd BIE_bot_v_n = VectorXd::Zero(Ndofn*(nx_faults[0]),1);
    // Add output files BIE_top BIE_bot u_n v_n
    file.open("results/BIE_top_u_n.bin",ios::binary | ios::app);
    file.close();
    file.open("results/BIE_top_v_n.bin",ios::binary | ios::app);
    file.close();
    file.open("results/BIE_bot_u_n.bin",ios::binary | ios::app);
    file.close();
    file.open("results/BIE_bot_u_n.bin",ios::binary | ios::app);
    file.close();

    std::ofstream Element_output("results/Element.txt");
    std::ofstream Node_output("results/Node.txt");
    Node_output<<Node;
    Element_output<<Element;

    double a = 0.008;
    double b = 0.012;
    double V_0 = 1e-6;
    double f_0 = 0.6;
    double L = 0.02;
    double tau_ini = 75e6;
    double sigma_ini = 120e6;
    double V_ini  = 1e-12;
    VectorXd a_array = a*VectorXd::Zero(nx_faults[0],1);
    VectorXd b_array = b*VectorXd::Zero(nx_faults[0],1);
    VectorXd theta_ini = b*VectorXd::Zero(nx_faults[0],1);
    VectorXd theta_n = VectorXd::Zero(nx_faults[0],1);
    VectorXd theta_dot_n = VectorXd::Zero(nx_faults[0],1);

    double Vw_width = 5.0e3;
    for (int i =0; i<nx_faults[0]; i++){
        if (abs(x_main(i))<Vw_width/2.0)
        {
            a_array(i) = 0.008;
            b_array(i) = 0.012;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
        else
        {
            b_array(i) = 0.012;
            a_array(i) = 0.008;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
    }
    theta_n = theta_ini;
    double M = density*dx*dy*1.0;

    // Rate and state initlaized v_n delt_v_n
    for (int i=0;i<nx_faults[0];i++)
    {
        delt_v_n[0](2*i)= V_ini;

        v_n(Fault_surf_index[0](2*i))= V_ini/2.0;

        v_n(Fault_surf_index[1](2*i))= -V_ini/2.0;
    }

    // Setting intial stress on the fault
    for (int i=0; i<T_0[0].size()/2; i++)
    {
        T_0[0](2*i) = tau_ini;
        T_0[0](2*i+1) = -sigma_ini;
    }
    VectorXd x = VectorXd::LinSpaced(nx_faults[0],x_min,x_max);
    double d_tau_0 = 25e6;
    double R = 2.5e3;
    VectorXd F_r = VectorXd::Zero(nx_faults[0],1);
    for (int i=0; i<nx_faults[0]; i++)
    {
        if (fabs(x(i))<R)
        {
            F_r(i) = exp(pow(x(i),2)/( pow(x(i),2)-pow(R,2)));
        }
        else
        {
            F_r(i) = 0.0;
        }
    }
    VectorXd G_t = VectorXd::Zero(numt,1);
    double T_ramp = 1.0;
    for (int i=0; i<numt;i++)
    {
        if(time(i)<T_ramp)
        {
            G_t(i) = exp(pow((time(i)-T_ramp),2)/(time(i)*(time(i)-2*T_ramp)));
        }
        else
        {
            G_t(i) = 1.0;
        }
    }

    MatrixXd d_tau = MatrixXd::Zero(nx_faults[0],numt);
    for (int i=0;i<numt;i++)
    {
        d_tau.col(i)=75e6*VectorXd::Ones(nx_faults[0],1)+d_tau_0*F_r*(G_t(i));
        // d_tau.col(i)=75e6*VectorXd::Ones(nx_el+1,1)+3.75e6*F_r+dt*0.3125e7*i*VectorXd::Ones(nx_el+1,1);
    }

   // time_output<<time_run<<"\n";
   // time_output<<dt<<"\n";
    // Main time loop
    for (int j=0;j<numt;j++)
    {
        // Compute the global internal force
        VectorXd fe_global= VectorXd::Zero(n_nodes*Ndofn,1);
        cal_fe_global_vary_ke(n_nodes, n_el, index_store, q, u_n, v_n, Ndofn, ke_store, fe_global);
      //  cal_fe_global_plastic_vary(n_el, index_store, ke_store, B_mat, detJ, E, nu, q, u_n, v_n, Ndofn, fe_global, strain_n_store,stress_n_store, sxx_initial, syy_initial, sxy_initial, cohes, blkfric,eq_ep_out,E_p_out);
        // Friction subroutine
        VectorXd F_total = F_ext_global-fe_global;
        for (int i=0 ; i<num_faults; i++)
        {
            VectorXd F_fault = VectorXd::Zero(Ndofn*(nx_faults[i]),1);
            VectorXd M_pos = VectorXd::Zero(Ndofn*nx_faults[i], 1);
            VectorXd M_neg = VectorXd::Zero(Ndofn*nx_faults[i],1);

            maplocal(Fault_surf_index[2*i], M_global_vec, M_pos);
            maplocal(Fault_surf_index[2*i+1], M_global_vec, M_neg);

           // Slip_Weakening_lumpM_with_buffer(M_global_vec, Fault_surf_index[2*i], Fault_surf_index[2*i+1], fe_global, dt, dx, dy, nx_faults[i]-1, delt_v_n[i], delt_u_n[i], T_0[i], tau_s[i], mu_s[i], mu_d, Dc, Ndofn, M_pos, M_neg , F_fault, T_c[i], fault_angle[i]);

            for (int i=0;i<nx_faults[0];i++)
            {
                T_0[0](2*i) = d_tau(i,j);
            }

            Rate_and_State_Aging(M_global_vec, Fault_surf_index[0], Fault_surf_index[1], fe_global, dt, dx, dy, nx_faults[i]-1, delt_u_n[0], delt_v_n[0], T_0[i], tau_s[i], a_array, b_array , L, V_0, f_0, Ndofn, M, F_fault, T_c[i], theta_n, theta_dot_n);

            mapglobal(Fault_surf_index[2*i],F_total,-F_fault);
            mapglobal(Fault_surf_index[2*i+1],F_total,F_fault);
        }

        // Calculate Absorbing force
      //  cal_ab_force(T_abc, F_total, AB_left_surf_index, v_n, Ndofn);
      //  cal_ab_force(T_abc, F_total, AB_right_surf_index, v_n, Ndofn);
       // Central Difference Time integration
        time_advance(u_n, v_n, F_total, M_global_vec, dt);

        update_disp_velocity(u_n, v_n, Fault_surf_index[0], Fault_surf_index[1], Ndofn, nx_faults[0]-1, delt_u_n[0], delt_v_n[0]);

        delt_v_n[0](0)=0;
        delt_v_n[0](2*nx_faults[0]-2)=0;
        delt_u_n[0](0)=0;
        delt_u_n[0](2*nx_faults[0]-2)=0;
        // Correct the BIE surf nodes solutions from FEM with the BIE solution
        BIE_correct(BIE_top_surf_index, BIE_bot_surf_index, fe_global, Ndofn, nx_BIE-1, dx, BIE_inf_top, BIE_inf_bot, u_n, v_n);

        for (int i=0;i<1;i++)
        {
            std::string slip = "results/slip_"+std::to_string(i)+".bin";
            file.open(slip,ios::binary | ios::app);
            file.write((char*)(delt_u_n[i].data()),delt_u_n[i].size()*sizeof(double));
            file.close();

            std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
            file.open(slip_rate,ios::binary | ios::app);
            file.write((char*)(delt_v_n[i].data()),delt_v_n[i].size()*sizeof(double));
            file.close();

            std::string shear = "results/shear_"+std::to_string(i)+".bin";
            file.open(shear,ios::binary | ios::app);
            file.write((char*)(T_c[i].data()),T_c[i].size()*sizeof(double));
            file.close();
        }
//
        if (j%100==1)
        {
            file.open("results/u_n.bin",ios::binary | ios::app);
            file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
            file.close();
            file.open("results/v_n.bin",ios::binary | ios::app);
            file.write((char*)(v_n.data()),v_n.size()*sizeof(double));
            file.close();
        }

//  Adding virtual boundary information
        maplocal(BIE_top_surf_index,u_n,BIE_top_u_n);
        maplocal(BIE_top_surf_index,v_n,BIE_top_v_n);
        maplocal(BIE_bot_surf_index,u_n,BIE_bot_u_n);
        maplocal(BIE_bot_surf_index,v_n,BIE_bot_v_n);

        file.open("results/BIE_top_u_n.bin",ios::binary | ios::app);
        file.write((char*)(BIE_top_u_n.data()),BIE_top_u_n.size()*sizeof(double));
        file.close();
        file.open("results/BIE_top_v_n.bin",ios::binary | ios::app);
        file.write((char*)(BIE_top_v_n.data()),BIE_top_v_n.size()*sizeof(double));
        file.close();
        file.open("results/BIE_bot_u_n.bin",ios::binary | ios::app);
        file.write((char*)(BIE_bot_u_n.data()),BIE_bot_u_n.size()*sizeof(double));
        file.close();
        file.open("results/BIE_bot_v_n.bin",ios::binary | ios::app);
        file.write((char*)(BIE_bot_v_n.data()),BIE_bot_v_n.size()*sizeof(double));
        file.close();
//
        printf("Simulation time = %f\n",time(j));
    }
    return 0;
}


#include "../sigmaextended_prover.h"
#include "../sigmaextended_verifier.h"

#include "lelantus_test_fixture.h"

#include <boost/test/unit_test.hpp>

namespace lelantus {

class SigmaExtendedTests : public LelantusTestingSetup {
public:
    struct Secret {
    public:
        Secret(int l) : l(l) {
            s.randomize();
            v.randomize();
            r.randomize();
        }

    public:
        int l;
        Scalar s, v, r;
    };

public:
    typedef SigmaExtendedProver Prover;
    typedef SigmaExtendedProof Proof;
    typedef SigmaExtendedVerifier Verifier;

public:
    SigmaExtendedTests() {}

public:
    void GenerateParams(int _N, int _n, int _m = 0) {
        N = _N;
        n = _n;
        m = _m;
        if (!m) {
            if (n <= 1) {
                throw std::logic_error("Try to get value of m from invalid n");
            }

            m = std::round(log(N) / log(n));
        }

        h_gens = RandomizeGroupElements(n * m);
        g.randomize();
    }

    void GenerateBatchProof(
        Prover &prover,
        std::vector<GroupElement> const &coins,
        int l,
        Scalar const &s,
        Scalar const &v,
        Scalar const &r,
        Scalar const &x,
        Proof &proof
    ) {
        auto gs = g * s.negate();
        std::vector<GroupElement> commits(coins.begin(), coins.end());
        for (auto &c : commits) {
            c += gs;
        }

        Scalar rA, rB, rC, rD;
        rA.randomize();
        rB.randomize();
        rC.randomize();
        rD.randomize();

        std::vector<Scalar> sigma;
        std::vector<Scalar> Tk, Pk, Yk;
        Tk.resize(m);
        Pk.resize(m);
        Yk.resize(m);

        std::vector<Scalar> a;
        a.resize(n * m);

        prover.sigma_commit(
            commits, l, rA, rB, rC, rD, a, Tk, Pk, Yk, sigma, proof);

        prover.sigma_response(
            sigma, a, rA, rB, rC, rD, v, r, Tk, Pk, x, proof);
    }

public:
    int N;
    int n;
    int m;

    std::vector<GroupElement> h_gens;
    GroupElement g;
};

BOOST_FIXTURE_TEST_SUITE(lelantus_sigma_tests, SigmaExtendedTests)

BOOST_AUTO_TEST_CASE(one_out_of_N_batch)
{
    GenerateParams(16, 4);

    auto commits = RandomizeGroupElements(N);

    // Generate
    std::vector<Secret> secrets;

    for (auto index : {1, 3, 5, 9, 15}) {
        secrets.emplace_back(index);

        auto &s = secrets.back();

        commits[index] = Primitives::double_commit(
            g, s.s, h_gens[1], s.v, h_gens[0], s.r);
    }

    Prover prover(g, h_gens, n, m);
    std::vector<Proof> proofs;
    std::vector<Scalar> serials;

    Scalar x;
    x.randomize();

    for (auto const &s : secrets) {
        proofs.emplace_back();
        serials.push_back(s.s);
        GenerateBatchProof(
            prover, commits, s.l, s.s, s.v, s.r, x, proofs.back());
    }

    Verifier verifier(g, h_gens, n, m);
    BOOST_CHECK(verifier.batchverify(commits, x, serials, proofs));

    // verify subset of valid proofs should success also
    serials.pop_back();
    proofs.pop_back();
    BOOST_CHECK(verifier.batchverify(commits, x, serials, proofs));
}

BOOST_AUTO_TEST_CASE(one_out_of_N_batch_with_some_invalid_proof)
{
    GenerateParams(16, 4);

    auto commits = RandomizeGroupElements(N);

    // Generate
    std::vector<Secret> secrets;

    for (auto index : {1, 3}) {
        secrets.emplace_back(index);

        auto &s = secrets.back();

        commits[index] = Primitives::double_commit(
            g, s.s, h_gens[1], s.v, h_gens[0], s.r);
    }

    Prover prover(g, h_gens, n, m);
    std::vector<Proof> proofs;
    std::vector<Scalar> serials;

    Scalar x;
    x.randomize();

    for (auto const &s : secrets) {
        proofs.emplace_back();
        serials.push_back(s.s);
        GenerateBatchProof(
            prover, commits, s.l, s.s, s.v, s.r, x, proofs.back());
    }

    // Add an invalid
    proofs.push_back(proofs.back());

    serials.emplace_back(serials.back());
    serials.back().randomize();

    Verifier verifier(g, h_gens, n, m);
    BOOST_CHECK(!verifier.batchverify(commits, x, serials, proofs));
}

BOOST_AUTO_TEST_SUITE_END()

} // namespace lelantus


//  (C) Copyright Raffi Enficiaud 2015.
//  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)

//  See http://www.boost.org/libs/test for the library home page.

//[example_code
#define BOOST_TEST_MODULE boost_test_container_lex_default
#include <boost/test/included/unit_test.hpp>
#include <vector>

namespace tt = boost::test_tools;

BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(std::vector<int>)

BOOST_AUTO_TEST_CASE( test_collections_vectors_lex )
{
  std::vector<int> a{1,2,3}, b{1,2,2};
  std::vector<long int> c{1,2,3,5}, d{1,2,3,4};

  BOOST_TEST(a < a); // extended diagnostic
  BOOST_TEST(a < b); // extended diagnostic
  BOOST_TEST(c < d); // no extended diagnostic
}
//]


/* =========================================================================
   Copyright (c) 2010-2012, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the PDF manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

// #define VIENNACL_DEBUG_CUSTOM_OPERATION
//#define VIENNACL_DEBUG_BUILD

//

// *** Boost
//
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/triangular.hpp>
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <boost/numeric/ublas/matrix.hpp>

//
// *** ViennaCL
//

// #define VIENNACL_DEBUG_ALL
// #define VIENNACL_HAVE_UBLAS 1
// #define VIENNACL_DEBUG_CUSTOM_OPERATION
//#define VIENNACL_DEBUG_BUILD

#include "viennacl/scalar.hpp"
#include "viennacl/matrix.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/linalg/prod.hpp"
#include "viennacl/linalg/norm_2.hpp"
#include "viennacl/linalg/direct_solve.hpp"
#include "examples/tutorial/Random.hpp"
#include "viennacl/generator/custom_operation.hpp"

//
// -------------------------------------------------------------
//
using namespace boost::numeric;
using namespace viennacl::generator;
//
// -------------------------------------------------------------
//

template <typename ScalarType, unsigned int Alignment>
ScalarType diff ( ublas::vector<ScalarType> & v1, viennacl::vector<ScalarType,Alignment> & v2 ) {
    ublas::vector<ScalarType> v2_cpu ( v2.size() );
    viennacl::copy( v2.begin(), v2.end(), v2_cpu.begin() );
    for ( unsigned int i=0; i<v1.size(); ++i ) {
        if ( std::max ( std::abs ( v2_cpu[i] ), std::abs ( v1[i] ) ) > 0 )
            v2_cpu[i] = std::abs ( v2_cpu[i] - v1[i] ) / std::max ( std::abs ( v2_cpu[i] ), std::abs ( v1[i] ) );
        else
            v2_cpu[i] = 0.0;
    }
    return norm_inf ( v2_cpu );
}

template< typename NumericT,  typename F,typename F2, unsigned int Alignment, typename Epsilon >
int test ( Epsilon const& epsilon ) {
    int retval = EXIT_SUCCESS;
    static const unsigned int SIZE = 5;//027;
    // --------------------------------------------------------------------------
    NumericT scalar = 2;
    ublas::vector<NumericT> v0(SIZE);
    ublas::vector<NumericT> v1(SIZE);
    ublas::vector<NumericT> v2(SIZE);
    ublas::vector<NumericT> v3(SIZE);
    for ( unsigned int i = 0; i < SIZE; ++i ){
        v0( i ) = i;
        v1( i ) = i+1;
        v2( i ) = i+2;
        v3( i ) = i+3;
    }
    ublas::matrix<NumericT,F2> matrix ( v0.size(), v1.size() );
    for ( unsigned int i = 0; i < matrix.size1(); ++i )
        for ( unsigned int j = 0; j < matrix.size2(); ++j )
            matrix ( i,j ) = i+j;

    std::cout << "----- Alignment " << Alignment << " -----" << std::endl;

    viennacl::scalar<NumericT> vcl_scalar(scalar);
    viennacl::vector<NumericT,Alignment> vcl_v0 (v0.size());
    viennacl::vector<NumericT,Alignment> vcl_v1 (v1.size());
    viennacl::vector<NumericT,Alignment> vcl_v2 (v2.size());
    viennacl::vector<NumericT,Alignment> vcl_v3 (v3.size());
    viennacl::matrix<NumericT, F, Alignment> vcl_matrix ( matrix.size1(), matrix.size2() );

    viennacl::copy(v0,vcl_v0);
    viennacl::copy(v1,vcl_v1);
    viennacl::copy(v2,vcl_v2);
    viennacl::copy(v3,vcl_v3);

    viennacl::copy(matrix,vcl_matrix);

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

    symbolic_vector<0,NumericT> symv0;
    symbolic_vector<1,NumericT> symv1;
    symbolic_vector<2,NumericT> symv2;
    symbolic_vector<3,NumericT> symv3;

    symbolic_matrix<1,NumericT,F,Alignment> symm1;
    symbolic_matrix<3,NumericT,F,Alignment> symm3;

    gpu_symbolic_scalar<3,NumericT> syms3;
    gpu_symbolic_scalar<4,NumericT> syms4;

    cpu_symbolic_scalar<5,NumericT> sym_bound;
    cpu_symbolic_scalar<6,NumericT> sym_bound2;

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

    float bound1 = 2;

    std::cout << "Testing repetition..." << std::endl;
    v0 = v1 + v2;
    for(unsigned int i = 0 ; i< bound1 ; ++i){
        v2 = scalar*v2;
    }
    v3 = v0 + v2;
    viennacl::ocl::enqueue(viennacl::generator::custom_operation (symv0 = symv1 + symv2
                                                                    ,viennacl::generator::repeat(sym_bound,symv2 = syms4 * symv2)
                                                                    ,symv3 = symv0 + symv2
                                                                    ,"Multiline1") ( vcl_v0, vcl_v1, vcl_v2, vcl_v3, vcl_scalar, bound1));
    viennacl::ocl::get_queue().finish();

    if ( diff(v0, vcl_v0) > epsilon
         || diff (v2,vcl_v2) > epsilon
         || diff (v3,vcl_v3) > epsilon){
        std::cout << "# Error at operation:repetition" << std::endl;
        std::cout << "  diff0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff2: " << std::abs ( diff ( v2, vcl_v2 ) ) << std::endl;
        std::cout << "  diff3: " << std::abs ( diff ( v2, vcl_v2 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }

    std::cout << "Testing nested repetition..." << std::endl;

    float bound2 = 1;
    v0 = v1 + v2;
    for(unsigned int i = 0 ; i< bound1 ; ++i){
        v2 = scalar*v2;
        for(unsigned int j = 0 ; j < bound2 ; ++j){
            v3 = v3 - v2;
        }
    }
    v1 = v2 + v3;
    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv0 = symv1 + symv2
                                                                    ,viennacl::generator::repeat(sym_bound,symv2 = syms4 * symv2
                                                                                , viennacl::generator::repeat(sym_bound2,symv3 = symv3 - symv2))
                                                                    ,symv1 = symv2 + symv3
                                                                    ,"Multiline2") ( vcl_v0, vcl_v1, vcl_v2, vcl_v3, vcl_scalar, bound1,bound2) );
    viennacl::ocl::get_queue().finish();

    if ( diff(v0, vcl_v0) > epsilon
         || diff(v1, vcl_v1) > epsilon
         || diff (v2,vcl_v2) > epsilon
         || diff (v3,vcl_v3) > epsilon ) {
        std::cout << "# Error at operation: Nested repetition" << std::endl;
        std::cout << "  diff0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff1: " << std::abs ( diff ( v1, vcl_v1 ) ) << std::endl;
        std::cout << "  diff2: " << std::abs ( diff ( v2, vcl_v2 ) ) << std::endl;
        std::cout << "  diff3: " << std::abs ( diff ( v3, vcl_v3 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing V0=V1+V2 ; s=inner_prod(V0,V1) ..." << std::endl;

    v0 = v1 + v2;
    scalar = ublas::inner_prod(v0,v1);

    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv0 = symv1 + symv2
                                                                    ,syms3= inner_prod(symv0,symv1)
                                                                    ,"Multiline3") ( vcl_v0, vcl_v1, vcl_v2, vcl_scalar) );

    viennacl::ocl::get_queue().finish();

    if ( diff (v0,vcl_v0 ) > epsilon
         || std::abs( scalar - static_cast<NumericT>(vcl_scalar) ) / std::max<NumericT>(scalar, vcl_scalar) > epsilon){
        std::cout << "# Error at operation: V0=V1+V2 ; s=inner_prod(V0,V1) " << std::endl;
        std::cout << "  diff_vec: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_scal: " << std::abs ( scalar - vcl_scalar ) << std::endl;
        std::cout << "  scalar: " << scalar << std::endl;
        std::cout << "  vcl_scalar: " << vcl_scalar << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing  s=inner_prod(V0,V1) ; V0=V1+V2 ..." << std::endl;
    scalar = ublas::inner_prod(v0,v1);
    v0 = v1 + v2;
    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (syms3= inner_prod(symv0,symv1)
                                                                    ,symv0 = symv1 + symv2
                                                                    ,"Multiline4") ( vcl_v0, vcl_v1, vcl_v2, vcl_scalar) );

    viennacl::ocl::get_queue().finish();
    if ( diff(v0, vcl_v0) > epsilon
         || std::abs( scalar - static_cast<NumericT>(vcl_scalar) ) / std::max<NumericT>(scalar, vcl_scalar) > epsilon){
        std::cout << "# Error at operation: s=inner_prod(V0,V1) ; V0=V1+V2  ... " << std::endl;
        std::cout << "  diff_vec: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_scal: " << std::abs ( scalar - vcl_scalar ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing V0=V1+V2 ; V1=prod(M,V0) ..." << std::endl;

    v0 = v1 + v2;
    v1 = ublas::prod(matrix,v0);

    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv0 = symv1 + symv2
                                                                     ,symv1= prod(symm3,symv0)
                                                                    ,"Multiline5") ( vcl_v0, vcl_v1, vcl_v2, vcl_matrix) );

    viennacl::ocl::get_queue().finish();

    if ( diff(v0, vcl_v0) > epsilon
         || diff(v1, vcl_v1) > epsilon){
        std::cout << "# Error at operation: V0=V1+V2 ; V1=prod(M,V0) " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_vec1: " << std::abs ( diff ( v1, vcl_v1 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing  V1=prod(M,V0) ; V0=V1+V2 ..." << std::endl;
    v1 = ublas::prod(matrix,v0);
    v0 = v1 + v2;

    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv1= prod(symm3,symv0)
                                                                    ,symv0 = symv1 + symv2
                                                                    ,"Multiline6") ( vcl_v0, vcl_v1, vcl_v2, vcl_matrix) );

    viennacl::ocl::get_queue().finish();

    if ( diff(v0, vcl_v0) > epsilon
         || diff(v1, vcl_v1) > epsilon){
        std::cout << "# Error at operation:  V1=prod(M,V0) ; V0=V1+V2 " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_vec1: " << std::abs ( diff ( v1, vcl_v1 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing V0 = M*V2 * ip(V2,V3) ; V2 = V0*s ..." << std::endl;
    v0 = ublas::prod(matrix, v2)*ublas::inner_prod(v2,v3);
    v2 = v0*scalar;
    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv0 = prod(symm1,symv2)*inner_prod(symv2,symv3)
                                                                    ,symv2 = symv0*syms4
                                                                    ,"Multiline7") ( vcl_v0, vcl_matrix, vcl_v2, vcl_v3, vcl_scalar) );

    viennacl::ocl::get_queue().finish();
    if ( diff(v0, vcl_v0) > epsilon
         || diff(v2, vcl_v2) > epsilon){
        std::cout << "# Error at operation: V0 = M*V2 * ip(V2,V3) ; V2 = V0*s ... " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_vec2: " << std::abs ( diff ( v2, vcl_v2 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing V2 = V0*s ; V0 = M*V2 * ip(V2,V3) ..." << std::endl;
    v2 = v0*scalar;
    v0 = ublas::prod(matrix, v2)*ublas::inner_prod(v2,v3);
    viennacl::ocl::enqueue ( viennacl::generator::custom_operation (symv2 = symv0*syms4
                                                                    ,symv0 = prod(symm1,symv2)*inner_prod(symv2,symv3)
                                                                    ,"Multiline8") ( vcl_v0, vcl_matrix, vcl_v2, vcl_v3, vcl_scalar) );

    viennacl::ocl::get_queue().finish();
    if ( diff(v0, vcl_v0) > epsilon
         || diff(v2, vcl_v2) > epsilon){
        std::cout << "# Error at operation: V2 = V0*s ; V0 = M*V2 * ip(V2,V3) ... " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( v0, vcl_v0 ) ) << std::endl;
        std::cout << "  diff_vec2: " << std::abs ( diff ( v2, vcl_v2 ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing dX = sigma * (X - Y) ; "
              << "        dY = R * X - Y - X * Z;"
              << "        dZ = X * Y - b * Z;" << std::endl;

    for ( unsigned int i = 0; i < SIZE; ++i ){
        v0( i ) = i;
        v1( i ) = i+1;
        v2( i ) = i+2;
        v3( i ) = i+3;
    }

    viennacl::copy(v0,vcl_v0);
    viennacl::copy(v1,vcl_v1);
    viennacl::copy(v2,vcl_v2);
    viennacl::copy(v3,vcl_v3);

    ublas::vector<NumericT> dv0(v0);
    ublas::vector<NumericT> dv1(v1);
    ublas::vector<NumericT> dv2(v2);
    NumericT sigma = 3.1415;
    NumericT b = 2.7182;
    dv0 = sigma * (v1 - v0);
    dv1 = boost::numeric::ublas::element_prod(v3, v0) - v1 - boost::numeric::ublas::element_prod(v0, v2);
    dv2 = boost::numeric::ublas::element_prod(v0, v1) - b * v2;

    symbolic_vector<0,NumericT> sym_dX;
    symbolic_vector<1,NumericT> sym_dY;
    symbolic_vector<2,NumericT> sym_dZ;

    symbolic_vector<3,NumericT> sym_X;
    symbolic_vector<4,NumericT> sym_Y;
    symbolic_vector<5,NumericT> sym_Z;

    symbolic_vector<6,NumericT> sym_R;

    cpu_symbolic_scalar<7,NumericT> sym_sigma;
    cpu_symbolic_scalar<8,NumericT> sym_b;

    viennacl::vector<NumericT,Alignment> vcl_dX (vcl_v0);
    viennacl::vector<NumericT,Alignment> vcl_dY (vcl_v1);
    viennacl::vector<NumericT,Alignment> vcl_dZ (vcl_v2);
    viennacl::vector<NumericT,Alignment> vcl_X (vcl_v0);
    viennacl::vector<NumericT,Alignment> vcl_Y (vcl_v1);
    viennacl::vector<NumericT,Alignment> vcl_Z (vcl_v2);

    viennacl::vector<NumericT,Alignment> vcl_R (vcl_v3);

    viennacl::ocl::enqueue ( viennacl::generator::custom_operation ( sym_dX = sym_sigma * (sym_Y - sym_X)
                                                                    ,sym_dY = element_prod(sym_R, sym_X) - sym_Y - element_prod(sym_X, sym_Z)
                                                                    ,sym_dZ = element_prod(sym_X, sym_Y) - sym_b * sym_Z
                                                                    ,"Lorenz") ( vcl_dX, vcl_dY, vcl_dZ, vcl_X, vcl_Y, vcl_Z, vcl_R, sigma, b) );

    viennacl::ocl::get_queue().finish();
    if ( diff(dv0, vcl_dX) > epsilon
         || diff(dv1, vcl_dY) > epsilon
         || diff(dv2, vcl_dZ) > epsilon){
        std::cout << "# Error at operation: dX = sigma * (X - Y) ; ... " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( dv0, vcl_dX ) ) << std::endl;
        std::cout << "  diff_vec1: " << std::abs ( diff ( dv1, vcl_dY ) ) << std::endl;
        std::cout << "  diff_vec2: " << std::abs ( diff ( dv2, vcl_dZ ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    std::cout << "Testing dX -= beta * X * X * X;" << std::endl;

    NumericT beta = sigma;
    viennacl::copy(v0,vcl_X);
    viennacl::copy(dv0,vcl_dX);
    dv0 -= beta * boost::numeric::ublas::element_prod(v0, boost::numeric::ublas::element_prod(v0, v0));

    cpu_symbolic_scalar<2,NumericT> sym_beta;
    viennacl::ocl::enqueue ( viennacl::generator::custom_operation ( symv0 -= sym_beta * element_prod(symv1, element_prod(symv1, symv1))
                                                                    ,"test_nested_element_prod") ( vcl_dX, vcl_X, beta) );

    viennacl::ocl::get_queue().finish();
    if ( diff(dv0, vcl_dX) > epsilon){
        std::cout << "# Error at operation: dX -= beta * X * X * X ; ... " << std::endl;
        std::cout << "  diff_vec0: " << std::abs ( diff ( dv0, vcl_dX ) ) << std::endl;
        retval = EXIT_FAILURE;
    }
    viennacl::ocl::get_queue().finish();

    return retval;

}

int main() {
    std::cout << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "## Test :: Multiline Operations" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << std::endl;

    int retval = EXIT_SUCCESS;

    std::cout << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << std::endl;
    {
        typedef float NumericT;
        NumericT epsilon = 1.0E-3;
        std::cout << "# Testing setup:" << std::endl;
        std::cout << "  eps:     " << epsilon << std::endl;
        std::cout << "  numeric: float" << std::endl;

        std::cout << "---- Layout : Row Major" << std::endl;
        retval = test<NumericT, viennacl::row_major,ublas::row_major,1> ( epsilon );
        //retval = test<NumericT, viennacl::row_major,ublas::row_major,16> ( epsilon );

        if ( retval == EXIT_SUCCESS )
            std::cout << "# Test passed" << std::endl;
        else
            return retval;
    }

    return retval;
}


//                  _  _
//  _   _|_ _  _|o_|__|_
// (_||_||_(_)(_|| |  |
//
// automatic differentiation made easier in C++
// https://github.com/autodiff/autodiff
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
//
// Copyright (c) 2018-2020 Allan Leal
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

// Eigen includes
#include <Eigen/Core>

// autodiff includes
#include <autodiff/common/eigen.hpp>
#include <autodiff/common/meta.hpp>
#include <autodiff/reverse/var/var.hpp>

//------------------------------------------------------------------------------
// SUPPORT FOR EIGEN MATRICES AND VECTORS OF VAR
//------------------------------------------------------------------------------
namespace Eigen {

template<typename T>
struct NumTraits;

template<typename T>
struct NumTraits<autodiff::Variable<T>> : NumTraits<T> // permits to get the epsilon, dummy_precision, lowest, highest functions
{
    typedef autodiff::Variable<T> Real;
    typedef autodiff::Variable<T> NonInteger;
    typedef autodiff::Variable<T> Nested;
    enum
    {
        IsComplex = 0,
        IsInteger = 0,
        IsSigned = 1,
        RequireInitialization = 1,
        ReadCost = 1,
        AddCost = 3,
        MulCost = 3
    };
};

template<typename T, typename BinOp>
struct ScalarBinaryOpTraits<autodiff::Variable<T>, T, BinOp>
{
    typedef autodiff::Variable<T> ReturnType;
};

template<typename T, typename BinOp>
struct ScalarBinaryOpTraits<T, autodiff::Variable<T>, BinOp>
{
    typedef autodiff::Variable<T> ReturnType;
};

} // namespace Eigen

namespace autodiff {
namespace detail {

template<typename T, int Rows, int MaxRows>
using Vec = Eigen::Matrix<T, Rows, 1, 0, MaxRows, 1>;

template<typename T, int Rows, int Cols, int MaxRows, int MaxCols>
using Mat = Eigen::Matrix<T, Rows, Cols, 0, MaxRows, MaxCols>;

/// Return the gradient vector of variable y with respect to variables x.
template<typename T, typename X>
auto gradient(const Variable<T>& y, Eigen::DenseBase<X>& x)
{
    using U = VariableValueType<T>;

    using ScalarX = typename X::Scalar;
    static_assert(isVariable<ScalarX>, "Argument x is not a vector with Variable<T> (aka var) objects..");

    constexpr auto isVec = X::IsVectorAtCompileTime;
    static_assert(isVec, "Argument x is not a vector.");

    constexpr auto Rows = X::RowsAtCompileTime;
    constexpr auto MaxRows = X::MaxRowsAtCompileTime;

    const auto n = x.size();
    for(auto i = 0; i < n; ++i)
        x[i].seed();

    y.expr->propagate(1.0);

    Vec<U, Rows, MaxRows> g(n);
    for(auto i = 0; i < n; ++i)
        g[i] = val(x[i].grad());

    return g;
}

/// Return the Hessian matrix of variable y with respect to variables x.
template<typename T, typename X, typename Vec>
auto hessian(const Variable<T>& y, Eigen::DenseBase<X>& x, Vec& g)
{
    using U = VariableValueType<T>;

    using ScalarX = typename X::Scalar;
    static_assert(isVariable<ScalarX>, "Argument x is not a vector with Variable<T> (aka var) objects.");

    using ScalarG = typename Vec::Scalar;
    static_assert(std::is_same_v<U, ScalarG>, "Argument g does not have the same arithmetic type as y.");

    constexpr auto Rows = X::RowsAtCompileTime;
    constexpr auto MaxRows = X::MaxRowsAtCompileTime;

    const auto n = x.size();
    for(auto k = 0; k < n; ++k)
        x[k].seedx();

    y.expr->propagatex(constant<T>(1.0));

    g.resize(n);
    for(auto i = 0; i < n; ++i)
        g[i] = val(x[i].gradx());

    Mat<U, Rows, Rows, MaxRows, MaxRows> H(n, n);
    for(auto i = 0; i < n; ++i)
    {
        for(auto k = 0; k < n; ++k)
            x[k].seed();

        auto dydxi = x[i].gradx();
        dydxi->propagate(1.0);

        for(auto j = i; j < n; ++j)
            H(i, j) = H(j, i) = val(x[j].grad());
    }

    return H;
}

/// Return the Hessian matrix of variable y with respect to variables x.
template<typename T, typename X>
auto hessian(const Variable<T>& y, Eigen::DenseBase<X>& x)
{
    using U = VariableValueType<T>;
    constexpr auto Rows = X::RowsAtCompileTime;
    constexpr auto MaxRows = X::MaxRowsAtCompileTime;
    Vec<U, Rows, MaxRows> g;
    return hessian(y, x, g);
}

} // namespace detail

AUTODIFF_DEFINE_EIGEN_TYPEDEFS_ALL_SIZES(autodiff::var, var)

using detail::gradient;
using detail::hessian;

} // namespace autodiff


/*
 * @brief A class for the implementation of a phase variable, which operates between [0,1].
 * @author Jenna Reher (jreher@caltech.edu)
 */

#ifndef PHASE_VARIABLE_HPP
#define PHASE_VARIABLE_HPP

#include <Eigen/Dense>

class PhaseVariable {
public:
    double tau;
    double dtau;

    PhaseVariable();
    void update(double time);
    void reconfigure(Eigen::Vector2d &phaseRange, double timeScale);
    Eigen::Vector2d getPhaseRange();

private:
    double timeScale;
    double pActual;
    double dpActual;

    Eigen::Vector2d phaseRange;

    void calcP(double time);
    void calcTau();
    void calcDTau();

};

#endif // PHASE_VARIABLE_HPP


#include "tkdCmdParser.h"

#include <boost/accumulators/numeric/functional/vector.hpp>
#include <boost/accumulators/numeric/functional/complex.hpp>
#include <boost/accumulators/numeric/functional/valarray.hpp>

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>

#include <fstream>

/**
 * Output max values and indices of given vector data in txt file.
 */
class AnnMax
{
public:

	typedef double ValueType;
	typedef std::vector< double > VectorType;
	typedef boost::tokenizer< boost::char_separator< char > > TokType;

	/**
	 * Run.
	 */
	void Run( const std::string& inputFileName, bool reportMin, bool reportMax, bool reportMinIndex, bool reportMaxIndex )
	{
		VectorType input = GetAverageVectorFromFile( inputFileName );

		ValueType min = GetMin( input );
		ValueType max = GetMax( input );
		int indexMin = GetIndex( input, min );
		int indexMax = GetIndex( input, max );

		if ( reportMin )
			std::cout << "min: " << min << std::endl;

		if ( reportMax )
			std::cout << "max: " << max << std::endl;

		if ( reportMinIndex )
			std::cout << "index min: " << indexMin << std::endl;

		if ( reportMaxIndex )
			std::cout << "index max: " << indexMax << std::endl;

		exit( EXIT_SUCCESS );
	}

protected:

	/**
	 * Return vector from file.
	 */
	VectorType GetVectorFromFile( const std::string& input )
	{
		std::ifstream inFile;

		inFile.open( input.c_str() );
		if ( !inFile )
		{
			std::cout << "*** ERROR ***: Unable to open: " << input << "." << std::endl;
			exit( EXIT_FAILURE );
		}

		double x;
		VectorType output;

		while ( inFile >> x )
		{
			output.push_back( x );
		}
		inFile.close();

		return output;
	}

	/**
	 * Return average vector from file.
	 */
	VectorType GetAverageVectorFromFile( const std::string& inputFileName )
	{

		std::ifstream in( inputFileName.c_str() );

		if ( !in )
		{
			std::cerr << "*** ERROR ***: Unable to open file \"" << inputFileName << "\" for input.\n";
			exit( EXIT_FAILURE );
		}

		std::string line;

		VectorType result;

		try
		{
			while ( getline( in, line ) )
			{
				ValueType value = 0;
				ValueType total = 0;
				TokType tok( line, boost::char_separator< char >( "\t" ) );

				for ( TokType::iterator id = tok.begin(); id != tok.end(); ++id )
				{
					value += boost::lexical_cast< ValueType >( *id );
					total++;
				}

				// average...
				if ( total != 0 )
					value /= total;

				result.push_back( value );
			}
		}
		catch ( std::exception& e )
		{
			std::cerr << "*** ERROR ***: could not parse input. "
				"Are you sure columns are separated with the '\\t' char?" << std::endl;

			exit( EXIT_FAILURE );
		}

		in.close();

		return result;
	}

	/**
	 * Return max.
	 */
	ValueType GetMax( const VectorType& V )
	{
		using namespace boost;
		using namespace accumulators;

		accumulator_set< ValueType, stats< tag::max > > acc;

		for ( unsigned int i = 0; i < V.size(); i++ )
			acc( V[i] );

		return max( acc );
	}

	/**
	 * Return min.
	 */
	ValueType GetMin( const VectorType& V )
	{
		using namespace boost;
		using namespace accumulators;

		accumulator_set< ValueType, stats< tag::min > > acc;

		for ( unsigned int i = 0; i < V.size(); i++ )
			acc( V[i] );

		return min( acc );
	}

	/**
	 * Return index of given value (if not found return -1).
	 */
	int GetIndex( const VectorType& V, ValueType value )
	{
		int result = -1;
		for( unsigned int i = 0; i < V.size(); i++ )
		{
			if ( V[i] == value )
				return i;
		}
		return result;
	}

};

/**
 * Minimal connected components filter.
 */
int main( int argc, char ** argv )
{
	tkd::CmdParser p( "ann max", "Output index and value of maximum value" );

	std::string inputFileName;
	bool reportMin = false;
	bool reportMax = false;
	bool reportMinIndex = false;
	bool reportMaxIndex = false;

	p.AddArgument( inputFileName, "input" )->AddAlias( "i" )->SetDescription( "Input file name" )->SetRequired( true );

	p.AddArgument( reportMin, "min" )->AddAlias( "l" )->SetDescription( "Print minimal value" )->SetRequired( false );
	p.AddArgument( reportMax, "max" )->AddAlias( "u" )->SetDescription( "Print maximal value" )->SetRequired( false );
	p.AddArgument( reportMinIndex, "min-index" )->AddAlias( "ml" )->SetDescription( "Print minimal value index (zero-based)" )->SetRequired( false );
	p.AddArgument( reportMaxIndex, "max-index" )->AddAlias( "mu" )->SetDescription( "Print maximal value index (zero-based)" )->SetRequired( false );

	if ( !p.Parse( argc, argv ) )
	{
		p.PrintUsage( std::cout );
		return EXIT_FAILURE;
	}

	AnnMax annMax;
	annMax.Run( inputFileName, reportMin, reportMax, reportMinIndex, reportMaxIndex );

	return EXIT_SUCCESS;
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// 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
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#include <iostream>
#include <vector>

#include <boost/spirit/include/karma.hpp>

#include <boost/compute/system.hpp>
#include <boost/compute/algorithm/sort.hpp>

namespace compute = boost::compute;
namespace karma = boost::spirit::karma;

int rand_int()
{
    return rand() % 100;
}

// this example demonstrates how to sort a std::vector of ints on the GPU
int main()
{
    // get default device and setup context
    compute::device gpu = compute::system::default_device();
    compute::context context(gpu);
    compute::command_queue queue(context, gpu);
    std::cout << "device: " << gpu.name() << std::endl;

    // create vector of random values on the host
    std::vector<int> vector(8);
    std::generate(vector.begin(), vector.end(), rand_int);

    // print input vector
    std::cout << "input:  [ "
              << karma::format(karma::int_ % ", ", vector)
              << " ]"
              << std::endl;

    // sort vector
    compute::sort(vector.begin(), vector.end(), queue);

    // print sorted vector
    std::cout << "output: [ "
              << karma::format(karma::int_ % ", ", vector)
              << " ]"
              << std::endl;

    return 0;
}


//---------------------------------------------------------------------------//
//!
//! \file   tstSampleMoment.cpp
//! \author Alex Robinson
//! \brief  The sample moment unit tests
//!
//---------------------------------------------------------------------------//

// Boost Includes
#include <boost/units/quantity.hpp>
#include <boost/units/systems/cgs/length.hpp>
#include <boost/units/systems/si/length.hpp>

// FRENSIE Includes
#include "Utility_SampleMoment.hpp"
#include "Utility_QuantityTraits.hpp"
#include "Utility_UnitTestHarnessWithMain.hpp"

//---------------------------------------------------------------------------//
// Template Typedefs
//---------------------------------------------------------------------------//
typedef std::tuple<double,
                   boost::units::quantity<boost::units::cgs::length>,
                   boost::units::quantity<boost::units::si::length> > TestingTypes;

//---------------------------------------------------------------------------//
// Tests
//---------------------------------------------------------------------------//
// Check that the current score of the moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, getCurrentScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T> moment_a;

  FRENSIE_CHECK_EQUAL( moment_a.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::zero() );

  Utility::SampleMoment<1,T>
    moment_b( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that the current score of the moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, getCurrentScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T> moment_a;

  FRENSIE_CHECK_EQUAL( moment_a.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::zero() );

  Utility::SampleMoment<2,T>
    moment_b( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that the current score of the moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, getCurrentScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T> moment_a;

  FRENSIE_CHECK_EQUAL( moment_a.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::zero() );

  Utility::SampleMoment<3,T>
    moment_b( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that the current score of the moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, getCurrentScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T> moment_a;

  FRENSIE_CHECK_EQUAL( moment_a.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::zero() );

  Utility::SampleMoment<4,T>
    moment_b( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that the calculated moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, get, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T>
    moment( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment.get( 10 ),
                       Utility::QuantityTraits<ValueType>::one()/10. );
}

//---------------------------------------------------------------------------//
// Check that the calculated moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, get, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T>
    moment( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment.get( 10 ),
                       Utility::QuantityTraits<ValueType>::one()/10. );
}

//---------------------------------------------------------------------------//
// Check that the calculated moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, get, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T>
    moment( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment.get( 10 ),
                       Utility::QuantityTraits<ValueType>::one()/10. );
}

//---------------------------------------------------------------------------//
// Check that the calculated moment can be returned
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, get, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T>
    moment( Utility::QuantityTraits<ValueType>::one() );

  FRENSIE_CHECK_EQUAL( moment.get( 10 ),
                       Utility::QuantityTraits<ValueType>::one()/10. );
}

//---------------------------------------------------------------------------//
// Check that the copy constructor can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, copy_constructor, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<1,T> moment_b( moment_a );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the copy constructor can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, copy_constructor, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<2,T> moment_b( moment_a );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the copy constructor can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, copy_constructor, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<3,T> moment_b( moment_a );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the copy constructor can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, copy_constructor, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<4,T> moment_b( moment_a );

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the assignment operator can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, assignment_operator, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<1,T> moment_b = moment_a;

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the assignment operator can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, assignment_operator, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<2,T> moment_b = moment_a;

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the assignment operator can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, assignment_operator, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<3,T> moment_b = moment_a;

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that the assignment operator can be used
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, assignment_operator, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T>
    moment_a( Utility::QuantityTraits<ValueType>::one()/2. );

  Utility::SampleMoment<4,T> moment_b = moment_a;

  FRENSIE_CHECK_EQUAL( moment_b.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that a raw score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, addRawScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T> moment;

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that a raw score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, addRawScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T> moment;

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/4. );

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );
}

//---------------------------------------------------------------------------//
// Check that a raw score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, addRawScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T> moment;

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/8. );

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/4. );
}

//---------------------------------------------------------------------------//
// Check that a raw score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, addRawScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T> moment;

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/16. );

  moment.addRawScore( Utility::QuantityTraits<T>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/8. );
}

//---------------------------------------------------------------------------//
// Check that a processed score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_1, addProcessedScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T> moment;

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that a processed score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_2, addProcessedScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType;

  Utility::SampleMoment<2,T> moment;

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that a processed score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_3, addProcessedScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType;

  Utility::SampleMoment<3,T> moment;

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that a processed score can be added to the moment
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_4, addProcessedScore, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType;

  Utility::SampleMoment<4,T> moment;

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one()/2. );

  moment.addProcessedScore( Utility::QuantityTraits<ValueType>::one()/2. );

  FRENSIE_CHECK_EQUAL( moment.getCurrentScore(),
                       Utility::QuantityTraits<ValueType>::one() );
}

//---------------------------------------------------------------------------//
// Check that the mean can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateMean, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType;

  Utility::SampleMoment<1,T> moment( Utility::QuantityTraits<ValueType>::one()*10. );

  ValueType mean = Utility::calculateMean( moment, 100 );

  FRENSIE_CHECK_EQUAL( mean, Utility::QuantityTraits<ValueType>::one()*0.1 );
}

//---------------------------------------------------------------------------//
// Check that the variance can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateVariance, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );

  ValueType2 variance =
    Utility::calculateVariance( first_moment, second_moment, 100 );

  FRENSIE_CHECK_EQUAL( variance, Utility::QuantityTraits<ValueType2>::one() );
}

//---------------------------------------------------------------------------//
// Check that the std dev can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateStdDev, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );

  ValueType1 std_dev =
    Utility::calculateStdDev( first_moment, second_moment, 100 );

  FRENSIE_CHECK_EQUAL( std_dev, Utility::QuantityTraits<ValueType1>::one() );
}

//---------------------------------------------------------------------------//
// Check that the variance of the mean can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateVarianceOfMean, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );

  ValueType2 variance =
    Utility::calculateVarianceOfMean( first_moment, second_moment, 100 );

  FRENSIE_CHECK_EQUAL( variance, Utility::QuantityTraits<ValueType2>::one()/100. );
}

//---------------------------------------------------------------------------//
// Check that the std dev of the mean can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateStdDevOfMean, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );

  ValueType1 std_dev =
    Utility::calculateStdDevOfMean( first_moment, second_moment, 100 );

  FRENSIE_CHECK_EQUAL( std_dev, Utility::QuantityTraits<ValueType1>::one()/10. );
}

//---------------------------------------------------------------------------//
// Check that the relative error can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateRelativeError, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );

  typename Utility::QuantityTraits<T>::RawType relative_error =
    Utility::calculateRelativeError( first_moment, second_moment, 100 );

  FRENSIE_CHECK_FLOATING_EQUALITY( relative_error, 1.0, 1e-12 );
}

//---------------------------------------------------------------------------//
// Check that the relative variance of the variance can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateRelativeVOV, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typedef typename Utility::SampleMoment<1,T>::ValueType ValueType1;
  typedef typename Utility::SampleMoment<2,T>::ValueType ValueType2;
  typedef typename Utility::SampleMoment<3,T>::ValueType ValueType3;
  typedef typename Utility::SampleMoment<4,T>::ValueType ValueType4;

  Utility::SampleMoment<1,T> first_moment( Utility::QuantityTraits<ValueType1>::one()*10. );
  Utility::SampleMoment<2,T> second_moment( Utility::QuantityTraits<ValueType2>::one()*100. );
  Utility::SampleMoment<3,T> third_moment( Utility::QuantityTraits<ValueType3>::one()*1000. );
  Utility::SampleMoment<4,T> fourth_moment( Utility::QuantityTraits<ValueType4>::one()*10000. );

  typename Utility::QuantityTraits<T>::RawType relative_vov =
    Utility::calculateRelativeVOV( first_moment,
                                   second_moment,
                                   third_moment,
                                   fourth_moment,
                                   100 );

  FRENSIE_CHECK_FLOATING_EQUALITY( relative_vov, 0.97010101010101002, 1e-12 );
}

//---------------------------------------------------------------------------//
// Check that the figure of merit can be calculated
FRENSIE_UNIT_TEST_TEMPLATE( SampleMoment_Helpers, calculateFOM, TestingTypes )
{
  FETCH_TEMPLATE_PARAM( 0, T );

  typename Utility::QuantityTraits<T>::RawType fom =
    Utility::calculateFOM( 2.0, 1e3 );

  FRENSIE_CHECK_FLOATING_EQUALITY( fom, 2.5e-4, 1e-12 );
}

//---------------------------------------------------------------------------//
// end tstSampleMoment.cpp
//---------------------------------------------------------------------------//


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2017 Sebastian Koch <s.koch@tu-berlin.de> and Daniele Panozzo <daniele.panozzo@gmail.com>
//
// 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 <Eigen/Geometry>
//#include <Eigen/Dense>
//#include <Eigen/Sparse>

#include "../../python_shared.h"

#include <igl/copyleft/cgal/mesh_boolean.h>
#include <igl/copyleft/cgal/remesh_self_intersections.h>
#include <igl/copyleft/cgal/RemeshSelfIntersectionsParam.h>

void python_export_igl_cgal(py::module &me) {

  py::module m = me.def_submodule(
    "cgal", "Wrappers for libigl functions that use cgal");

  #include "../../py_igl/copyleft/cgal/py_mesh_boolean.cpp"
  #include "../../py_igl/copyleft/cgal/py_remesh_self_intersections.cpp"
  #include "../../py_igl/copyleft/cgal/py_RemeshSelfIntersectionsParam.cpp"

}


/*
Copyright 2015 Rogier van Dalen.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#define BOOST_TEST_MODULE test_sequence_hash
#include "utility/test/boost_unit_test.hpp"

#include "math/sequence.hpp"

#include <vector>
#include <string>

#include <boost/functional/hash.hpp>

#include "range/tuple.hpp"
#include "range/std/container.hpp"

#include "math/check/check_hash.hpp"

BOOST_AUTO_TEST_SUITE (test_suite_sequence_hash)

template <class Direction> void check_hash_homogeneous() {
    typedef math::sequence <char, Direction> sequence;
    std::vector <sequence> examples;
    examples.push_back (sequence());
    examples.push_back (sequence (std::string ("a")));
    examples.push_back (sequence (std::string ("b")));
    examples.push_back (sequence (std::string ("a\0")));
    examples.push_back (sequence (std::string ("abcd")));
    examples.push_back (math::sequence_annihilator <char, Direction>());

    math::check_hash (examples);
}

template <class Direction> void check_hash_heterogeneous() {
    typedef math::sequence <char, Direction> sequence;
    auto examples = range::make_tuple (
        math::sequence_annihilator <char, Direction>(),
        math::empty_sequence <char, Direction>(),
        math::single_sequence <char, Direction> ('a'),
        math::single_sequence <char, Direction> ('b'),
        math::optional_sequence <char, Direction>(),
        math::optional_sequence <char, Direction> ('a'),
        sequence (),
        sequence (std::string ("a")),
        sequence (std::string ("abc")));

    math::check_hash (examples);
    math::check_cast_hash <math::sequence <char, Direction>> (examples);
}

BOOST_AUTO_TEST_CASE (test_hash) {
    check_hash_homogeneous <math::left>();
    check_hash_homogeneous <math::right>();
    check_hash_heterogeneous <math::left>();
    check_hash_heterogeneous <math::right>();
}

BOOST_AUTO_TEST_SUITE_END()


#include <boost/math/constants/constants.hpp>
#include <boost/qvm/all.hpp>
#include <agate_pris/aplas/qvm.hpp>

int main()
{
    using namespace boost::math::float_constants;
    using namespace boost::qvm;
    using namespace agate_pris;

    auto xy = aplas::vector_2{};
    X(xy) = 1.f;
    Y(xy) = 2.f;

    auto xyz = aplas::vector_3{};
    X(xyz) = 3.f;
    Y(xyz) = 4.f;
    Z(xyz) = 5.f;

    auto xyzw = aplas::vector_4{};
    X(xyzw) = 6.f;
    Y(xyzw) = 7.f;
    Z(xyzw) = 8.f;
    W(xyzw) = 9.f;

    return 0;
}


/*
    Copyright (c) 2021-2022 Xavier Leclercq
    Released under the MIT License
    See https://github.com/ishiko-cpp/time/blob/main/LICENSE.txt
*/

#ifndef _ISHIKO_CPP_TIME_DATE_HPP_
#define _ISHIKO_CPP_TIME_DATE_HPP_

#include "DayOfWeek.hpp"
#include "Month.hpp"
#include <boost/date_time.hpp>

namespace Ishiko
{

class Date
{
public:
    Date(int year, Month month, unsigned char day);
    Date(int year, unsigned char month, unsigned char day);
    Date(const boost::gregorian::date& date);

    int year() const;
    Month month() const;
    unsigned char day() const;
    DayOfWeek dayOfWeek() const;

    std::string toISO8601String() const;

private:
    boost::gregorian::date m_date;
};

}

#endif