/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the tsppd program and library for solving           */
/*  Traveling Salesman Problems with Pickup and Delivery. tsppd requires     */
/*  other commercial and open source software to build. tsppd is decribed    */
/*  in the paper "Exact Methods for Solving Traveling Salesman Problems      */
/*  with Pickup and Delivery in Real Time".                                  */
/*                                                                           */
/*  Copyright (C) 2017 Ryan J. O'Neil <roneil1@gmu.edu>                      */
/*                                                                           */
/*  tsppd is distributed under the terms of the ZIB Academic License.        */
/*  You should have received a copy of the ZIB Academic License along with   */
/*  tsppd. See the file LICENSE. If not, email roneil1@gmu.edu.              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <algorithm>
#include <cmath>
#include <iterator>
#include <limits>
#include <utility>

#include <boost/graph/kruskal_min_spanning_tree.hpp>

#include <tsppd/solver/focacci/filter/one_tree/focacci_tsp_one_tree.h>

using namespace Gecode;
using namespace TSPPD::Data;
using namespace TSPPD::Solver;
using namespace std;

OneTree::OneTree(
    ViewArray<Int::IntView>& next,
    const TSPPDProblem& problem,
    const unsigned int max_iterations) :
    OneTree(next, problem, max_iterations, nullptr) { }

OneTree::OneTree(
    ViewArray<Int::IntView>& next,
    const TSPPDProblem& problem,
    const unsigned int max_iterations,
    TSPPD::AP::PrimalDualAPSolver* ap) :
    next(next),
    problem(problem),
    max_iterations(max_iterations),
    ap(ap),
    graph(next.size() - 2),
    potentials(next.size(), 0),
    edges(next.size(), set<int>()),
    start_index(problem.index("+0")),
    end_index(problem.index("-0")),
    iteration(1),
    w(0) {

    weights = boost::get(boost::edge_weight, graph);
    initialize_one_tree();
}

double OneTree::bound() {
    double best_w = 0;
    vector<set<int>> best_edges;

    while (!done) {
        improve();

        if (iteration == 1 || w > best_w) {
            best_w = w;
            best_edges = edges;
        }

        if (done)
            break;
    }

    w = best_w;
    edges = best_edges;
    return w;
}

void OneTree::improve() {
    // Find the max min 1-tree by updating node potentials based on violation of degree constraints.
    bool is_tour = true;

    // Compute optimal 1-tree.
    edges = vector<set<int>>(next.size(), set<int>());
    auto new_w = minimize_one_tree();

    // Update step size
    auto M = max_iterations;
    auto m = iteration;
    if (m == 1) {
        t1 = new_w / (2.0 * next.size());
        ti = t1;
    } else {
        ti = t1*(m - 1)*(2*M - 5)/(2*(M-1)) - t1*(m-2) + t1*(m-1)*(m-2)/(2*(M-1)*(M-2));
    }

    // Remove node potentials from tour.
    for (auto pi : potentials)
        new_w -= 2 * pi;

    // Update node potentials.
    for (int node = 0; node < next.size(); ++node) {
        if (node != start_index && node != end_index && edges[node].size() != 2)
            is_tour = false;
        potentials[node] += (((int) edges[node].size()) - 2) * ti;
    }

    if (is_tour || iteration++ > max_iterations)
        done = true;
    else
        update_one_tree();

    w = new_w;
}

bool OneTree::has_edge(int from, int to) {
    return edges[from].find(to) != edges[from].end();
}

int OneTree::marginal_cost(int from, int to) {
    vector<bool> seen(edges.size(), false);
    seen[to] = true;
    return marginal_cost(from, to, seen, to, 0);
}

void OneTree::initialize_one_tree() {
    for (int i = 0; i < next.size(); ++i) {
        // Arcs are undirected for MST.
        if (i == start_index || i == end_index)
            continue;

        for (int j = i + 1; j < next.size(); ++j) {
            if (j == start_index || j == end_index)
                continue;

            if (!(next[i].in(j) || next[j].in(i)))
                continue;

            OneTreeEdge e;
            bool inserted;
            boost::tie(e, inserted) = boost::add_edge(i, j, graph);
            weights[e] = undirected_cost(i, j);
        }
    }
}

void OneTree::update_one_tree() {
    auto es = boost::edges(graph);
    for (auto eit = es.first; eit != es.second; ++eit) {
        auto e = *eit;
        weights[e] = transformed_cost(e.m_source, e.m_target);
    }
}

double OneTree::minimize_one_tree() {
    vector<OneTreeEdge> mst;
    boost::kruskal_minimum_spanning_tree(graph, back_inserter(mst));

    // Calculate cost of the MST and pull out edges.
    double z = 0;
    for (auto e : mst) {
        edges[e.m_source].insert(e.m_target);
        edges[e.m_target].insert(e.m_source);
        z += weights[e];
    }

    // Add cheapest arc connecting +0.
    int min_p0_idx = -1;
    double min_p0 = numeric_limits<double>::max();
    for (auto to = next[start_index].min(); to <= next[start_index].max(); ++to) {
        if (!next[start_index].in(to))
            continue;

        auto c = transformed_cost(start_index, to);
        if (c < min_p0) {
            min_p0_idx = to;
            min_p0 = c;
        }
    }
    edges[start_index].insert(min_p0_idx);
    edges[min_p0_idx].insert(start_index);

    // Add cheapest arc connecting -0.
    int min_d0_idx = -1;
    double min_d0 = numeric_limits<double>::max();
    for (int from = 0; from < next.size(); ++from) {
        if (!next[from].in(end_index))
            continue;

        auto c = transformed_cost(from, end_index);
        if (c < min_d0) {
            min_d0_idx = from;
            min_d0 = c;
        }
    }
    edges[end_index].insert(min_d0_idx);
    edges[min_d0_idx].insert(end_index);

    edges[start_index].insert(end_index);
    edges[end_index].insert(start_index);

    return z + min_p0 + min_d0;
}

int OneTree::undirected_cost(int i, int j) {
    auto c_ij = next[i].in(j) ? cost(i, j) : numeric_limits<int>::max();
    auto c_ji = next[j].in(i) ? cost(j, i) : numeric_limits<int>::max();
    return min(c_ij, c_ji);
}

double OneTree::transformed_cost(int i, int j) {
    return undirected_cost(i, j) + potentials[i] + potentials[j];
}

int OneTree::marginal_cost(
    int from,
    int to,
    vector<bool> seen,
    int node,
    int max_edge_cost) {

    // Introducing a nonbasic arc into the basis would create a cycle.
    // The marginal cost of this operation is the cost of the new arc
    // minus the max cost in the cycle. This can be found using DFS.
    for (auto next : edges[node]) {
        if ((node == start_index && next == end_index) || (node == end_index && next == start_index))
            continue;

        if (seen[next])
            continue;

        // If we loop back to the from node, then compute marginal cost.
        if (next == from && node != to)
            return cost(from, to) - max_edge_cost;

        vector<bool> new_seen(seen);
        new_seen[next] = true;

        int new_max = max(max_edge_cost, undirected_cost(node, next));
        auto cost = marginal_cost(from, to, new_seen, next, new_max);
        if (cost > -1)
            return cost;
    }

    return -1;
}

int OneTree::cost(int i, int j) {
    if (ap == nullptr)
        return problem.cost(i, j);
    return ap->get_rc({i, j});
}


#include <boost/multiprecision/cpp_int/add.hpp>


#include <boost/integer.hpp>


#include <Eigen/Core>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

template <int N> struct Mesh
{
    using ElemList = Eigen::Matrix<std::int32_t, N + 1, Eigen::Dynamic>;

    ElemList elems;

    Eigen::VectorXd x_rest;
    Eigen::VectorXd x;
    Eigen::VectorXd v;
    Eigen::VectorXd f;

    double mass = 1.0;

    /// \details Should be precomputed
    Eigen::VectorXd lumped_mass;

    /// \details Should be precomputed
    std::vector<double> volume_array;

    /// \details Should be precomputed
    std::vector<Eigen::Matrix<double, N, N>> rest_shape_mat_inv_array;

    /// \details Should be precomputed
    std::vector<Eigen::Matrix<double, N * N, N*(N + 1)>> vec_PFPx_array;
};

using TetraMesh = Mesh<3>;

/// \brief Read a tetrahedral mesh from a ".mesh" file
///
/// \details Currently, this function is only tested with the data generated by fTetWild:
/// https://github.com/wildmeshing/fTetWild
TetraMesh ReadTetraMesh(const std::string& path)
{
    TetraMesh mesh;

    std::ifstream file(path.c_str());
    std::string   line;

    // Detect the vertices block
    while (std::getline(file, line))
    {
        if (line == "Vertices")
        {
            break;
        }
    }
    std::getline(file, line);

    // Read the number of vertices
    const int num_verts = std::stoi(line);

    // Allocate the vertex data
    mesh.x_rest.resize(num_verts * 3);

    // Read the vertex data
    std::size_t vert_index = 0;
    while (std::getline(file, line))
    {
        if (line == "Triangles")
        {
            break;
        }

        std::istringstream iss(line);

        double x, y, z, zero;
        if (!(iss >> x >> y >> z >> zero))
        {
            assert(false);
        }

        mesh.x_rest.segment<3>(vert_index * 3) = Eigen::Vector3d(x, y, z);

        ++vert_index;
    }

    // This line reads the number of triangles, which should be zero
    std::getline(file, line);

    // Detect the tetrahedra block
    while (std::getline(file, line))
    {
        if (line == "Tetrahedra")
        {
            break;
        }
    }
    std::getline(file, line);

    // Read the number of elements
    const int num_elems = std::stoi(line);

    // Allocate the element data
    mesh.elems.resize(4, num_elems);

    // Read the element data
    std::size_t elem_index = 0;
    while (std::getline(file, line))
    {
        std::istringstream iss(line);

        int e0, e1, e2, e3, zero;
        if (!(iss >> e0 >> e1 >> e2 >> e3 >> zero))
        {
            break;
        }

        // Note: the indices in .mesh files start from "1", not "0"
        mesh.elems(0, elem_index) = e0 - 1;
        mesh.elems(1, elem_index) = e1 - 1;
        mesh.elems(2, elem_index) = e2 - 1;
        mesh.elems(3, elem_index) = e3 - 1;

        ++elem_index;
    }

    // Initialize other values
    mesh.x = mesh.x_rest;
    mesh.v = Eigen::VectorXd::Zero(3 * num_verts);
    mesh.f = Eigen::VectorXd::Zero(3 * num_verts);

    return mesh;
}


#ifndef LP_DEF_VISITOR
#define LP_DEF_VISITOR

#include <boost/python.hpp>
#include <sstream>
#include <string>
#include <boost/python/def_visitor.hpp>

#include <opengm/python/opengmpython.hxx>
#include <opengm/python/converter.hxx>
#include <opengm/python/numpyview.hxx>
#include <opengm/python/pythonfunction.hxx>

#include "gil.hxx"

#include <opengm/inference/inference.hxx>
#include <opengm/opengm.hxx>

#include <opengm/python/opengmpython.hxx>
#include <opengm/python/converter.hxx>
#include <opengm/python/numpyview.hxx>
#include <opengm/python/pythonfunction.hxx>

namespace pylp{

}

template<class INF>
class LpInferenceSuite: public boost::python::def_visitor<LpInferenceSuite<INF> >{
public:
   friend class boost::python::def_visitor_access;
   typedef typename INF::IndexType IndexType;
   typedef typename INF::LabelType LabelType;
   typedef typename INF::ValueType ValueType;

   LpInferenceSuite(){

   }

   template <class classT>
   void visit(classT& c) const{
      c
         .def("_addConstraint", &addConstraintPythonNumpy)
         .def("_addConstraints", &addConstraintsPythonNumpy)

         .def("_lpNodeVariableIndex",&INF::lpNodeVi)
         .def("_lpFactorVariableIndex_Scalar",&lpFactorViScalar)
         .def("_lpFactorVariableIndex_Numpy",&lpFactorIter)
      ;
   }

   // add constraints from python numpy
   static void addConstraintPythonNumpy
   (
      INF & inf,
      opengm::python::NumpyView<IndexType,1> lpVariableIndices,
      opengm::python::NumpyView<ValueType,1> coefficients,
      const ValueType lowerBound,
      const ValueType upperBound
   ) {
      // add constraint
      inf.addConstraint(lpVariableIndices.begin(),lpVariableIndices.end(),coefficients.begin(),lowerBound,upperBound);
   }

   static void addConstraintsPythonNumpy
   (
      INF & inf,
      opengm::python::NumpyView<IndexType,2> lpVariableIndices,
      opengm::python::NumpyView<ValueType,2> coefficients,
      opengm::python::NumpyView<ValueType,1> lowerBounds,
      opengm::python::NumpyView<ValueType,1>  upperBounds
   ) {

      const size_t nVi     = static_cast<size_t>(lpVariableIndices.shape(0));
      const size_t nCoeff  = static_cast<size_t>(coefficients.shape(0));
      const size_t nLBound = static_cast<size_t>(lowerBounds.size());
      const size_t nUBound = static_cast<size_t>(upperBounds.size());

      const size_t nVipPerConstaint    = static_cast<size_t>(lpVariableIndices.shape(1));
      const size_t nCoeffPerConstaint   = static_cast<size_t>(coefficients.shape(1));
      if(nVipPerConstaint!=nCoeffPerConstaint)
         throw opengm::RuntimeError("coefficients per constraints must be as long as variables per constraint");
      opengm::FastSequence<IndexType> lpVar(nVipPerConstaint);
      opengm::FastSequence<IndexType> coeff(nCoeffPerConstaint);
      if(nVi==nCoeff || nCoeff==1){
         // all the same length
         if( (nLBound==nUBound && nLBound == nVi)  || nLBound==1 || nUBound==1){
            // accessors for lower and upper bounds
            // loop over all constraints
            // which should be added
            for(size_t c=0;c<nVi;++c){
               for(size_t i=0;i<nVipPerConstaint;++i){
                  lpVar[i]=lpVariableIndices(c,i);
                  coeff[i]=coefficients(nCoeff==1 ? 0:c ,i);
               }
               // call funtion which adds a single constraint from a lists
               inf.addConstraint(lpVar.begin(),lpVar.end(),coeff.begin(),lowerBounds( nLBound==1 ? 0:c ),upperBounds(nUBound==1 ? 0:c));
            }
         }
         else
            throw opengm::RuntimeError("len(lowerBounds) and len(upperBounds) must be equal to 1 or len(coefficients)");
      }
      else
         throw opengm::RuntimeError("len(coefficients) must be 1 or equal to len(lpVariableIndices)");
   }

   static IndexType lpFactorIter(
      const INF & inf,
      typename INF::IndexType factorIndex,
      opengm::python::NumpyView<typename INF::LabelType> labeling
   ){
      return inf.lpFactorVi(factorIndex,labeling.begin(),labeling.end());
   }

   static IndexType lpFactorViScalar(
      const INF & inf,
      IndexType factorIndex,
      IndexType labelingIndex
   ){
      return inf.lpFactorVi(factorIndex,labelingIndex);
   }

};

#endif // LP_DEF_VISITOR


//| This file is a part of the sferes2 framework.
//| Copyright 2009, ISIR / Universite Pierre et Marie Curie (UPMC)
//| Main contributor(s): Jean-Baptiste Mouret, mouret@isir.fr
//|
//| This software is a computer program whose purpose is to facilitate
//| experiments in evolutionary computation and evolutionary robotics.
//|
//| This software is governed by the CeCILL license under French law
//| and abiding by the rules of distribution of free software.  You
//| can use, modify and/ or redistribute the software under the terms
//| of the CeCILL license as circulated by CEA, CNRS and INRIA at the
//| following URL "http://www.cecill.info".
//|
//| As a counterpart to the access to the source code and rights to
//| copy, modify and redistribute granted by the license, users are
//| provided only with a limited warranty and the software's author,
//| the holder of the economic rights, and the successive licensors
//| have only limited liability.
//|
//| In this respect, the user's attention is drawn to the risks
//| associated with loading, using, modifying and/or developing or
//| reproducing the software by the user in light of its specific
//| status of free software, that may mean that it is complicated to
//| manipulate, and that also therefore means that it is reserved for
//| developers and experienced professionals having in-depth computer
//| knowledge. Users are therefore encouraged to load and test the
//| software's suitability as regards their requirements in conditions
//| enabling the security of their systems and/or data to be ensured
//| and, more generally, to use and operate it in the same conditions
//| as regards security.
//|
//| The fact that you are presently reading this means that you have
//| had knowledge of the CeCILL license and that you accept its terms.

#ifndef PHEN_COLOR_IMAGE_HPP
#define PHEN_COLOR_IMAGE_HPP

#include <map>
#include <modules/nn2/nn.hpp>

#include <modules/nn2/params.hpp>
#include <modules/nn2/gen_hyper_nn.hpp>

// New stuff added ------------------------------------------

#include <cmath>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/join.hpp>
#include <string>

#include "cvmat_serialization.h" // Serialize cv::Mat
#include <glog/logging.h>	// Google Logging

#include "phen_image.hpp" // Base Image class

// New stuff added ------------------------------------------

namespace sferes
{
  namespace phen
  {
     // hyperneat-inspired phenotype, based on a cppn
    SFERES_INDIV(ColorImageCropEuclidean, Image)
    {
      public:
        typedef Gen gen_t;
        typedef typename gen_t::nn_t gen_nn_t;
        SFERES_CONST size_t nb_cppn_inputs = 2 + 2;
        SFERES_CONST size_t nb_cppn_outputs = 3;	// Red, Green, Blue

        ColorImageCropEuclidean():_developed(false)
				{
				}

        void develop()
        {
        	// Check if phenotype has not been developed
        	if (!_developed)
        	{
						// Initialize the image to be a white background image
						reset_image();

						this->gen().init();
						 // develop the parameters
						BGL_FORALL_VERTICES_T(v, this->gen().get_graph(),
																	typename gen_t::nn_t::graph_t)
						{
							this->gen().get_graph()[v].get_afparams().develop();
							this->gen().get_graph()[v].get_pfparams().develop();
						}
						BGL_FORALL_EDGES_T(e, this->gen().get_graph(),
															 typename gen_t::nn_t::graph_t)
						this->gen().get_graph()[e].get_weight().develop();

						assert(nb_cppn_inputs == this->gen().get_nb_inputs());
						assert(nb_cppn_outputs == this->gen().get_nb_outputs());

						int offset = (Params::image::canvas_size - Params::image::size) / 2;

						// Change specific color of every pixel in the image
						int size = offset + Params::image::size;
						for (int x = offset; x < size; ++x)
						{
							for (int y = offset; y < size; ++y)
							{
								std::vector<float> output =	cppn_value(x, y);	// HLS array

								cv::Vec3b color = _image.at<cv::Vec3b>(cv::Point(x,y));

								color[0] = this->convert_to_color_scale(255, output[0]);	// H
								color[1] = this->convert_to_color_scale(255, output[1]);	// L
								color[2] = this->convert_to_color_scale(255, output[2]);	// S

								_image.at<cv::Vec3b>(cv::Point(x,y)) = color;
							}
						}

						_developed = true;	// Raise the flag that this phenotype has been developed.
        	}
        }

        /**
				 * Programmatically put the patterns in here.
				 */
				void reset_image()
				{
					// Paint background : white
					_image = cv::Mat(Params::image::canvas_size, Params::image::canvas_size, CV_8UC3, cv::Scalar(255,255,255));
				}

				double normalize_map_xy_to_grid(const int & r_xyVal, const int & r_numVoxelsXorY)
				{
					// turn the xth or yth node into its coordinates on a grid from -1 to 1, e.g. x values (1,2,3,4,5) become (-1, -.5 , 0, .5, 1)
					// this works with even numbers, and for x or y grids only 1 wide/tall, which was not the case for the original
					// e.g. see findCluster for the orignal versions where it was not a funciton and did not work with odd or 1 tall/wide #s

					double coord;

					if (r_numVoxelsXorY==1) coord = 0;
					else coord = -1 + ( r_xyVal * 2.0/(r_numVoxelsXorY-1) );

					return(coord);
				}

				std::vector<float> cppn_value(size_t i, size_t j)
        {
					const int size = Params::image::canvas_size;

					// Euclidean distance from center
					const float xNormalized = normalize_map_xy_to_grid(i, size);
					const float yNormalized = normalize_map_xy_to_grid(j, size);
					const float distanceFromCenter = sqrt(pow(double(xNormalized),2.0)+pow(double(yNormalized),2.0));

					// CPPN inputs
          std::vector<float> in(nb_cppn_inputs);
          this->gen().init();
          in[0] = i;										// x
          in[1] = j;										// y
          in[2] = distanceFromCenter;		// distance from center
          in[3] = 1.0;									// bias

          for (size_t k = 0; k < this->gen().get_depth(); ++k)
            this->gen().step(in);

          // Get the CPPN output
          std::vector<float> out(nb_cppn_outputs);
          out[0] = this->gen().get_outf(0);		// Hue
          out[1] = this->gen().get_outf(1);		// Lightness
          out[2] = this->gen().get_outf(2);		// Saturation

          return out;
        }

				/**
				 * Convert [-1, 1] range to a color scale
				 * [0, 255] for Saturation / Brightness or
				 * [0, 180] for Hue
				 */
				static int convert_to_color_scale(const int scale, const float value)
				{
					int color = value * scale;

					if (value < 0)
					{
						color *= -1;
					}

					return color;
				}

        void write_png_image(const std::string fileName, const cv::Mat& map)
				{
					// Read the target bitmap
					try
					{
						cv::Mat output;

						// Convert HLS into BGR because imwrite uses BGR color space
						cv::cvtColor(map, output, CV_HLS2BGR);

//						// DEBUGGING
						// Convert to 256x256
//						cv::Size size(256, 256);
//						resize(output, output, size);

						// Parameters for cv::imwrite
						std::vector<int> write_params;
						write_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
						write_params.push_back(0);	// Fastest writing without compression

						// Write to a file
						imwrite(fileName, output, write_params);
					}
					catch (std::runtime_error& ex)
					{
						std::cout << "Failed to write image: " << fileName << std::endl;
						fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
					}
				}

        void log_best_image_fitness(const std::string title)
				{
					std::vector < std::string > list;
					list.push_back (title);
					list.push_back (".png");
					const std::string fileName = boost::algorithm::join (list, "");

					write_png_image(fileName, _image);

					std::cout << "Written to " << title << std::endl;
				}

				cv::Mat& image() {
					return _image;
				}
				const cv::Mat& image() const {
					return _image;
				}

				/**
				 * Get the distance between two images.
				 */
				template<typename Indiv>
				float dist(const Indiv& ind)
				{
					cv::Mat tmp(ind.image());

					assert(tmp.cols == _image.cols);
					assert(tmp.rows == _image.rows);

					// Calculate the L2 distance
					float L2_distance = cv::norm(tmp, _image, cv::NORM_L2);

					return L2_distance;
				}

        /**
				 * Returns image in BGR color space.
				 */
				void imageBGR(cv::Mat& output) {
					// Convert image to BGR before evaluating
//					cv::Mat output;

					// Convert HLS into BGR because imwrite uses BGR color space
					cv::cvtColor(_image, output, CV_HLS2BGR);
				}

				template<class Archive>
				void serialize(Archive & ar, const unsigned int version) {
					dbg::trace trace("phen", DBG_HERE);
					sferes::phen::Indiv<Gen, Fit, Params,  typename stc::FindExact<ColorImageCropEuclidean<Gen, Fit, Params, Exact>, Exact>::ret>::serialize(ar, version);
					ar & BOOST_SERIALIZATION_NVP(_image);
					ar & BOOST_SERIALIZATION_NVP(_developed);
				}

			protected:
				cv::Mat _image;
				bool _developed;

    };
  }
}

#endif


#ifndef __DLSODEPROPAGATOR_CPP_
#define __DLSODEPROPAGATOR_CPP_

#include <boost/filesystem.hpp>

#include "../../../include/propagator/dlsodePropagator/dlsodePropagator.h"
#include "../../../include/mechanism/mechanism.h"
#include "../../../include/fileIO/CSVFileReader/CSVReader.h"
#include "../../../include/fileIO/fileIO/fileIO.h"

#include "../../../include/tools/misc/fortran_routine_block_alias.h"
#include "../../../include/tools/misc/global_extern_vars.h"

namespace propagator_sr {

	dlsodePropagator::dlsodePropagator(std::vector<double> uncertainties, std::string cwd_in) :superPropagator(uncertainties, cwd_in)
	{
		//defer this to actual use time
		propagate_and_initiate_cublic_spline_pgt();

	}

	dlsodePropagator::~dlsodePropagator()
	{
	}

	void dlsodePropagator::propagate_pgt()
	{
		if (pgt_pt.get<std::string>("propagator.primary_type") == std::string("from_file")) {
			// search file, if found, read and return, can not find, keep going
			if (boost::filesystem::exists(this->cwd_pgt + "/output/time_dlsode_M.csv")) {
				propagator_from_file(std::string("_M"));
				integrate_propensity_function_pgt();
				return;
			}
			else if (boost::filesystem::exists(this->cwd_pgt + "/output/time_dlsode_fraction.csv")) {
				propagator_from_file(std::string("_fraction"));
				integrate_propensity_function_pgt();

#if defined(__CHEMKIN_AVAILABLE_) && defined(__LSODE_AVAILABLE_)
				convert_mole_fraction_to_molar_concentration();
#endif // defined(__CHEMKIN_AVAILABLE_) && defined(__LSODE_AVAILABLE_)

				return;
			}
		}

#if defined(__CHEMKIN_AVAILABLE_) && defined(__LSODE_AVAILABLE_)

		std::string sub_job_type = pgt_pt.get<std::string>("propagator.sub_type");
		if (sub_job_type == std::string("temperature_propagator_cv_s2m_pgt"))
			temperature_propagator_cv_s2m_pgt(uncertainties, this->pgt_pt.get<double>("T.critical_temperature"), this->pgt_pt.get<double>("T.end_temperature"));
		else if (sub_job_type == std::string("time_propagator_cv_s2m_pgt"))
			time_propagator_cv_s2m_pgt(uncertainties, this->pgt_pt.get<rsp::my_time_t>("time.critical_time"), this->pgt_pt.get<rsp::my_time_t>("time.max_time"));
		else if (sub_job_type == std::string("time_propagator_cv_ct_s2m_pgt"))
			time_propagator_cv_ct_s2m_pgt(uncertainties, this->pgt_pt.get<rsp::my_time_t>("time.critical_time"), this->pgt_pt.get<rsp::my_time_t>("time.max_time"));
		else if (sub_job_type == std::string("equilibrium_propagator_cv_s2m_pgt"))
			equilibrium_propagator_cv_s2m_pgt(uncertainties, this->pgt_pt.get<double>("T.critical_temperature"));
		else if (sub_job_type == std::string("temperature_propagator_cp_s2m_pgt"))
			temperature_propagator_cp_s2m_pgt(uncertainties, this->pgt_pt.get<double>("T.critical_temperature"), this->pgt_pt.get<double>("T.end_temperature"));
		else if (sub_job_type == std::string("time_propagator_cp_s2m_pgt"))
			time_propagator_cp_s2m_pgt(uncertainties, this->pgt_pt.get<rsp::my_time_t>("time.critical_time"), this->pgt_pt.get<rsp::my_time_t>("time.max_time"));
		else if (sub_job_type == std::string("equilibrium_propagator_cp_s2m_pgt"))
			equilibrium_propagator_cp_s2m_pgt(uncertainties, this->pgt_pt.get<double>("T.critical_temperature"));
		else if (sub_job_type == std::string("time_propagator_cp_ct_s2m_pgt"))
			time_propagator_cp_ct_s2m_pgt(uncertainties, this->pgt_pt.get<double>("time.critical_time"), this->pgt_pt.get<double>("time.max_time"));
		else if (sub_job_type == std::string("time_propagator_s_ct_np_s2m_pgt"))
			time_propagator_s_ct_np_s2m_pgt(uncertainties, this->pgt_pt.get<double>("time.critical_time"), this->pgt_pt.get<double>("time.max_time"));
		else if (sub_job_type == std::string("time_propagator_s_ct_np_cc1_s2m_pgt"))
			time_propagator_s_ct_np_cc1_s2m_pgt(uncertainties, this->pgt_pt.get<double>("time.critical_time"), this->pgt_pt.get<double>("time.max_time"));
		else if (sub_job_type == std::string("time_propagator_s_ct_np_cc2_s2m_pgt"))
			time_propagator_s_ct_np_cc2_s2m_pgt(uncertainties, this->pgt_pt.get<double>("time.critical_time"), this->pgt_pt.get<double>("time.max_time"));

		if (pgt_pt.get<std::string>("propagator.convert_molar_concentration_to_mole_fraction") == std::string("yes"))
			convert_molar_concentration_to_mole_fraction();

#endif // defined(__CHEMKIN_AVAILABLE_) && defined(__LSODE_AVAILABLE_)

	}

	void dlsodePropagator::propagator_from_file(std::string tag)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		std::size_t topN = std::numeric_limits<std::size_t>::max() - 100;
		// time
		std::vector<std::vector<double> > time_Mat;
		time_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/time_dlsode" + tag + ".csv", topN);
		this->time_data_pgt.resize(time_Mat.size());
		for (std::size_t i = 0; i < time_Mat.size(); ++i)
			this->time_data_pgt[i] = time_Mat[i][0];
		time_Mat.resize(0); time_Mat.clear();
		// pressure
		std::vector<std::vector<double> > pressure_Mat;
		pressure_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/pressure_dlsode" + tag + ".csv", topN);
		this->pressure_data_pgt.resize(pressure_Mat.size());
		for (std::size_t i = 0; i < pressure_Mat.size(); ++i)
			this->pressure_data_pgt[i] = pressure_Mat[i][0];
		pressure_Mat.resize(0); pressure_Mat.clear();
		// temperature
		std::vector<std::vector<double> > temperature_Mat;
		temperature_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/temperature_dlsode" + tag + ".csv", topN);
		this->temperature_data_pgt.resize(temperature_Mat.size());
		for (std::size_t i = 0; i < temperature_Mat.size(); ++i)
			this->temperature_data_pgt[i] = temperature_Mat[i][0];
		temperature_Mat.resize(0); temperature_Mat.clear();
		// concentration
		std::vector<std::vector<double> > concentration_Mat;
		concentration_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/concentration_dlsode" + tag + ".csv", topN);
		this->concentration_data_pgt.resize(concentration_Mat[0].size());
		for (std::size_t i = 0; i < concentration_Mat[0].size(); ++i) {
			this->concentration_data_pgt[i].resize(concentration_Mat.size());
			for (std::size_t j = 0; j < concentration_Mat.size(); ++j) {
				this->concentration_data_pgt[i][j] = concentration_Mat[j][i];
			}
		}
		concentration_Mat.resize(0); concentration_Mat.clear();
		// drc
		std::vector<std::vector<double> > drc_Mat;
		drc_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/drc_dlsode" + tag + ".csv", topN);
		this->spe_drc_data_pgt.resize(drc_Mat[0].size());
		for (std::size_t i = 0; i < drc_Mat[0].size(); ++i) {
			this->spe_drc_data_pgt[i].resize(drc_Mat.size());
			for (std::size_t j = 0; j < drc_Mat.size(); ++j) {
				this->spe_drc_data_pgt[i][j] = drc_Mat[j][i];
			}
		}
		drc_Mat.resize(0); drc_Mat.clear();
		// reaction rate
		std::vector<std::vector<double> > reaction_rate_Mat;
		reaction_rate_Mat = fileIO::fileIO::read_topN_line_csv_matrix(this->cwd_pgt + "/output/reaction_rate_dlsode" + tag + ".csv", topN);
		this->reaction_rate_data_pgt.resize(reaction_rate_Mat[0].size());
		for (std::size_t i = 0; i < reaction_rate_Mat[0].size(); ++i) {
			this->reaction_rate_data_pgt[i].resize(reaction_rate_Mat.size());
			for (std::size_t j = 0; j < reaction_rate_Mat.size(); ++j) {
				this->reaction_rate_data_pgt[i][j] = reaction_rate_Mat[j][i];
			}
		}
		reaction_rate_Mat.resize(0); reaction_rate_Mat.clear();

	}

#if defined(__CHEMKIN_AVAILABLE_) && defined(__LSODE_AVAILABLE_)

	void dlsodePropagator::time_propagator_cv_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();
		//spe_production_rate_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;
		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		this->read_configuration(Temp, ckstore.pressure, neq - 1, x_t);

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		double dt = 0;
		this->initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant volume condition. So 'ckstore.rhomass' is constant in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);
		//spe_production_rate_data_pgt.resize(nkk);

		//while (tout < end_time)
		do
		{
			//destruction relative rate Constant of species
			//[ print out
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodev(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodav(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_cv_ct_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();
		//spe_production_rate_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();

		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		this->read_configuration(Temp, ckstore.pressure, neq - 1, x_t);

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		double dt = 0;
		this->initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant volume condition. So 'ckstore.rhomass' is constant in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);
		//spe_production_rate_data_pgt.resize(nkk);

		//while (tout < end_time)
		do
		{
			//[ print out
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodevt(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodavt(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::temperature_propagator_cv_s2m_pgt(std::vector<double> uncertainties, double critical_temperature_t, double end_temperature)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();
		//spe_production_rate_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();

		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		this->read_configuration(Temp, ckstore.pressure, neq - 1, x_t);

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		double dt = 0;
		this->initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant volume condition. So 'ckstore.rhomass' is constant in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);
		//spe_production_rate_data_pgt.resize(nkk);

		//while (Temp<end_temperature)
		while ((Temp < end_temperature) || (temperature_data_pgt.back() < end_temperature))
		{
			//[ print out
			if ((Temp >= critical_temperature_t) && (print_Count%lsodestore.deltaN2 == 0)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if (print_Count%lsodestore.deltaN1 == 0) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodev(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodav(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		}//while

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::equilibrium_propagator_cv_s2m_pgt(std::vector<double> uncertainties, double critical_temperature_t)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp, Temp_t = 0.0;
		read_configuration(Temp, ckstore.pressure, neq - 1, x_t);
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant volume condition. So 'ckstore.rhomass' is constant in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (Temp<end_temperature)
		//while ((Temp<end_temperature) || (temperature_data_pgt.back()<end_temperature))
		while (Temp != Temp_t)
		{
			Temp_t = Temp;

			//[ print out
			if ((Temp >= critical_temperature_t) && (print_Count%lsodestore.deltaN2 == 0)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if (print_Count%lsodestore.deltaN1 == 0) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cv(&Temp, &ckstore.rhomass, y_t, &ckstore.pressure, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodev(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodav(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;

		}//while

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_cp_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		read_configuration(Temp, ckstore.pressure, neq - 1, x_t);
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant pressure condition.
		//So 'ckstore.pressure' is consant, 'ckstore.rhomass' is not in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		while (tout < end_time)
		{
			//destruction relative rate Constant of species
			//[ print out
			//int NPrecision=16;
			if ((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if (print_Count%lsodestore.deltaN1 == 0) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodep(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodap(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		}//while

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::temperature_propagator_cp_s2m_pgt(std::vector<double> uncertainties, double critical_temperature_t, double end_temperature)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		read_configuration(Temp, ckstore.pressure, neq - 1, x_t);

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant pressure condition.
		//So 'ckstore.pressure' is consant, 'ckstore.rhomass' is not in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (Temp<end_temperature)
		while ((Temp < end_temperature) || (temperature_data_pgt.back() < end_temperature))
		{
			//destruction relative rate Constant of species
			//[ print out
			//int NPrecision=16;
			//std::fill(vec_for_rr_tmp.begin(), vec_for_rr_tmp.end(), 0);
			if ((Temp >= critical_temperature_t) && (print_Count%lsodestore.deltaN2 == 0)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if (print_Count%lsodestore.deltaN1 == 0) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodep(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodap(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		}//while

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::equilibrium_propagator_cp_s2m_pgt(std::vector<double> uncertainties, double critical_temperature_t)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ sytle index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp, Temp_t = 0.0;
		read_configuration(Temp, ckstore.pressure, neq - 1, x_t);
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant pressure condition.
		//So 'ckstore.pressure' is consant, 'ckstore.rhomass' is not in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (Temp<end_temperature)
		//while ((Temp<end_temperature) || (temperature_data_pgt.back()<end_temperature))
		while (Temp != Temp_t)
		{
			Temp_t = Temp;

			//[ print out
			if ((Temp >= critical_temperature_t) && (print_Count%lsodestore.deltaN2 == 0)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if (print_Count%lsodestore.deltaN1 == 0) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodep(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodap(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;

		}//while

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_cp_ct_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ style index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS FRACTION of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar fraction
		double* x_t = new double[nkk]; double* y_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { x_t[i] = 0.0; y_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		read_configuration(Temp, ckstore.pressure, neq - 1, x_t);
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));

		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//convert mole fractions to mass fractions
		mechanism::kinetics::xty(x_t, y_t);
		//Returns the mass density of the gas mixture given pressure, temperature(s) and mass fractions
		//The simulation is done in the constant pressure condition.
		//So 'ckstore.pressure' is consant, 'ckstore.rhomass' is not in the simulation.
		mechanism::kinetics::rhoy(&ckstore.pressure, &Temp, y_t, &ckstore.rhomass);

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = y_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;
		//molar concentration.
		double* c_t = new double[nkk];
		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (tout < end_time)
		do
		{
			//destruction relative rate Constant of species
			//[ print out
			//int NPrecision=16;
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_c_CDOT_DDOT_FWDR_REVR_at_cp(&Temp, &ckstore.pressure, y_t, &ckstore.rhomass, c_t, x_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodept(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodapt(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update mass fractions
			for (int i = 0; i < nkk; ++i)
				y_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		delete[] x_t; delete[] y_t; delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_s_ct_np_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ style index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS molar concentration of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar concentration.
		double* c_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { c_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		read_configuration(Temp, ckstore.pressure, neq - 1, c_t);
		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = c_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;

		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//"imaginary pressure"
		//double i_pressure=0.0;

		//while (tout < end_time)
		do
		{
			//[ print out
			//int NPrecision=16;
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodest(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodast(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update  molar concentration
			for (int i = 0; i < nkk; ++i)
				c_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		//delete[] x_t;
		delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_s_ct_np_cc1_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ style index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS molar concentration of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar concentration.
		double* c_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { c_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		read_configuration(Temp, ckstore.pressure, neq - 1, c_t);
		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = c_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;

		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (tout < end_time)
		do
		{
			//[ print out
			//int NPrecision=16;
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodest(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodastcc1(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update  molar concentration
			for (int i = 0; i < nkk; ++i)
				c_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		//delete[] x_t;
		delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

	void dlsodePropagator::time_propagator_s_ct_np_cc2_s2m_pgt(std::vector<double> uncertainties, double critical_time, double end_time)
	{
		time_data_pgt.clear();
		temperature_data_pgt.clear();
		pressure_data_pgt.clear();

		concentration_data_pgt.clear();
		reaction_rate_data_pgt.clear();
		spe_drc_data_pgt.clear();

		//number of reaction in reaction network space
		std::size_t num_reaction = std::distance(reactionNetwork_chemkin_index_map.begin(), reactionNetwork_chemkin_index_map.end());

		//set new rate constant
		int I_t = 1;	double R_A = 0.0;
		//cout<<"\nPre-exponential Constant, old and new: "<<endl;
		for (; static_cast<size_t>(I_t) <= uncertainties.size(); ++I_t) {
			mechanism::kinetics::raex(&I_t, &R_A);
			//cout<<I_t<<" "<<R_A<<"\t";
			//C/C++ style index to Fortran style index
			I_t = -I_t; R_A *= uncertainties[abs(I_t) - 1];
			mechanism::kinetics::raex(&I_t, &R_A);
			////To see whether it changed or not
			I_t = abs(I_t);
			//chemkincpp_sr::chemkin::ckraex(&I_t, &R_A);
			//cout<<R_A<<endl;
		}

		// xgst[0]~xgst[nkk-1] are the MASS molar concentration of each species. xgst[nkk] is the temperature.
		//in which nkk is number of species
		//number of equations neq=nkk+1
		const int nkk = mechanism::kinetics::nkk(), neq = mechanism::kinetics::nkk() + 1, nii = mechanism::kinetics::nii();
		//initial conditions for lsode.
		double* xgst = new double[neq];	for (int i = 0; i < neq; ++i) xgst[i] = 0.0;

		//molar concentration.
		double* c_t = new double[nkk];
		for (int i = 0; i < nkk; ++i) { c_t[i] = 0.0; }

		// Read 'Temperature(K)' and 'Pressure(atm)'.
		// Read	MOLAR FRACTION for species.
		double Temp;
		//	chem_init(Temp, ckstore.pressure, neq-1, x_t, this->cwd_dl+std::string("/input/setting.cfg"));
		read_configuration(Temp, ckstore.pressure, neq - 1, c_t);
		//add temperature to initial conditions for lsode.
		xgst[neq - 1] = Temp;
		// Read time step 'dt(s)'.
		// Read 'lsode' parameters.
		//time step
		double dt = 0;
		initialize_lsode(dt);
		// tolerance for lsode smooth
		double tolerance = lsodestore.atol;

		//initialize the 1st order ode.
		for (int i = 0; i < nkk; ++i)
			xgst[i] = c_t[i];

		double ti = 0.0, tout = ti + dt;
		int print_Count = 0;

		//species creation rates and destruction rates.
		double* CDOT_t = new double[nkk]; double* DDOT_t = new double[nkk];
		//forward reaction rates, reverse reaction rates.
		double* FWDR_t = new double[nii]; double* REVR_t = new double[nii];

		concentration_data_pgt.resize(nkk);
		reaction_rate_data_pgt.resize(num_reaction);
		spe_drc_data_pgt.resize(nkk);

		//while (tout < end_time)
		do
		{
			//[ print out
			//int NPrecision=16;
			if (((tout >= critical_time) && (print_Count%lsodestore.deltaN2 == 0)) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			else if ((print_Count%lsodestore.deltaN1 == 0) || ((end_time - ti) < 0.001*dt)) {
				update_CDOT_DDOT_FWDR_REVR_at_np(&Temp, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t);
				update_temporary_data_pgt(nkk, neq, ti, c_t, CDOT_t, DDOT_t, FWDR_t, REVR_t, xgst);
			}
			//] print out

			//ODE::solver::cppdlsodest(&ti, &tout, &neq, xgst);
			ODE::solver::cppdlsodastcc2(&ti, &tout, &neq, xgst);
			smooth_lsode(neq, xgst, tolerance);
			//update  molar concentration
			for (int i = 0; i < nkk; ++i)
				c_t[i] = xgst[i];
			//update Temperature of system
			Temp = xgst[neq - 1];
			//tout=ti+dt;
			ti = tout;
			tout += dt;
			//in case print_Count is too big
			print_Count = (print_Count + 1) % 1000000;
		} while ((end_time - time_data_pgt.back()) > 0.001*dt);

		//delete[] x_t;
		delete[] xgst;
		delete[] c_t;
		delete[] CDOT_t; delete[] DDOT_t;
		delete[] FWDR_t; delete[] REVR_t;
	}

#endif // __CHEMKIN_AVAILABLE_ && __LSODE_AVAILABLE_

}

#endif


/* Copyright 2020, 2021 Evandro Chagas Ribeiro da Rosa <evandro.crr@posgrad.ufsc.br>
 * Copyright 2020, 2021 Rafael de Santiago <r.santiago@ufsc.br>
 *
 * 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.
 */

#include "../include/assembler.hpp"
#include "../include/simulator.hpp"
#include "../include/kbw.hpp"
#include <boost/dll/import.hpp>

inline size_t get_size_t(std::string token) {
    std::stringstream ss;
    ss << token.substr(1, token.size()-1);
    size_t index;
    ss >> index;
    return index;
}

Assembler::Assembler(block_t &blocks, next_block_t &next_block, std::string &end_block) :
    blocks{blocks},
    next_block{next_block},
    end_block{end_block}
    {}

antlrcpp::Any Assembler::visitBlock(kqasmParser::BlockContext *ctx) {
    auto label = visit(ctx->label()).as<std::string>();

    std::vector<std::function<void(Simulator&)>> instructions;

    for (auto inst : ctx->instruction())
        instructions.push_back(visit(inst).as<std::function<void(Simulator&)>>());

    blocks[label] = [instructions](Simulator& sim) {
        for (auto inst : instructions) inst(sim);
    };

    next_block[label] = visit(ctx->end_instruction()).as<std::function<std::string(Simulator&)>>();

    return 0;
}

antlrcpp::Any Assembler::visitEnd_block(kqasmParser::End_blockContext *ctx) {
    auto label = visit(ctx->label()).as<std::string>();

    std::vector<std::function<void(Simulator&)>> instructions;

    for (auto inst : ctx->instruction())
        instructions.push_back(visit(inst).as<std::function<void(Simulator&)>>());

    blocks[label] = [instructions](Simulator& sim) {
        for (auto inst : instructions) inst(sim);
    };

    end_block = label;

    return 0;
}

antlrcpp::Any Assembler::visitCtrl(kqasmParser::CtrlContext *ctx) {
    return visit(ctx->qubits_list());
}

antlrcpp::Any Assembler::visitGate(kqasmParser::GateContext *ctx) {
    std::vector<size_t> ctrl;
    if (ctx->ctrl()) ctrl = visit(ctx->ctrl()).as<std::vector<size_t>>();

    auto qbit_idx = get_size_t(ctx->QBIT()->getText());

    std::vector<double> args;
    if (ctx->arg_list()) args = visit(ctx->arg_list()).as<std::vector<double>>();

    auto gate = visit(ctx->gate_name()).as<std::string>();

    return std::function<void(Simulator&)>{[ctrl, qbit_idx, gate, args] (Simulator &simulator) {
        const boost::unordered_map<std::string, std::function<void(void)>> gate_map{
            {"X",  [&]() {simulator.x(qbit_idx, ctrl);}},
            {"Y",  [&]() {simulator.y(qbit_idx, ctrl);}},
            {"Z",  [&]() {simulator.z(qbit_idx, ctrl);}},
            {"H",  [&]() {simulator.h(qbit_idx, ctrl);}},
            {"S",  [&]() {simulator.s(qbit_idx, ctrl);}},
            {"SD", [&]() {simulator.sd(qbit_idx, ctrl);}},
            {"T",  [&]() {simulator.t(qbit_idx, ctrl);}},
            {"TD", [&]() {simulator.td(qbit_idx, ctrl);}},
            {"RX", [&]() {simulator.u3(args[0], -M_PI_2, M_PI_2, qbit_idx, ctrl);}},
            {"RY", [&]() {simulator.u3(args[0], 0, 0, qbit_idx, ctrl);}},
            {"RZ", [&]() {simulator.rz(args[0], qbit_idx, ctrl);}},
            {"P",  [&]() {simulator.p(args[0], qbit_idx, ctrl);}},
        };

        gate_map.at(gate)();

    }};
}

antlrcpp::Any Assembler::visitAlloc(kqasmParser::AllocContext *ctx) {
    auto qubit_idx = get_size_t(ctx->QBIT()->getText());
    bool dirty = ctx->DIRTY();

    return std::function<void(Simulator&)>{[qubit_idx, dirty](Simulator &simulator) {
        simulator.alloc(qubit_idx, dirty);
    }};
}

antlrcpp::Any Assembler::visitFree(kqasmParser::FreeContext *ctx) {
    auto qubit_idx = get_size_t(ctx->QBIT()->getText());
    bool dirty = ctx->DIRTY();
    return std::function<void(Simulator&)>{[qubit_idx, dirty](Simulator &simulator) {
        simulator.free(qubit_idx, dirty);
    }};
}

antlrcpp::Any Assembler::visitMeasure(kqasmParser::MeasureContext *ctx) {
    auto qubit_idx = visit(ctx->qubits_list()).as<std::vector<size_t>>();
    auto int_idx = get_size_t(ctx->INT()->getText());
    return std::function<void(Simulator&)>{[qubit_idx, int_idx](Simulator &simulator) {
        for (auto i : qubit_idx)
            simulator.measure(i);

        std::int64_t value = 0;
        size_t i = 0;
        for (auto j = qubit_idx.rbegin(); j != qubit_idx.rend(); ++j)
            value |= simulator.get_bit(*j) << i++;

        simulator.set_i64(int_idx, value);
    }};
}

antlrcpp::Any Assembler::visitLabel(kqasmParser::LabelContext *ctx) {
    return ctx->LABEL()->getText();
}

antlrcpp::Any Assembler::visitBranch(kqasmParser::BranchContext *ctx) {
    auto then = ctx->then->getText();
    auto otherwise = ctx->otherwise->getText();
    auto i64_idx = get_size_t(ctx->INT()->getText());

    return std::function<std::string(Simulator&)>{[i64_idx, then, otherwise](Simulator &simulator) {
        return simulator.get_i64(i64_idx)? then : otherwise;
    }};
}

antlrcpp::Any Assembler::visitJump(kqasmParser::JumpContext *ctx) {
    auto label = ctx->LABEL()->getText();

    return std::function<std::string(Simulator&)>{[label](Simulator&) {
        return label;
    }};
}

antlrcpp::Any Assembler::visitDump(kqasmParser::DumpContext *ctx) {
    auto qubit_idx = visit(ctx->qubits_list()).as<std::vector<size_t>>();

    return std::function<void(Simulator&)>{[qubit_idx](Simulator &simulator) {
        simulator.dump(qubit_idx);
    }};
}

antlrcpp::Any Assembler::visitPlugin(kqasmParser::PluginContext *ctx) {

    auto qubit_idx = visit(ctx->qubits_list()).as<std::vector<size_t>>();

    std::vector<size_t> ctrl_idx;
    if (ctx->ctrl()) ctrl_idx = visit(ctx->ctrl()).as<std::vector<size_t>>();

    auto args = ctx->ARGS() ? ctx->ARGS()->getText().substr(1, ctx->ARGS()->getText().size()-2) : "";

    bool adj = ctx->ADJ();
    auto ctrl = ctrl_idx.size();

    auto plugin_name = ctx->STR()->getText();

    return std::function<void(Simulator&)>{[plugin_name, ctrl, adj, qubit_idx, args, ctrl_idx](Simulator &simulator) {
        std::stringstream path_ss{plugin_path};
        std::string path;
        boost::shared_ptr<ket::bitwise_api> plugin;
        while (std::getline(path_ss, path, ':')) {
            try {
                boost::dll::fs::path lib_path(path);
                plugin = boost::dll::import<ket::bitwise_api>(lib_path / plugin_name,
                                                              "plugin",
                                                              boost::dll::load_mode::append_decorations);
                break;
            } catch (boost::system::system_error &e) {
                continue;
            }
        }

        simulator.apply_plugin(plugin, qubit_idx, args, adj, ctrl_idx);
    }};
}

antlrcpp::Any Assembler::visitBinary_op(kqasmParser::Binary_opContext *ctx) {
    auto result = get_size_t(ctx->result->getText());
    auto left_idx = get_size_t(ctx->left->getText());
    auto right_idx = get_size_t(ctx->right->getText());
    auto op = visit(ctx->bin_op()).as<std::string>();

    return std::function<void(Simulator&)>{[result, left_idx, right_idx, op](Simulator &simulator) {
        auto left = simulator.get_i64(left_idx);
        auto right = simulator.get_i64(right_idx);

        const boost::unordered_map<std::string, std::function<void(void)>> op_map{
            { "==",  [&](){simulator.set_i64(result, left == right);}},
            { "!=",  [&](){simulator.set_i64(result, left != right);}},
            { ">",   [&](){simulator.set_i64(result, left >  right);}},
            { ">=",  [&](){simulator.set_i64(result, left >= right);}},
            { "<",   [&](){simulator.set_i64(result, left <  right);}},
            { "<=",  [&](){simulator.set_i64(result, left <= right);}},
            { "+",   [&](){simulator.set_i64(result, left +  right);}},
            { "-",   [&](){simulator.set_i64(result, left -  right);}},
            { "*",   [&](){simulator.set_i64(result, left *  right);}},
            { "/",   [&](){simulator.set_i64(result, left /  right);}},
            { "<<",  [&](){simulator.set_i64(result, left << right);}},
            { ">>",  [&](){simulator.set_i64(result, left >> right);}},
            { "and", [&](){simulator.set_i64(result, left &  right);}},
            { "or",  [&](){simulator.set_i64(result, left |  right);}},
            { "xor", [&](){simulator.set_i64(result, left ^  right);}},
        };

        op_map.at(op)();
    }};
}

antlrcpp::Any Assembler::visitConst_int(kqasmParser::Const_intContext *ctx) {
    std::stringstream ss;
    ss << ctx->UINT()->getText();
    std::int64_t uval;
    ss >> uval;
    std::int64_t val = ctx->SIG()? -uval : uval;
    auto idx = get_size_t(ctx->INT()->getText());

    return std::function<void(Simulator&)>{[idx, val](Simulator &simulator) {
        simulator.set_i64(idx, val);
    }};
}

antlrcpp::Any Assembler::visitSet(kqasmParser::SetContext *ctx) {
    auto i64_in_idx = get_size_t(ctx->target->getText());
    auto i64_value_idx = get_size_t(ctx->from->getText());

    return std::function<void(Simulator&)>{[i64_in_idx, i64_value_idx](Simulator &simulator) {
        simulator.set_i64(i64_in_idx, simulator.get_i64(i64_value_idx));
    }};
}

antlrcpp::Any Assembler::visitBin_op(kqasmParser::Bin_opContext *ctx) {
    return ctx->getText();
}

antlrcpp::Any Assembler::visitArg_list(kqasmParser::Arg_listContext *ctx) {
    std::vector<double> args;
    for (auto i : ctx->DOUBLE())
        args.push_back(atof(i->getText().c_str()));
    return args;
}

antlrcpp::Any Assembler::visitGate_name(kqasmParser::Gate_nameContext *ctx) {
    return ctx->getText();
}

antlrcpp::Any Assembler::visitQubits_list(kqasmParser::Qubits_listContext *ctx) {
    std::vector<size_t> qubits;
    for (auto i : ctx->QBIT())
        qubits.push_back(get_size_t(i->getText()));
    return qubits;
}