// Copyright 2018-2019 Henry Schreiner and Hans Dembinski
//
// Distributed under the 3-Clause BSD License.  See accompanying
// file LICENSE or https://github.com/scikit-hep/boost-histogram for details.

#include <bh_python/pybind11.hpp>

#include <boost/histogram/algorithm/reduce.hpp>

void register_algorithms(py::module& algorithm) {
    py::class_<bh::algorithm::reduce_command>(algorithm, "reduce_command")
        .def(py::init<bh::algorithm::reduce_command>())
        .def_readwrite("iaxis", &bh::algorithm::reduce_command::iaxis)
        .def("__repr__", [](const bh::algorithm::reduce_command& self) {
            using range_t = bh::algorithm::reduce_command::range_t;

            if(self.range != range_t::none) {
                const char* suffix  = self.merge > 0 ? "_and_rebin" : "";
                const char* c_start = self.iaxis == bh::algorithm::reduce_command::unset
                                          ? ""
                                          : "iaxis={0}, ";
                const char* c_merge = self.merge > 0 ? ", merge={0}" : "";

                py::str start = py::str(c_start).format(self.iaxis);
                py::str merge = py::str(c_merge).format(self.merge);

                if(self.range == range_t::indices) {
                    return py::str("reduce_command(slice{0}({1}begin={2}, "
                                   "end={3}{4}, mode={5}))")
                        .format(suffix,
                                start,
                                self.begin.index,
                                self.end.index,
                                merge,
                                self.crop ? "slice_mode.crop" : "slice_mode.shrink");
                } else {
                    return py::
                        str("reduce_command(shrink{0}({1}, lower={2}, upper={3}{4}))")
                            .format(
                                suffix, start, self.begin.value, self.end.value, merge);
                }
            }

            // self.range == range_t::none
            return py::str("reduce_command(merge({0}))").format(self.merge);
        });

    using slice_mode = bh::algorithm::slice_mode;

    py::enum_<slice_mode>(algorithm, "slice_mode")
        .value("shrink", slice_mode::shrink)
        .value("crop", slice_mode::crop);

    algorithm
        .def("shrink_and_rebin",
             py::overload_cast<unsigned, double, double, unsigned>(
                 &bh::algorithm::shrink_and_rebin),
             "iaxis"_a,
             "lower"_a,
             "upper"_a,
             "merge"_a,
             "Shrink and rebin option to be used in reduce().\n"
             "\n"
             "To shrink and rebin in one command. Equivalent to passing both the "
             "shrink() and the\n"
             "rebin() option for the same axis to reduce.\n"
             "\n"
             ":param iaxis: which axis to operate on.\n"
             ":param lower: lowest bound that should be kept.\n"
             ":param upper: highest bound that should be kept. If upper is inside "
             "bin interval, the whole "
             "interval is removed.\n"
             ":param merge: how many adjacent bins to merge into one.")
        .def("shrink_and_rebin",
             py::overload_cast<double, double, unsigned>(
                 &bh::algorithm::shrink_and_rebin),
             "lower"_a,
             "upper"_a,
             "merge"_a,
             "Positional shrink and rebin option to be used in reduce().\n"
             "\n"
             "To shrink and rebin in one command. Equivalent to passing both the "
             "shrink() and the\n"
             "rebin() option for the same axis to reduce.\n"
             "\n"
             ":param iaxis: which axis to operate on.\n"
             ":param lower: lowest bound that should be kept.\n"
             ":param upper: highest bound that should be kept. If upper is inside "
             "bin interval, the whole "
             "interval is removed.\n"
             ":param merge: how many adjacent bins to merge into one.")

        .def("crop_and_rebin",
             py::overload_cast<unsigned, double, double, unsigned>(
                 &bh::algorithm::crop_and_rebin),
             "iaxis"_a,
             "lower"_a,
             "upper"_a,
             "merge"_a,
             "Crop and rebin option to be used in reduce().\n"
             "\n"
             "To crop and rebin in one command. Equivalent to passing both the "
             "crop and the\n"
             "rebin option for the same axis to reduce.\n"
             "\n"
             ":param iaxis: which axis to operate on.\n"
             ":param lower: lowest bound that should be kept.\n"
             ":param upper: highest bound that should be kept. If upper is inside "
             "bin interval, the whole "
             "interval is removed.\n"
             ":param merge: how many adjacent bins to merge into one.")
        .def(
            "crop_and_rebin",
            py::overload_cast<double, double, unsigned>(&bh::algorithm::crop_and_rebin),
            "lower"_a,
            "upper"_a,
            "merge"_a,
            "Positional crop and rebin option to be used in reduce().\n"
            "\n"
            "To crop and rebin in one command. Equivalent to passing both the "
            "crop and the\n"
            "rebin option for the same axis to reduce.\n"
            "\n"
            ":param iaxis: which axis to operate on.\n"
            ":param lower: lowest bound that should be kept.\n"
            ":param upper: highest bound that should be kept. If upper is inside "
            "bin interval, the whole "
            "interval is removed.\n"
            ":param merge: how many adjacent bins to merge into one.")

        .def("slice_and_rebin",
             py::overload_cast<unsigned,
                               bh::axis::index_type,
                               bh::axis::index_type,
                               unsigned,
                               slice_mode>(&bh::algorithm::slice_and_rebin),
             "iaxis"_a,
             "begin"_a,
             "end"_a,
             "merge"_a,
             "mode"_a = slice_mode::shrink,
             "Slice and rebin option to be used in reduce().\n"
             "\n"
             "To slice and rebin in one command. Equivalent to passing both the "
             "slice() and the\n"
             "rebin() option for the same axis to reduce.\n"
             "\n"
             ":param iaxis: which axis to operate on.\n"
             ":param begin: first index that should be kept.\n"
             ":param end: one past the last index that should be kept.\n"
             ":param merge: how many adjacent bins to merge into one.\n"
             ":param mode: see slice_mode")
        .def("slice_and_rebin",
             py::overload_cast<bh::axis::index_type,
                               bh::axis::index_type,
                               unsigned,
                               slice_mode>(&bh::algorithm::slice_and_rebin),
             "begin"_a,
             "end"_a,
             "merge"_a,
             "mode"_a = slice_mode::shrink,
             "Positional slice and rebin option to be used in reduce().\n"
             "\n"
             "To slice and rebin in one command. Equivalent to passing both the "
             "slice() and the\n"
             "rebin() option for the same axis to reduce.\n"
             "\n"
             ":param iaxis: which axis to operate on.\n"
             ":param begin: first index that should be kept.\n"
             ":param end: one past the last index that should be kept.\n"
             ":param merge: how many adjacent bins to merge into one.\n"
             ":param mode: see slice_mode")

        .def("rebin",
             py::overload_cast<unsigned, unsigned>(&bh::algorithm::rebin),
             "iaxis"_a,
             "merge"_a)
        .def("rebin", py::overload_cast<unsigned>(&bh::algorithm::rebin), "merge"_a)

        .def("shrink",
             py::overload_cast<unsigned, double, double>(&bh::algorithm::shrink),
             "iaxis"_a,
             "lower"_a,
             "upper"_a)
        .def("shrink",
             py::overload_cast<double, double>(&bh::algorithm::shrink),
             "lower"_a,
             "upper"_a)

        .def("crop",
             py::overload_cast<unsigned, double, double>(&bh::algorithm::crop),
             "iaxis"_a,
             "lower"_a,
             "upper"_a)
        .def("crop",
             py::overload_cast<double, double>(&bh::algorithm::crop),
             "lower"_a,
             "upper"_a)

        .def("slice",
             py::overload_cast<unsigned,
                               bh::axis::index_type,
                               bh::axis::index_type,
                               slice_mode>(&bh::algorithm::slice),
             "iaxis"_a,
             "begin"_a,
             "end"_a,
             "mode"_a = slice_mode::shrink)
        .def("slice",
             py::overload_cast<bh::axis::index_type, bh::axis::index_type, slice_mode>(
                 &bh::algorithm::slice),
             "begin"_a,
             "end"_a,
             "mode"_a = slice_mode::shrink)

        ;
}


#include <vector>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <string>
#include <regex>

#include <stdio.h>      /* printf, scanf, puts, NULL */
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */

#include "PerceptualHash.h"
#include "PerceptualHash_Fast.h"

//#include "FragmentHash.h"
//#include "ShapeAndPositionInvariantImage.h"
#include "Triangle.h"
#include "mainImageProcessingFunctions.hpp"
#include <boost/program_options.hpp>
#include <iostream>
#include "utils.hpp"
#include "hiredis/hiredis.h"
#include <map>

using namespace std;

void addAllHashesToRedis(string imagePath){
    auto loadedImage = getLoadedImage(imagePath);
    vector<Keypoint> keypoints = getKeypoints(loadedImage.getImageData());
    vector<Triangle> tris = buildTrianglesFromKeypoints(keypoints, 50, 400);;
    auto hashTrianglePairs = cv::getAllTheHashesForImage<hashes::PerceptualHash>(loadedImage, tris);

    redisContext *c;
//    redisReply *reply;
    const char *hostname = "127.0.0.1";
    int port = 6379;

    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    c = redisConnectWithTimeout(hostname, port, timeout);
    if (c == NULL || c->err) {
        if (c) {
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } else {
            printf("Connection error: can't allocate redis context\n");
        }
        exit(1);
    }

    int count = 0;
    for (auto hashTriangle : hashTrianglePairs)
    {
        string redisEntry = convertToRedisEntryJson(imagePath, hashTriangle.first);
        redisCommand(c,"SADD %s %s", hashTriangle.second.toString().c_str(), redisEntry.c_str());

	count++;
    }
    cout << "Added " << count << " fragments to DB" << endl;
}

int findMatchingHashInRedis(string imageName){
    auto loadedImage = getLoadedImage(imageName);
    vector<Keypoint> keypoints = getKeypoints(loadedImage.getImageData());
    vector<Triangle> tris = buildTrianglesFromKeypoints(keypoints, 50, 400);;
    auto hashTrianglePairs = cv::getAllTheHashesForImage<hashes::PerceptualHash>(loadedImage, tris);

    redisContext *c;
    redisReply *reply;
    const char *hostname = "127.0.0.1";
    int port = 6379;

    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    c = redisConnectWithTimeout(hostname, port, timeout);
    if (c == NULL || c->err) {
        if (c) {
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } else {
            printf("Connection error: can't allocate redis context\n");
        }
        exit(1);
    }
//    cout << "finished hashing" << endl;
//    vector<hashes::PerceptualHash_Fast> result;
    vector<string> result;
//    for (auto hash : hashes)
//    {
    unsigned int batchSize = 1000;
    for (unsigned int i = 0; i < hashTrianglePairs.size(); i++)
    {
        unsigned int j = 0;
        for(;i < hashTrianglePairs.size() && j < batchSize; j++, i++){
            auto hashTriangle = hashTrianglePairs[i];
            redisAppendCommand(c,"SMEMBERS %s", hashTriangle.second.toString().c_str());
        }

        for(; j > 0; j--){
            redisGetReply(c, (void **) &reply );
            //unsigned int r = redisGetReply(c, (void **) &reply );
            for (unsigned int k = 0; k < reply->elements; k++)
            {
                string str(reply->element[k]->str);
                result.push_back(str);
            }
        }

    }
    std::map<string,vector<Triangle>> resultMap;
    for (auto t_str : result)
    {
        auto redisReplyImageName = getImageNameFromRedisEntry(t_str);
        auto redisReplyTriangle = getTriangleFromRedisEntry(t_str);
        resultMap[redisReplyImageName];
        resultMap[redisReplyImageName].push_back(redisReplyTriangle);
    }
    cout << "Matches:" << endl;
    for(auto const& ent1 : resultMap)
    {
        auto tempImg = cv::imread(ent1.first);
        //drawTrianglesOntoImage(ent1.second, tempImg);
        //cv::imwrite("./outputImages/outputFromSearch_"+ent1.second[0].toString()+".jpg", tempImg);
        cout << ent1.first << ": " << ent1.second.size() << endl;
    }

    cout << "Number of matches: " << result.size() << endl;
    return result.size();
}

void redisClearDatabase(){
    redisContext *c;
    const char *hostname = "127.0.0.1";
    int port = 6379;

    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    c = redisConnectWithTimeout(hostname, port, timeout);
    if (c == NULL || c->err) {
        if (c) {
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } else {
            printf("Connection error: can't allocate redis context\n");
        }
        exit(1);
    }
    redisCommand(c,"FLUSHALL");
    redisFree(c);
}

void compareTwoImages(string imageName1, string imageName2) {

    //clear the db
    redisClearDatabase();

    //add the first image to db
    addAllHashesToRedis(imageName1);

    //check for matches using the second image
    cout << "{\n\tcount: " << findMatchingHashInRedis(imageName2) << "\n}";
}

int main(int argc, char* argv[])
{
    if (argc < 3){
        printf("error: no args!!!\n");
        return -1;
    }

    if (argc > 2 && !strcmp(argv[1], "insert")){
	for (int i = 2; i < argc; i++) {
    		string imageName = argv[i];
        	addAllHashesToRedis(imageName);
	}
    }else if (argc > 2 && !strcmp(argv[1], "lookup")){
	for (int i = 2; i < argc; i++) {
    		string imageName = argv[i];
        	findMatchingHashInRedis(imageName);
	}
    }else{
        cout << "Bad argument: " << argv[1] << endl;
    }
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_PUT_FIRST_HPP_INCLUDED
#define BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_PUT_FIRST_HPP_INCLUDED
#ifdef BOOST_SIMD_HAS_SSE2_SUPPORT
#include <boost/simd/swar/functions/put_first.hpp>
#include <boost/dispatch/meta/as_floating.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/sdk/meta/make_dependent.hpp>
#include <boost/dispatch/meta/strip.hpp>
#include <boost/simd/include/functions/simd/shli.hpp>
#include <boost/simd/include/functions/simd/shri.hpp>

namespace boost { namespace simd { namespace ext
{
  //============================================================================
  // Implementation when type A0 is double
  //============================================================================
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::put_first_
                                    , boost::simd::tag::sse2_
                                    , (A0)(A1)
                                    , ((simd_<double_<A0>,boost::simd::tag::sse_>))
                                      ((scalar_< integer_<A1> >))
                                    )
  {
    typedef A0 result_type;

    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      if(a1)
      {
        return _mm_unpackhi_pd(a0,a0);
      }
      return a0;
    }
  };

  //============================================================================
  // Implementation when type A0 is type8_
  //============================================================================
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::put_first_
                                    , boost::simd::tag::sse2_
                                    , (A0)(A1)
                                    , ((simd_<type8_<A0>,boost::simd::tag::sse_>))
                                      ((scalar_< integer_<A1> >))
                                    )
  {
    typedef simd::native< typename meta::make_dependent<boost::simd::int64_t,A0>::type
                        , boost::simd::tag::sse_
                        >                                           type64;
    typedef A0                                                      result_type;

    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      if(a1 > 7)
      {
        A0 t = _mm_srli_si128(simd::bitwise_cast<type64>(a0), 8 );
        return simd::bitwise_cast<A0> ( shri( simd::bitwise_cast<type64>(t)
                                            , (a1-8) << 3
                                            )
                                      );
      }

      return simd::bitwise_cast<A0> ( shri(simd::bitwise_cast<type64>(a0)
                                    , a1 << 3)
                                    );
    }
  };

  //============================================================================
  // Implementation when type A0 is type64_
  //============================================================================
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::put_first_
                                    , boost::simd::tag::sse2_
                                    , (A0)(A1)
                                    , ((simd_<type64_<A0>,boost::simd::tag::sse_>))
                                      ((scalar_< integer_<A1> >))
                                    )
  {
    typedef A0                                              result_type;
    typedef typename dispatch::meta::as_floating<A0>::type  rtype;

    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      if(a1)
      {
        return simd::bitwise_cast<A0>
                              ( _mm_unpackhi_pd ( simd::bitwise_cast<rtype>(a0)
                                                , simd::bitwise_cast<rtype>(a0)
                                                )
                              );
      }

      return a0;
    }
  };

  //============================================================================
  // Implementation when type A0 is type16_
  //============================================================================
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::put_first_
                                    , boost::simd::tag::sse2_
                                    , (A0)(A1)
                                    , ((simd_<type16_<A0>,boost::simd::tag::sse_>))
                                      ((scalar_< integer_<A1> >))
                                    )
  {
    typedef A0                                                      result_type;
    typedef typename meta::make_dependent<double,A0>::type          double_t;
    typedef simd::native<double_t,boost::simd::tag::sse_>           rtype;
    typedef typename dispatch::meta::as_integer<rtype,signed>::type type64;

    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      if(a1 > 3)
      {
        A0 t = _mm_srli_si128(simd::bitwise_cast<type64>(a0), 8 );
        return simd::bitwise_cast<A0> ( shri( simd::bitwise_cast<type64>(t)
                                            , (a1-4) << 4
                                            )
                                      );
      }

      return simd::bitwise_cast<A0> ( shri( simd::bitwise_cast<type64>(a0)
                                          , a1 << 4
                                          )
                                    );
    }
  };

  //============================================================================
  // Implementation when type A0 is type32_
  //============================================================================
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION ( boost::simd::tag::put_first_
                                    , boost::simd::tag::sse2_
                                    , (A0)(A1)
                                    , ((simd_<type32_<A0>,boost::simd::tag::sse_>))
                                      ((scalar_< integer_<A1> >))
                                    )
  {
    typedef A0                                                      result_type;
    typedef typename meta::make_dependent<double,A0>::type          double_t;
    typedef simd::native<double_t,boost::simd::tag::sse_>           rtype;
    typedef typename dispatch::meta::as_integer<rtype,signed>::type type64;

    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      if(a1 > 1)
      {
        type64  z = _mm_srli_si128(simd::bitwise_cast<type64>(a0), 8 );
        return simd::bitwise_cast<A0>( shri( z, (a1-2) << 5 ) );
      }

      return simd::bitwise_cast<A0> ( shri( simd::bitwise_cast<type64>(a0)
                                          , a1 << 5
                                          )
                                    );
    }
  };
} } }

#endif
#endif


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE contiuous_mvc

#include <boost/test/unit_test.hpp>
#include <cstdlib>
#include <set>
#include <vector>

#include <geneial/algorithm/BaseGeneticAlgorithm.h>
#include <geneial/core/population/chromosome/MultiValueChromosome.h>

#include <geneial/core/population/builder/ContinousMultiValueChromosomeFactory.h>
#include <geneial/core/population/builder/ContinousMultiValueBuilderSettings.h>

#include "mocks/MockFitnessEvaluator.h"

#include "helper/common_casts.h"
#include "helper/continuity.h"
#include "helper/chromosome_compare.h"

using namespace geneial::population::chromosome;

BOOST_AUTO_TEST_SUITE( ContinousMultiValueChromosome )

const int CONTINUITY_FACTORY_SIGNIFICANCE = 1000;

struct continuity_factory_testsets //POD struct for testcases for the following test
{
    int amountChromosomes;
    int epsilon;
    int min;
    int max;
    bool hasStart;
    int start;
};

BOOST_AUTO_TEST_CASE( Continuity_Factory )
{
    const test_mock::MockFitnessEvaluator<double>::ptr evaluator(new test_mock::MockFitnessEvaluator<double>);

    int i = CONTINUITY_FACTORY_SIGNIFICANCE;

    std::vector<continuity_factory_testsets> testcases = {
            {100,5,0,130,true,20},
            {100,5,0,130,false,0},
            {100,1,0,130,true,20},
            {100,1,0,130,false,0},
            {100,5,0,130,true,0},
            {100,5,0,130,false,0},
            {100,5,0,130,true,5},
            {100,5,-130,130,true,5},
            {100,5,-130,130,false,0},
            {100,5,-130,-10,true,-5},
            {100,5,-130,-10,false,0},
            {100,5,-130,0,true,-5},
            {100,5,-130,0,false,0},
            {100,5,-130,0,true,-5},
            {100,5,-130,0,false,0}
    };

    while (i--)
    {
        for(const auto &testcase :testcases)
        {

            auto builder = ContinousMultiValueChromosomeFactory<int,double>::Builder(evaluator);
            auto factory = std::dynamic_pointer_cast<ContinousMultiValueChromosomeFactory<int,double>>(builder.create());
            builder.getSettings().setNum(testcase.amountChromosomes);
            builder.getSettings().setEps(testcase.epsilon);
            builder.getSettings().setRandomMin(testcase.min);
            builder.getSettings().setRandomMax(testcase.max);
            builder.getSettings().setHasStart(testcase.hasStart);
            builder.getSettings().setStartValue(testcase.start);

            auto chromosome = factory->createChromosome();
            test_helper::checkContinuity<int, double>(chromosome, testcase.epsilon);
            test_helper::checkLimits<int, double>(chromosome, testcase.min, testcase.max);

            if(testcase.hasStart)
            {
                test_helper::checkForStart<int,double>(chromosome,testcase.start,testcase.epsilon);
            }
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()


#include "Common/Common.h"
#include "Demos/Visualization/MiniGL.h"
#include "Demos/Visualization/Selection.h"
#include "GL/glut.h"
#include "Simulation/TimeManager.h"
#include <Eigen/Dense>
#include "Simulation/SimulationModel.h"
#include "Simulation/TimeStepController.h"
#include <iostream>
#include "Demos/Visualization/Visualization.h"
#include "Simulation/DistanceFieldCollisionDetection.h"
#include "Utils/OBJLoader.h"
#include "Utils/Logger.h"
#include "Utils/Timing.h"
#include "Utils/FileSystem.h"
#include "Demos/Common/DemoBase.h"
#include "Demos/Common/TweakBarParameters.h"
#include "Simulation/Simulation.h"

#include "Utils/PDPJConverter.h"
 #include "ShapeOpGL/mesh.h"
 #include "Utils/MyUtil.h"

 #include <memory>
 #include <opencv2/opencv.hpp>
 #include <unistd.h>
#include <string>
#include <jsoncpp/json/json.h>
#include <sys/stat.h>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/filesystem/convenience.hpp>

#include <stdlib.h>

// Enable memory leak detection
#if defined(_DEBUG) && !defined(EIGEN_ALIGN)
	#define new DEBUG_NEW
#endif

using namespace PBD;
using namespace Eigen;
using namespace std;
using namespace Utilities;

void initParameters();
void timeStep ();
void buildModel ();
void createMesh();
void render ();
void reset();
void TW_CALL setStiffness(const void *value, void *clientData);
void TW_CALL getStiffness(void *value, void *clientData);
void TW_CALL setPoissonRatio(const void *value, void *clientData);
void TW_CALL getPoissonRatio(void *value, void *clientData);
void TW_CALL setNormalizeStretch(const void *value, void *clientData);
void TW_CALL getNormalizeStretch(void *value, void *clientData);
void TW_CALL setNormalizeShear(const void *value, void *clientData);
void TW_CALL getNormalizeShear(void *value, void *clientData);
void TW_CALL setSimulationMethod(const void *value, void *clientData);
void TW_CALL getSimulationMethod(void *value, void *clientData);

DemoBase *base;
short simulationMethod = 2;

/////シミュレーション設定
const unsigned int width = 15;
const unsigned int depth = 3;
const unsigned int height = 3;

// const unsigned int width = 2;
// const unsigned int depth = 2;
// const unsigned int height = 2;

const int reducted = 2;
short bendingMethod = 2;

/////実験設定GLOBAL
std::string idf = "default_collision";
std::string idf_itr = "0";
int max_itr = 40;
int precompute_max_itr = 40;
int substeps = 1;
double frametime;
float rate = 1.0;
double num_frames  = 1500;
float k = 0.8;
float coll_max_speed = 2.0;

float custom;

//SLOPE
bool slope = false;
float ikichi = -1;
float scale = 0.6;

bool if_render = true;
bool img_render = if_render;
bool output_position = false;
bool if_dummy = false;
bool if_dummy_3 = false;
bool custom_bool = true;
bool custom_bool2 = true;
bool custom_bool3 = true;
bool custom_bool4 = true;

/////GLOBAL変数
float old_energy = -1;
float old_inc = -1;
int image_itr = 0;
std::shared_ptr<ShapeOpSurfaceMesh> s1;
MAverage a(3);
MAverage amp(30);
// std::make_shared<std::vector<bool>> violated;
int neo_dummy = 1;

int hold_phase = 0;
float hold_phase_inc = 0;

std::vector<std::vector<int>> neighbors;

////ファイル出力
std::ofstream o_pos;
std::ofstream energy;
std::ofstream precompute_error;
std::ofstream dummy_violate;

std::ofstream cost_log;

DistanceFieldCollisionDetection cd;

ShapeOp::Solver solver_;
// main
int main( int argc, char **argv )
{

	//LOCAL Configure
	bool config_file = false;
	double hz = 30.0;
	double damping = 1.0;
	double masses = 0.1;
	double TriangleConstraint = 1000.0;

	//引数回収
	if (argc > 1){//ファイルから設定
		config_file = (bool)atoi(argv[1]);
		if (config_file) std::cout << "Configure by File" << std::endl;
	}

	if(argc > 6){
		for(int i=2; i<argc; i++){
			switch(i){
			case 2: substeps = (float)atoi(argv[i]);
			case 3: max_itr =  atoi(argv[i]);
			case 4: hz =  atoi(argv[i]);
			case 5: damping =  atoi(argv[i]);
			case 6: idf =  argv[i];
			case 7: idf_itr =  argv[i];
			case 8: output_position = (bool)atoi(argv[i]);
			case 9: slope = (bool)atoi(argv[i]);
			case 10: ikichi = (float)atof(argv[i]);
			case 11: num_frames = atoi(argv[i]);
			case 12: rate = atof(argv[i]);
			case 13: if_render = (bool)atoi(argv[i]);
			case 14: custom = (float)atof(argv[i]);
			case 15: if_dummy = (bool)atoi(argv[i]);
			case 16: custom_bool = (bool)atoi(argv[i]);
			case 17: custom_bool2 = (bool)atoi(argv[i]);
			case 18: neo_dummy =  atoi(argv[i]);
			case 19: if_dummy_3 = (bool)atoi(argv[i]);
			case 20: custom_bool3 = (bool)atoi(argv[i]);
			case 21: precompute_max_itr = atoi(argv[i]);
			case 22: k = (float)atof(argv[i]);
			case 23: coll_max_speed = (float) atof(argv[i]);
			}
		}
	}
	if_render = img_render || if_render;

	if(config_file){
		std::ifstream ifs("/home/octan/デスクトップ/PositionBasedDynamics/Demos/ClothDemo/ClothConfig");
		std::string str;

		if (ifs.fail()) {
			std::cerr << "Failed to open file." << std::endl;
			return -1;
		}
		while (getline(ifs, str)) {
			int num = str.find_first_of(":");
			std::string key = str.substr(0, num);
			std::string value = str.substr(num+1, str.size());
			if(key == "damping") damping = std::stod(value.c_str());
			if(key == "substeps") substeps = std::stod(value.c_str());
			if(key == "max_itr") max_itr = std::stod(value.c_str());
			if(key == "TriangleConstraint") TriangleConstraint  = std::stod(value.c_str());
			if(key == "hz") hz  = std::stod(value.c_str());
			if(key == "masses") masses  = std::stod(value.c_str());
		}
	}
	img_render = if_render;

	frametime = 1.0/hz;
	std::cout << "frametime: " << frametime << "substeps: " << substeps << " , max_itr: " << max_itr << std::endl;
	if(if_dummy){
		if(if_dummy_3){
			if(substeps != 1 && substeps%3 != 0){
				std::cout << "ステップ数が不正です" << std::endl;
				exit(1);
			}

		}
		else{
			if(substeps != 1 && substeps%2 != 0){
				std::cout << "ステップ数が不正です" << std::endl;
				exit(1);
			}

		}
	}

	//フォルダ初期化
	const char *dir = ("../output/"+idf).c_str();
    struct stat statBuf;

    if (stat(("../output/"+idf).c_str(), &statBuf) == 0)
        printf("ディレクトリ%sは存在します。\n", dir);
    else{
		mkdir(("../output/"+idf).c_str(),  0777);
        printf("ディレクトリ%sは存在しません。\n。", dir);
	}

	mkdir(("../output/"+idf+"/"+idf_itr).c_str(),  0777);
	mkdir(("../output/"+idf+"/"+idf_itr+"/images").c_str(),  0777);
	boost::filesystem::remove_all("../output/"+idf+"/"+idf_itr+"/image");

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/enrgy_data");
	energy = std::ofstream("../output/"+idf+"/"+idf_itr+"/enrgy_data", std::ios::app);

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/positions_data");
	o_pos = std::ofstream("../output/"+idf+"/"+idf_itr+"/positions_data", std::ios::app);

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/outputconfig");
	std::ofstream outputconfig("../output/"+idf+"/"+idf_itr+"/outputconfig", std::ios::app);

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/precompute_error");
	precompute_error = std::ofstream("../output/"+idf+"/"+idf_itr+"/precompute_error", std::ios::app);

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/cost_log");
	cost_log = std::ofstream("../output/"+idf+"/"+idf_itr+"/cost_log", std::ios::app);

	boost::filesystem::remove("../output/"+idf+"/"+idf_itr+"/dummy_violate");
	dummy_violate = std::ofstream("../output/"+idf+"/"+idf_itr+"/dummy_violate", std::ios::app);

	REPORT_MEMORY_LEAKS

	base = new DemoBase();
	base->init(argc, argv, "Bar demo");

	SimulationModel *model = new SimulationModel();
	model->init();
	Simulation::getCurrent()->setModel(model);

	buildModel();

	initParameters();

	Simulation::getCurrent()->setSimulationMethodChangedCallback([&]() { reset(); initParameters(); base->getSceneLoader()->readParameterObject(Simulation::getCurrent()->getTimeStep()); });

	// OpenGL
	MiniGL::setClientIdleFunc (30, timeStep);
	MiniGL::setKeyFunc(0, 'r', reset);
	MiniGL::setClientSceneFunc(render);
	MiniGL::setViewport (40.0f, 0.1f, 500.0f, Vector3r (5.0, 10.0, 30.0), Vector3r (5.0, 0.0, 0.0));

	// TwType enumType2 = TwDefineEnum("SimulationMethodType", NULL, 0);
	// TwAddVarCB(MiniGL::getTweakBar(), "SimulationMethod", enumType2, setSimulationMethod, getSimulationMethod, &simulationMethod,
	// 		" label='Simulation method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}' group=Simulation");
	// TwAddVarCB(MiniGL::getTweakBar(), "Stiffness", TW_TYPE_REAL, setStiffness, getStiffness, model, " label='Stiffness'  min=0.0 step=0.1 precision=4 group='Simulation' ");
	// TwAddVarCB(MiniGL::getTweakBar(), "PoissonRatio", TW_TYPE_REAL, setPoissonRatio, getPoissonRatio, model, " label='Poisson ratio XY'  min=0.0 step=0.1 precision=4 group='Simulation' ");
	// TwAddVarCB(MiniGL::getTweakBar(), "NormalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Strain based dynamics' ");
	// TwAddVarCB(MiniGL::getTweakBar(), "NormalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Strain based dynamics' ");

	ShapeOp::Matrix3X p = r_to_x(model->getParticles());
	solver_.setPoints(p);
	solver_.addForces(std::make_shared<ShapeOp::GravityForce>(ShapeOp::Vector3(0.0, -10.0, 0.0)));
	Add_EdgeConstraint(solver_ , model, 5000);
	Add_VolumeConstraint(solver_, model,5000);

	for (unsigned int i = 0; i < 1; i++)
	{
		for (unsigned int j = 0; j < height; j++)
		{
			for (unsigned int k = 0; k < depth; k++){
				AddFix(solver_, height*depth + j*depth + k, 10000);
			}
		}
	}
	solver_.initialize(true, masses, damping, frametime / substeps);

	PrecomputeNeighbor(neighbors, model);
	// std::cout << "neighbors" << std::endl;
	// std::cout << neighbors.size() << std::endl;
	// for(int i=0; i<neighbors.size(); i++){
	// 	std::cout << i << ": ";
	// 	for(int j =0; j<neighbors[i].size(); j++){
	// 		std::cout << neighbors[i][j] << ", ";
	// 	}
	// 	std::cout << std::endl;
	// }

	outputconfig << "/////////////////////////////////////////////////////////" << std::endl;
	outputconfig <<  "substeps: " <<  substeps  << std::endl;
	outputconfig <<  "max_itr: "  <<  max_itr  << std::endl;
	outputconfig <<  "hz: " <<  hz  << std::endl;
	outputconfig <<  "damping: " <<  damping  << std::endl;
	outputconfig <<  "idf: " <<  idf << std::endl;
	outputconfig <<  "idf_itr: " <<  idf_itr  << std::endl;
	outputconfig <<  "output_position: " <<  output_position  << std::endl;
	outputconfig <<  "slope: " <<  slope  << std::endl;
	outputconfig << "ikichi: "  << ikichi  << std::endl;
	outputconfig <<  "num_frames: " <<  num_frames  << std::endl;
	outputconfig <<  "rate: " << rate << std::endl;
	outputconfig <<  "if_render: " <<  if_render  << std::endl;
	outputconfig <<  "custom: " <<  custom << std::endl;
	outputconfig <<  "if_dummy: " <<  if_dummy  << std::endl;
	outputconfig <<  "custom_bool: " <<  custom_bool << std::endl;
	outputconfig <<  "custom_bool2: " <<  custom_bool2  << std::endl;
	outputconfig <<  "neo_dummy: " <<  neo_dummy << std::endl;
	outputconfig <<  "if_dummy_3: " <<  if_dummy_3  << std::endl;
	outputconfig <<  "custom_bool3: " <<  custom_bool3  << std::endl;

	// outputconfig <<  "stretching: " <<  stretching  << std::endl;
	outputconfig <<  "TriangleConstraint: " <<  TriangleConstraint  << std::endl;
	// outputconfig <<  "pin: " <<  pin  << std::endl;
	// outputconfig <<  "nRowss " <<  nRows  << std::endl;

	outputconfig << "/////////////////////////" << std::endl;

	std::cout << "/////////////////////////////////////////////////////////" << std::endl;

    std::cout << "damping: " << damping << std::endl;
    std::cout << "timestep: " << frametime / substeps << std::endl;
	std::cout << "substeps: " << substeps << std::endl;
	std::cout << "frametime: " << frametime << std::endl;
    std::cout << "masses: " << masses << std::endl;
	std::cout << "ikichi(): " << ikichi << std::endl;
	std::cout << "slope: " << slope << std::endl;
	std::cout << "dummy: " << if_dummy << std::endl;
	std::cout << "num_frames: " << num_frames << std::endl;
	std::cout << "/////////////////////////" << std::endl;

	glutMainLoop ();

	Utilities::Timing::printAverageTimes();
	Utilities::Timing::printTimeSums();

	delete Simulation::getCurrent();
	delete base;
	delete model;

	return 0;
}

void initParameters()
{
	TwRemoveAllVars(MiniGL::getTweakBar());
	TweakBarParameters::cleanup();

	MiniGL::initTweakBarParameters();

	TweakBarParameters::createParameterGUI();
	TweakBarParameters::createParameterObjectGUI(base);
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent());
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel());
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep());
}

void reset()
{
	Utilities::Timing::printAverageTimes();
	Utilities::Timing::reset();

	Simulation::getCurrent()->reset();
	base->getSelectedParticles().clear();

	Simulation::getCurrent()->getModel()->cleanup();
	buildModel();
}

void timeStep ()
{
	if(image_itr > num_frames) {
		std::cout << "EXIT by num_Frames" << std::endl;
		std::cout << std::endl;
		Utilities::Timing::printAverageTimes();
		Utilities::Timing::printTimeSums();
		Utilities::Timing::printAverageTimes_F(cost_log);
		Utilities::Timing::printTimeSums_F(cost_log);
		exit(1);
	}

	START_TIMING("Frame");

	const Real pauseAt = base->getValue<Real>(DemoBase::PAUSE_AT);
	if ((pauseAt > 0.0) && (pauseAt < TimeManager::getCurrent()->getTime()))
		base->setValue(DemoBase::PAUSE, true);

	if (base->getValue<bool>(DemoBase::PAUSE))
		return;

	// Simulation code
	SimulationModel *model = Simulation::getCurrent()->getModel();

	START_TIMING("PrecomputeError");
	if(custom_bool3){
		solver_.init_alternative();
		for (unsigned int i = 0; i < substeps; i++){
			solver_.alt_solve(precompute_max_itr, frametime/substeps);
		}
	}
	STOP_TIMING_AVG;

	int dum_num;
	if(if_dummy_3) dum_num = 3;
	else dum_num = 2;
	int num_violate = 0;

	START_TIMING("Simulation");
	if(if_dummy){
		if(substeps==1){
			if(if_dummy_3){
				if(image_itr%2 == 0){
					solver_.dummy_solve(frametime/substeps);
				}
				else if(image_itr%2 == 1){
					solver_.solve(max_itr, (frametime/substeps));
				}
				else{
					solver_.solve(max_itr, (frametime/substeps));
				}

			}
			else{
				if(image_itr%2 == 0){
					solver_.initDummy();
					solver_.dummy_solve(frametime/substeps);
				}
				else if(image_itr%2 == 1){
					solver_.solve(max_itr, (frametime/substeps));
					if(custom_bool) solver_.closeDummy(neighbors, k);
				}
			}
		}
		else{
			for(unsigned int i = 0; i < substeps; i++){
				START_TIMING("SimStep");
				if(i%dum_num == 0){
					solver_.initDummy();
					for(int j=0; j<neo_dummy; j++) solver_.dummy_solve( ((frametime/substeps) * 1 ) / neo_dummy);
				}
				else if(i%dum_num == 1){
					solver_.solve(max_itr, (frametime/substeps));
					if(custom_bool) num_violate = solver_.closeDummy(neighbors, k);
					dummy_violate << num_violate << ", ";
				}
				else if(i%dum_num == 2) solver_.solve(max_itr, (frametime/substeps) * 1);
				STOP_TIMING_AVG;
			}
		}
	}
	else{
	//通常営業
		for (unsigned int i = 0; i < substeps; i++)
		{
			START_TIMING("SimStep");
				solver_.solve(max_itr, frametime/substeps);	//ここが一番スタンダード
			STOP_TIMING_AVG;
		}
	//通常営業
	}

	STOP_TIMING_AVG;

	START_TIMING("PostProcessing");

	ParticleData &pd = model->getParticles();
	const ShapeOp::Matrix3X p = solver_.getPoints();
	if(custom_bool3){
		const ShapeOp::Matrix3X p2 = solver_.getPoints2();
		float error = 0;
		for (int i = 0; i < (int) pd.size(); i++){
			error += (p2.col(i) - p.col(i)).norm();
		}
		precompute_error << error/(float)p.cols() << ", ";
	}

	#pragma omp for schedule(static)
	for (int i = 0; i < (int) pd.size(); i++) updatePJ(pd.getPosition(i), p, i);
	#pragma omp for schedule(static)
	for (int i = 0; i < (int) pd.size(); i++) updatePJ(pd.getVelocity(i), solver_.getVelocities(), i);

	for (unsigned int i = 0; i < model->getTetModels().size(); i++)
	{
		model->getTetModels()[i]->updateMeshNormals(model->getParticles());
	}
		//OpenCV
	if(img_render&&if_render){
		MiniGL::display ();
		int w = 1024;    int h = 768;
		cv::Mat_<cv::Vec3b> image(h,w);
		glReadPixels(0, 0, w, h, GL_BGR, GL_UNSIGNED_BYTE, image.data);
		cv::flip(image, image, 0);
		cv::imwrite("../output/"+idf+"/"+idf_itr+"/images/"+std::to_string(image_itr)+".png", image);
	}
	energy << (float)solver_.compute_energy() << ", ";

	record_positions(o_pos, pd);
	image_itr++;

	STOP_TIMING_AVG;//PostProcessing
	STOP_TIMING_AVG;//Frame
}

void buildModel ()
{
	TimeManager::getCurrent ()->setTimeStepSize (static_cast<Real>(0.005));

	createMesh();
}

void render ()
{

	base->render();

	// float staticColor[4] = { 1.0f, 0.5f, 0.5f, 1 };
	// float staticColor2[4] = { 0.5f, 0.5f, 1.0f, 1 };
	// Matrix3X vel = s1->Solver().getVelocities();
	// Matrix3X pos = s1->Solver().getPoints();
	// for(int i=0; i<vel.cols(); i++){
	// 	if(!s1->Solver().getViolate()[i]) MiniGL::drawVector(pos.col(i), pos.col(i) - 0.1*vel.col(i), 1, staticColor);
	// 	else MiniGL::drawVector(pos.col(i), pos.col(i) - 0.1*vel.col(i), 1, staticColor2);
	// }

}

void createMesh()
{
	Vector3r points[width*height*depth];
	for (unsigned int i = 0; i < width; i++)
	{
		for (unsigned int j = 0; j < height; j++)
		{
			for (unsigned int k = 0; k < depth; k++)
			{
				points[i*height*depth + j*depth + k] = scale*Vector3r((Real)i, (Real)j, (Real)k);
			}
		}
	}

	vector<unsigned int> indices;
	for (unsigned int i = 0; i < width - 1; i++)
	{
		for (unsigned int j = 0; j < height - 1; j++)
		{
			for (unsigned int k = 0; k < depth - 1; k++)
			{
				// For each block, the 8 corners are numerated as:
				//     4*-----*7
				//     /|    /|
				//    / |   / |
				//  5*-----*6 |
				//   | 0*--|--*3
				//   | /   | /
				//   |/    |/
				//  1*-----*2
				unsigned int p0 = i*height*depth + j*depth + k;
				unsigned int p1 = p0 + 1;
				unsigned int p3 = (i + 1)*height*depth + j*depth + k;
				unsigned int p2 = p3 + 1;
				unsigned int p7 = (i + 1)*height*depth + (j + 1)*depth + k;
				unsigned int p6 = p7 + 1;
				unsigned int p4 = i*height*depth + (j + 1)*depth + k;
				unsigned int p5 = p4 + 1;

				// Ensure that neighboring tetras are sharing faces
				if ((i + j + k) % 2 == 1)
				{
					indices.push_back(p2); indices.push_back(p1); indices.push_back(p6); indices.push_back(p3);
					indices.push_back(p6); indices.push_back(p3); indices.push_back(p4); indices.push_back(p7);
					indices.push_back(p4); indices.push_back(p1); indices.push_back(p6); indices.push_back(p5);
					indices.push_back(p3); indices.push_back(p1); indices.push_back(p4); indices.push_back(p0);
					indices.push_back(p6); indices.push_back(p1); indices.push_back(p4); indices.push_back(p3);
				}
				else
				{
					indices.push_back(p0); indices.push_back(p2); indices.push_back(p5); indices.push_back(p1);
					indices.push_back(p7); indices.push_back(p2); indices.push_back(p0); indices.push_back(p3);
					indices.push_back(p5); indices.push_back(p2); indices.push_back(p7); indices.push_back(p6);
					indices.push_back(p7); indices.push_back(p0); indices.push_back(p5); indices.push_back(p4);
					indices.push_back(p0); indices.push_back(p2); indices.push_back(p7); indices.push_back(p5);
				}
			}
		}
	}
	SimulationModel *model = Simulation::getCurrent()->getModel();
	model->addTetModel(width*height*depth, (unsigned int)indices.size() / 4u, points, indices.data());

	ParticleData &pd = model->getParticles();
	for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++)
	{
		pd.setMass(i, 1.0);
	}
	for (unsigned int i = 0; i < 1; i++)
	{
		for (unsigned int j = 0; j < height; j++)
		{
			for (unsigned int k = 0; k < depth; k++){
				pd.setMass(i*height*depth + j*depth + k, 0.0);
			}

		}
	}

	// init constraints
	for (unsigned int cm = 0; cm < model->getTetModels().size(); cm++)
	{
		const unsigned int nTets = model->getTetModels()[cm]->getParticleMesh().numTets();
		const unsigned int *tets = model->getTetModels()[cm]->getParticleMesh().getTets().data();
		const IndexedTetMesh::VertexTets *vTets = model->getTetModels()[cm]->getParticleMesh().getVertexTets().data();
		if (simulationMethod == 1)
		{
			const unsigned int offset = model->getTetModels()[cm]->getIndexOffset();
			const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges();
			const IndexedTetMesh::Edge *edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data();//koko
			for (unsigned int i = 0; i < nEdges; i++)
			{
				const unsigned int v1 = edges[i].m_vert[0] + offset;
				const unsigned int v2 = edges[i].m_vert[1] + offset;

				model->addDistanceConstraint(v1, v2);
			}

			for (unsigned int i = 0; i < nTets; i++)
			{
				const unsigned int v1 = tets[4 * i];
				const unsigned int v2 = tets[4 * i + 1];
				const unsigned int v3 = tets[4 * i + 2];
				const unsigned int v4 = tets[4 * i + 3];

				model->addVolumeConstraint(v1, v2, v3, v4);
			}
		}
		else if (simulationMethod == 2)
		{
			TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh();
			for (unsigned int i = 0; i < nTets; i++)
			{
				const unsigned int v1 = tets[4 * i];
				const unsigned int v2 = tets[4 * i + 1];
				const unsigned int v3 = tets[4 * i + 2];
				const unsigned int v4 = tets[4 * i + 3];

				model->addFEMTetConstraint(v1, v2, v3, v4);
			}
		}
		else if (simulationMethod == 3)
		{
			TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh();
			for (unsigned int i = 0; i < nTets; i++)
			{
				const unsigned int v1 = tets[4 * i];
				const unsigned int v2 = tets[4 * i + 1];
				const unsigned int v3 = tets[4 * i + 2];
				const unsigned int v4 = tets[4 * i + 3];

				model->addStrainTetConstraint(v1, v2, v3, v4);
			}
		}
		else if (simulationMethod == 4)
		{
			TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh();
			for (unsigned int i = 0; i < nTets; i++)
			{
				const unsigned int v[4] = { tets[4 * i], tets[4 * i + 1], tets[4 * i + 2], tets[4 * i + 3] };
				// Important: Divide position correction by the number of clusters
				// which contain the vertex.
				const unsigned int nc[4] = { vTets[v[0]].m_numTets, vTets[v[1]].m_numTets, vTets[v[2]].m_numTets, vTets[v[3]].m_numTets };
				model->addShapeMatchingConstraint(4, v, nc);
			}
		}
		model->getTetModels()[cm]->updateMeshNormals(pd);
	}

	LOG_INFO << "Number of tets: " << indices.size() / 4;
	LOG_INFO << "Number of vertices: " << width*height*depth;

}

void TW_CALL setStiffness(const void *value, void *clientData)
{
	const Real val = *(const Real *)(value);
	((SimulationModel*) clientData)->setValue<Real>(SimulationModel::SOLID_STIFFNESS, val);
}

void TW_CALL getStiffness(void *value, void *clientData)
{
	*(Real *)(value) = ((SimulationModel*)clientData)->getValue<Real>(SimulationModel::SOLID_STIFFNESS);
}

void TW_CALL setPoissonRatio(const void *value, void *clientData)
{
	const Real val = *(const Real *)(value);
	((SimulationModel*)clientData)->setValue<Real>(SimulationModel::SOLID_POISSON_RATIO, val);
}

void TW_CALL getPoissonRatio(void *value, void *clientData)
{
	*(Real *)(value) = ((SimulationModel*)clientData)->getValue<Real>(SimulationModel::SOLID_POISSON_RATIO);
}

void TW_CALL setNormalizeStretch(const void *value, void *clientData)
{
	const bool val = *(const bool *)(value);
	((SimulationModel*)clientData)->setValue<Real>(SimulationModel::SOLID_NORMALIZE_STRETCH, val);
}

void TW_CALL getNormalizeStretch(void *value, void *clientData)
{
	*(bool *)(value) = ((SimulationModel*)clientData)->getValue<Real>(SimulationModel::SOLID_NORMALIZE_STRETCH);
}

void TW_CALL setNormalizeShear(const void *value, void *clientData)
{
	const bool val = *(const bool *)(value);
	((SimulationModel*)clientData)->setValue<Real>(SimulationModel::SOLID_NORMALIZE_SHEAR, val);
}

void TW_CALL getNormalizeShear(void *value, void *clientData)
{
	*(bool *)(value) = ((SimulationModel*)clientData)->getValue<Real>(SimulationModel::SOLID_NORMALIZE_SHEAR);
}

void TW_CALL setSimulationMethod(const void *value, void *clientData)
{
	const short val = *(const short *)(value);
	*((short*)clientData) = val;
	reset();
}

void TW_CALL getSimulationMethod(void *value, void *clientData)
{
	*(short *)(value) = *((short*)clientData);
}


#include <iostream>
#include "atkui/framework.h"
#include "atkmath/matrix3.h"
#include <Eigen/Dense>
using namespace glm;

class Unique : public atkui::Framework
{
public:
	Unique() : atkui::Framework(atkui::Orthographic)
	{
	}

	virtual void scene()
	{
		// drawing cursor
		setColor(vec3(1, 0, 0));
		drawSphere(target, 5);

		for (int i = 0; i < width(); i += 15)
		{
			for (int j = 0; j < height(); j += 15)
			{

				if (j == 0) {
					setColor(vec3(1, 0, 1));
				}
				else if (j == height() - 5) {
					setColor(vec3(0, 1, 1));
				}
				else {
					float t_y = j / height();
					t_y = glm::clamp(t_y, 0.0f, 1.0f);
					vec3 color = vec3(1, 0, 1) * (1 - t_y) + vec3(0, 1, 1) * t_y;
					setColor(color);

				}

				vec3 position = vec3(i, j, 0);
				float distance = sqrt(pow(position.x - target.x, 2) + pow(position.y - target.y, 2));

				if (distance <= 100) {

					vec3 v1 = target - position;
					theta = atan2(v1.y, v1.x);

					push();
					translate(position);
					rotate(theta, vec3(0, 0, 1));
					drawCube(vec3(0), vec3(15, 1, 0));
					pop();

				}
				else {
					drawCube(position, vec3(15, 1, 0));
				}
			}
		}

	}

	void mouseMotion(int pX, int pY, int dx, int dy)
	{
		if (click)
		{
			_mouseX = pX;
			_mouseY = height() - pY;
			target = vec3(_mouseX, _mouseY, 0);
		}
	}

	void mouseDown(int button, int mods)
	{

		click = true;

	}

	void mouseUp(int button, int mods)
	{
		click = false;
	}

private:
	int _mouseX;
	int _mouseY;
	float theta;
	bool click;
	vec3 target;
};

int main(int argc, char** argv)
{
	Unique viewer;
	viewer.run();
}


#ifndef slic3r_ExPolygon_hpp_
#define slic3r_ExPolygon_hpp_

#include "Polygon.hpp"
#include "Polyline.hpp"
#include <vector>

namespace Slic3r {

class ExPolygon;
typedef std::vector<ExPolygon> ExPolygons;

class ExPolygon
{
    public:
    Polygon contour;
    Polygons holes;
    operator Points() const;
    operator Polygons() const;
    void scale(double factor);
    void translate(double x, double y);
    void rotate(double angle, const Point &center);
    double area() const;
    bool is_valid() const;
    bool contains_line(const Line &line) const;
    bool contains_polyline(const Polyline &polyline) const;
    bool contains_point(const Point &point) const;
    Polygons simplify_p(double tolerance) const;
    ExPolygons simplify(double tolerance) const;
    void simplify(double tolerance, ExPolygons &expolygons) const;
    void medial_axis(double max_width, double min_width, Polylines* polylines) const;
    void get_trapezoids(Polygons* polygons) const;
    void get_trapezoids(Polygons* polygons, double angle) const;
    void get_trapezoids2(Polygons* polygons) const;
    void get_trapezoids2(Polygons* polygons, double angle) const;
    void triangulate(Polygons* polygons) const;
    void triangulate_pp(Polygons* polygons) const;
    void triangulate_p2t(Polygons* polygons) const;

    #ifdef SLIC3RXS
    void from_SV(SV* poly_sv);
    void from_SV_check(SV* poly_sv);
    SV* to_AV();
    SV* to_SV_pureperl() const;
    #endif
};

}

// start Boost
#include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon {
    template <>
        struct polygon_traits<ExPolygon> {
        typedef coord_t coordinate_type;
        typedef Points::const_iterator iterator_type;
        typedef Point point_type;

        // Get the begin iterator
        static inline iterator_type begin_points(const ExPolygon& t) {
            return t.contour.points.begin();
        }

        // Get the end iterator
        static inline iterator_type end_points(const ExPolygon& t) {
            return t.contour.points.end();
        }

        // Get the number of sides of the polygon
        static inline std::size_t size(const ExPolygon& t) {
            return t.contour.points.size();
        }

        // Get the winding direction of the polygon
        static inline winding_direction winding(const ExPolygon& t) {
            return unknown_winding;
        }
    };

    template <>
    struct polygon_mutable_traits<ExPolygon> {
        //expects stl style iterators
        template <typename iT>
        static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
            expolygon.contour.points.assign(input_begin, input_end);
            // skip last point since Boost will set last point = first point
            expolygon.contour.points.pop_back();
            return expolygon;
        }
    };

    template <>
    struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };

    template <>
    struct polygon_with_holes_traits<ExPolygon> {
        typedef Polygons::const_iterator iterator_holes_type;
        typedef Polygon hole_type;
        static inline iterator_holes_type begin_holes(const ExPolygon& t) {
            return t.holes.begin();
        }
        static inline iterator_holes_type end_holes(const ExPolygon& t) {
            return t.holes.end();
        }
        static inline unsigned int size_holes(const ExPolygon& t) {
            return t.holes.size();
        }
    };

    template <>
    struct polygon_with_holes_mutable_traits<ExPolygon> {
         template <typename iT>
         static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
              t.holes.assign(inputBegin, inputEnd);
              return t;
         }
    };

    //first we register CPolygonSet as a polygon set
    template <>
    struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };

    //next we map to the concept through traits
    template <>
    struct polygon_set_traits<ExPolygons> {
        typedef coord_t coordinate_type;
        typedef ExPolygons::const_iterator iterator_type;
        typedef ExPolygons operator_arg_type;

        static inline iterator_type begin(const ExPolygons& polygon_set) {
            return polygon_set.begin();
        }

        static inline iterator_type end(const ExPolygons& polygon_set) {
            return polygon_set.end();
        }

        //don't worry about these, just return false from them
        static inline bool clean(const ExPolygons& polygon_set) { return false; }
        static inline bool sorted(const ExPolygons& polygon_set) { return false; }
    };

    template <>
    struct polygon_set_mutable_traits<ExPolygons> {
        template <typename input_iterator_type>
        static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
            expolygons.assign(input_begin, input_end);
        }
    };
} }
// end Boost

#endif


#include <type_traits>

#include <range/v3/algorithm/for_each.hpp>
#include <range/v3/numeric/accumulate.hpp>
#include <range/v3/view/indices.hpp>
#include <range/v3/view/zip.hpp>

#include <boost/range/irange.hpp>

#include <elle/log.hh>
#include <elle/utils.hh>

namespace elle
{
  /*--------.
  | Helpers |
  `--------*/

  namespace _details
  {
    namespace table
    {
      template <typename T, std::size_t S>
      constexpr
      int
      size(std::array<T, S> const& t)
      {
        return std::apply([] (auto&& ... v) { return (v * ... * 1); }, t);
      }

      template <int dimension, typename I, typename T>
      auto
      _dimensions(I const& init, T& res)
      {
        if constexpr(dimension < std::tuple_size<T>::value)
        {
          std::get<dimension>(res) = init.size();
          _dimensions<dimension + 1>(*begin(init), res);
        }
      }

      template <int dimension, typename T>
      auto
      dimensions(T init)
      {
        std::array<int, dimension> res;
        _dimensions<0>(init, res);
        return res;
      }

      template <typename T>
      using aligned =
        typename std::aligned_storage<sizeof(T), alignof(T)>::type;

      template <typename T, typename Index, int index>
      struct Initializer
      {
        static constexpr auto dimension = std::tuple_size<Index>::value;

        using type = std::initializer_list<
          typename Initializer<T, Index, index - 1>::type>;
        static
        void
        distribute(Index const& dimensions, type const& init, std::vector<T>& t)
        {
          if (init.size() != unsigned(std::get<dimension - index>(dimensions)))
            elle::err("wrong row size in Table initializer");
          for (auto& e: init)
            Initializer<T, Index, index - 1>::distribute(dimensions, e, t);
        }
      };

      template <typename T, typename Index>
      struct Initializer<T, Index, 0>
      {
        using type = T;
        static
        void
        distribute(Index const& dimensions, T const& e, std::vector<T>& table)
        {
          table.emplace_back(e);
        }
      };
    }
  }

  /*-------------.
  | Construction |
  `-------------*/

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(Indexes ... dimensions, T value)
    : TableImpl(std::array{std::forward<Indexes>(dimensions)...},
                std::move(value))
  {}

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(Dimensions dimensions, T value)
    : TableImpl(std::move(dimensions), no_init())
  {
    this->_table.reserve(this->size());
    this->_table.resize(this->size(), std::move(value));
  }

  template <typename T, typename ... Indexes>
  TableImpl<T, std::true_type, Indexes...>::TableImpl(Indexes ... dimensions)
    : Super(dimensions..., T())
  {}

  template <typename T, typename ... Indexes>
  TableImpl<T, std::true_type, Indexes...>::TableImpl(Dimensions dimensions)
    : Super(dimensions, T())
  {}

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(
    elle::meta::fold1<dimension, std::initializer_list, T> init)
    : TableImpl(_details::table::dimensions<dimension>(init), no_init())
  {
    _details::table::Initializer<T, Index, dimension>::distribute(
      this->_dimensions, std::move(init), this->_table);
  }

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(TableImpl&& src)
    : _size(src._size)
    , _dimensions(src._dimensions)
    , _table(std::move(src._table))
  {}

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(TableImpl const& src)
    : TableImpl(src._dimensions, no_init())
  {
    for (auto const& e: src._table)
      this->_table.emplace_back(e);
  }

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::~TableImpl()
  noexcept(noexcept(std::declval<T>().~T()))
  {}

  template <typename T, typename DC, typename ... Indexes>
  struct TableImpl<T, DC, Indexes...>::no_init {};

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(Dimensions dimensions, no_init)
    : _size(_details::table::size(dimensions))
    , _dimensions(std::move(dimensions))
    , _table()
  {}

  /*-----------.
  | Dimensions |
  `-----------*/

  template <typename T, typename ... Indexes>
  void
  TableImpl<T, std::true_type, Indexes...>::dimensions(Dimensions dimensions)
  {
    this->dimensions(std::move(dimensions), T());
  }

  template <typename T, typename DC, typename ... Indexes>
  void
  TableImpl<T, DC, Indexes...>::dimensions(Dimensions dimensions, T value)
  {
    TableImpl table(dimensions, no_init());
    for (auto i: ranges::view::indices(0, table.size()))
    {
      auto index = table.index(i);
      if (this->contains(index))
        table._table.emplace_back(std::move(this->at(index)));
      else
        table._table.emplace_back(value);
    }
    this->~TableImpl();
    new (this) TableImpl(std::move(table));
  }

  /*-------.
  | Access |
  `-------*/

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::Access
  TableImpl<T, DC, Indexes...>::at(Indexes const& ... indexes)
  {
    return this->at({indexes...});
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::Access
  TableImpl<T, DC, Indexes...>::at(array_like<int, dimension> index)
  {
    if (!this->contains(index))
      elle::err("{} is out of bounds {}", index, this->_dimensions);
    // Don't just check index is in bound, because {2, 2} fits in {1, 100} but
    // is still out of bounds.
    auto const i = this->index(index);
    ELLE_ASSERT_LT(i, this->_size);
    return this->_table[i];
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::CAccess
  TableImpl<T, DC, Indexes...>::at(Indexes const& ... indexes) const
  {
    return unconst(*this).at(indexes...);
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::CAccess
  TableImpl<T, DC, Indexes...>::at(array_like<int, dimension> index) const
  {
    return unconst(*this).at(index);
  }

  template <typename T, typename DC, typename ... Indexes>
  bool
  TableImpl<T, DC, Indexes...>::contains(array_like<int, dimension> index) const
  {
    return this->_contains(
      index, std::make_index_sequence<sizeof...(Indexes)>());
  }

  template <typename T, typename DC, typename ... Indexes>
  template <std::size_t ... S>
  bool
  TableImpl<T, DC, Indexes...>::_contains(Index const& index,
                                              std::index_sequence<S...>) const
  {
    return ((std::get<S>(index) < std::get<S>(this->_dimensions) &&
             std::get<S>(index) >= 0) && ...);
  }

  /*---------.
  | Indexing |
  `---------*/

  template <typename T, typename DC, typename ... Indexes>
  int
  TableImpl<T, DC, Indexes...>::index(array_like<int, dimension> index) const
  {
    return this->_index(index, std::make_index_sequence<sizeof...(Indexes)>());
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::Index
  TableImpl<T, DC, Indexes...>::index(int i) const
  {
    return this->_index(i, std::make_index_sequence<sizeof...(Indexes)>());
  }

  template <typename T, typename DC, typename ... Indexes>
  template <std::size_t ... I>
  typename TableImpl<T, DC, Indexes...>::Index
  TableImpl<T, DC, Indexes...>::_index(
    int i, std::index_sequence<I...>) const
  {
    return {
      i %
      this->_index_offset(std::make_index_sequence<sizeof...(Indexes) - I>()) /
      this->_index_offset(std::make_index_sequence<sizeof...(Indexes) - I - 1>())
      ...
    };
  }

  template <typename T, typename DC, typename ... Indexes>
  template <std::size_t ... S>
  int
  TableImpl<T, DC, Indexes...>::_index(
    Index const& index, std::index_sequence<S...>) const
  {
    return sum(
      (std::get<S>(index) *
       this->_index_offset(
         std::make_index_sequence<sizeof...(Indexes) - S - 1>()))...);
  }

  template <typename T, typename DC, typename ... Indexes>
  template <std::size_t ... S>
  int
  TableImpl<T, DC, Indexes...>::_index_offset(std::index_sequence<S...>) const
  {
    return product(
      std::get<sizeof...(Indexes) - S - 1>(this->_dimensions)...);
  }

  /*-------------.
  | Modification |
  `-------------*/

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>&
  TableImpl<T, DC, Indexes...>::operator =(TableImpl const& table)
  {
    this->~TableImpl();
    new (this) TableImpl(table);
    return *this;
  }

  /*----------.
  | Iteration |
  `----------*/

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::iterator
  TableImpl<T, DC, Indexes...>::begin()
  {
    return iterator(*this, std::begin(this->_table));
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::iterator
  TableImpl<T, DC, Indexes...>::end()
  {
    return this->begin() + this->size();
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::const_iterator
  TableImpl<T, DC, Indexes...>::begin() const
  {
    return const_iterator(elle::unconst(*this), std::begin(this->_table));
  }

  template <typename T, typename DC, typename ... Indexes>
  typename TableImpl<T, DC, Indexes...>::const_iterator
  TableImpl<T, DC, Indexes...>::end() const
  {
    return this->begin() + this->size();
  }

  template <typename T, typename DC, typename ... Indexes>
  template <typename It>
  struct TableImpl<T, DC, Indexes...>::iterator_base
  {
  public:
    using iterator_category = std::input_iterator_tag;
    using value_type = std::pair<Index, T>;
    using difference_type = int;
    using pointer = std::pair<Index, typename It::pointer>;
    using reference = std::pair<Index, typename It::reference>;

    friend class TableImpl;

    bool
    operator !=(iterator_base rhs)
    {
      return this->_iterator != rhs._iterator;
    }

    iterator_base
    operator ++()
    {
      ++this->_iterator;
      return *this;
    }

    iterator_base
    operator +(int o)
    {
      return {this->_table, this->_iterator + o};
    }

    reference
    operator *()
    {
      auto offset =
        this->_iterator - std::begin(this->_table._table);
      return {
        this->_index(offset, std::make_index_sequence<sizeof...(Indexes)>()),
        *this->_iterator,
      };
    }

  private:
    iterator_base(TableImpl& table, It iterator)
      : _table(table)
      , _iterator(iterator)
    {}

    template <std::size_t ... I>
    TableImpl::Index
    _index(int offset, std::index_sequence<I...>)
    {
      auto& t = this->_table;
      return {
        offset %
        t._index_offset(std::make_index_sequence<sizeof...(Indexes) - I>()) /
        t._index_offset(std::make_index_sequence<sizeof...(Indexes) - I - 1>())
        ...
      };
    }

    ELLE_ATTRIBUTE((TableImpl&), table);
    ELLE_ATTRIBUTE(It, iterator);
  };

  template <typename T, typename DC, typename ... Indexes>
  elle::detail::range<typename std::vector<T>::iterator>
  TableImpl<T, DC, Indexes...>::elements()
  {
    return elle::as_range(std::begin(this->_table), std::end(this->_table));
  }

  template <typename T, typename DC, typename ... Indexes>
  elle::detail::range<typename std::vector<T>::const_iterator>
  TableImpl<T, DC, Indexes...>::elements() const
  {
    return elle::as_range(std::begin(this->_table), std::end(this->_table));
  }

  /*-----------.
  | Comparison |
  `-----------*/

  template <typename T, typename DC, typename ... Indexes>
  bool
  TableImpl<T, DC, Indexes...>::operator ==(TableImpl const& rhs) const
  {
    if (this->dimensions() != rhs.dimensions())
      return false;
    return
      ranges::accumulate(
        ranges::view::zip_with([] (T const& a, T const& b) { return a == b; },
                               this->elements(), rhs.elements()),
        true, [] (bool a, bool b) { return a && b; });
  }

  template <typename T, typename DC, typename ... Indexes>
  bool
  TableImpl<T, DC, Indexes...>::operator !=(TableImpl const& table) const
  {
    return !(*this == table);
  }

  /*------.
  | Print |
  `------*/

  template <typename T, typename DC, typename ... Indexes>
  void
  TableImpl<T, DC, Indexes...>::print(std::ostream& s) const
  {
    auto it = std::begin(this->_table);
    this->_print<0>(s, it);
  }

  template <typename T, typename DC, typename ... Indexes>
  template<int I>
  void
  TableImpl<T, DC, Indexes...>::_print(
    std::ostream& s,
    typename std::vector<T>::const_iterator& it) const
  {
    if constexpr(I == sizeof...(Indexes))
      elle::print(s, "{}", *it++);
    else
    {
      elle::print(s, "[");
      auto size = std::get<I>(this->_dimensions);
      ranges::for_each(
        ranges::view::indices(size),
        [&] (int i)
        {
          if (i > 0)
            elle::print(s, ", ");
          this->_print<I + 1>(s, it);
        });
      elle::print(s, "]");
    }
  }

  /*--------------.
  | Serialization |
  `--------------*/

  template <typename T, typename DC, typename ... Indexes>
  TableImpl<T, DC, Indexes...>::TableImpl(serialization::SerializerIn& s)
    : _dimensions()
    , _table()
  {
    this->serialize(s);
  }

  template <typename T, typename DC, typename ... Indexes>
  void
  TableImpl<T, DC, Indexes...>::serialize(serialization::Serializer& s)
  {
    s.serialize("dimensions", this->_dimensions);
    this->_size = _details::table::size(this->_dimensions);
    s.serialize("elements", this->_table);
    if (signed(this->_table.size()) != this->size())
      elle::err<serialization::Error>(
        "wrong number of elements: {} instead of {}",
        this->_table.size(), this->size());
  }
}


#ifndef ABSTRACT_TRANSFORMER_HPP
#define ABSTRACT_TRANSFORMER_HPP

/*
   Definition of abstract transfer functions.
 */

#include <boost/optional.hpp>
#include "boost/range/algorithm/set_algorithm.hpp"

#include <crab/common/debug.hpp>
#include <crab/common/stats.hpp>
#include <crab/cfg/cfg.hpp>
#include <crab/domains/domain_traits.hpp>
#include <crab/domains/linear_constraints.hpp>

namespace crab {

  namespace analyzer {

  using namespace cfg;
  using namespace std;

  //! API abstract transformer
  template<typename VariableName>
  class abs_transformer_api: public statement_visitor <VariableName>
  {
   public:

    typedef VariableName varname_t;
    typedef variable < z_number, VariableName > z_var_t;
    typedef linear_expression< z_number, VariableName > z_lin_exp_t;
    typedef linear_constraint< z_number, VariableName > z_lin_cst_t;
    typedef linear_constraint_system< z_number, VariableName > z_lin_cst_sys_t;

    typedef binary_op <z_number,VariableName>        z_bin_op_t;
    typedef assignment <z_number,VariableName>       z_assign_t;
    typedef assume_stmt <z_number,VariableName>      z_assume_t;
    typedef havoc_stmt<VariableName>                 havoc_t;
    typedef unreachable_stmt<VariableName>           unreach_t;
    typedef select_stmt <z_number,VariableName>      z_select_t;
    typedef assert_stmt <z_number,VariableName>      z_assert_t;
    typedef callsite_stmt<VariableName>              callsite_t;
    typedef return_stmt<VariableName>                return_t;
    typedef array_init_stmt<VariableName>            z_arr_init_t;
    typedef assume_array_stmt<z_number,VariableName> z_assume_arr_t;
    typedef array_store_stmt<z_number,VariableName>  z_arr_store_t;
    typedef array_load_stmt<z_number,VariableName>   z_arr_load_t;
    typedef ptr_store_stmt<VariableName>             ptr_store_t;
    typedef ptr_load_stmt<VariableName>              ptr_load_t;
    typedef ptr_assign_stmt<z_number,VariableName>   z_ptr_assign_t;
    typedef ptr_object_stmt<VariableName>            ptr_object_t;
    typedef ptr_function_stmt<VariableName>          ptr_function_t;
    typedef ptr_null_stmt<VariableName>              ptr_null_t;
    typedef ptr_assume_stmt<VariableName>            ptr_assume_t;
    typedef ptr_assert_stmt<VariableName>            ptr_assert_t;

   protected:

    virtual void exec (z_bin_op_t&)  { }
    virtual void exec (z_assign_t&) { }
    virtual void exec (z_assume_t&) { }
    virtual void exec (havoc_t&) { }
    virtual void exec (unreach_t&) { }
    virtual void exec (z_select_t&) { }
    virtual void exec (z_assert_t&) { }
    virtual void exec (callsite_t&) { }
    virtual void exec (return_t&) { }
    virtual void exec (z_arr_init_t&) { }
    virtual void exec (z_assume_arr_t&) { }
    virtual void exec (z_arr_store_t&) { }
    virtual void exec (z_arr_load_t&) { }
    virtual void exec (ptr_store_t&) { }
    virtual void exec (ptr_load_t&) { }
    virtual void exec (z_ptr_assign_t&) { }
    virtual void exec (ptr_object_t&) { }
    virtual void exec (ptr_function_t&) { }
    virtual void exec (ptr_null_t&) { }
    virtual void exec (ptr_assume_t&) { }
    virtual void exec (ptr_assert_t&) { }

   public: /* visitor api */

    void visit (z_bin_op_t &s) { exec (s); }
    void visit (z_assign_t &s) { exec (s); }
    void visit (z_assume_t &s) { exec (s); }
    void visit (havoc_t &s) { exec (s); }
    void visit (unreach_t &s) { exec (s); }
    void visit (z_select_t &s) { exec (s); }
    void visit (z_assert_t &s) { exec (s); }
    void visit (callsite_t &s) { exec (s); }
    void visit (return_t &s) { exec (s); }
    void visit (z_arr_init_t &s) { exec (s); }
    void visit (z_assume_arr_t &s) { exec (s); }
    void visit (z_arr_store_t &s) { exec (s); }
    void visit (z_arr_load_t &s) { exec (s); }
    void visit (ptr_store_t &s) { exec (s); }
    void visit (ptr_load_t &s) { exec (s); }
    void visit (z_ptr_assign_t &s) { exec (s); }
    void visit (ptr_object_t &s) { exec (s); }
    void visit (ptr_function_t &s) { exec (s); }
    void visit (ptr_null_t &s) { exec (s); }
    void visit (ptr_assume_t &s) { exec (s); }
    void visit (ptr_assert_t &s) { exec (s); }

  };

  //! Abstract transformer specialized for numerical abstract domains
  //! with arrays and pointers.
  // XXX: num_abs_transformer should be renamed to
  //      abs_transformer_impl or something like that.
  template<typename NumAbsDomain,
           typename SumTable /*unused*/, typename CallCtxTable /*unused*/>
  class num_abs_transformer:
        public abs_transformer_api <typename CallCtxTable::abs_domain_t::varname_t>
  {

   public:

    typedef typename SumTable::abs_domain_t summ_abs_domain_t;
    typedef typename CallCtxTable::abs_domain_t call_abs_domain_t;
    typedef NumAbsDomain abs_dom_t;

   public:

    typedef abs_transformer_api <typename CallCtxTable::abs_domain_t::varname_t> abs_transform_t;
    using typename abs_transform_t::varname_t;
    using typename abs_transform_t::z_var_t;
    using typename abs_transform_t::z_lin_exp_t;
    using typename abs_transform_t::z_lin_cst_t;
    using typename abs_transform_t::z_lin_cst_sys_t;
    using typename abs_transform_t::z_bin_op_t;
    using typename abs_transform_t::z_assign_t;
    using typename abs_transform_t::z_assume_t;
    using typename abs_transform_t::z_select_t;
    using typename abs_transform_t::z_assert_t;
    using typename abs_transform_t::havoc_t;
    using typename abs_transform_t::unreach_t;
    using typename abs_transform_t::callsite_t;
    using typename abs_transform_t::z_arr_init_t;
    using typename abs_transform_t::z_assume_arr_t;
    using typename abs_transform_t::z_arr_load_t;
    using typename abs_transform_t::z_arr_store_t;
    using typename abs_transform_t::ptr_load_t;
    using typename abs_transform_t::ptr_store_t;
    using typename abs_transform_t::ptr_assign_t;
    using typename abs_transform_t::ptr_object_t;
    using typename abs_transform_t::ptr_function_t;
    using typename abs_transform_t::ptr_null_t;
    using typename abs_transform_t::ptr_assume_t;
    using typename abs_transform_t::ptr_assert_t;

   protected:

    abs_dom_t& m_inv;

    template<typename T>
    void apply (abs_dom_t& inv, binary_operation_t op,
                varname_t x, varname_t y, T z) {

      auto op1 = conv_op<ikos::operation_t> (op);
      auto op2 = conv_op<ikos::div_operation_t> (op);
      auto op3 = conv_op<ikos::bitwise_operation_t> (op);

      if (op1)
        inv.apply (*op1, x, y, z);
      else if (op2)
        inv.apply (*op2, x, y, z);
      else if (op3)
        inv.apply (*op3, x, y, z);
      else
        CRAB_ERROR("unsupported binary operator", op);
    }

   public:

    num_abs_transformer (abs_dom_t& inv):
        m_inv (inv) { }

    num_abs_transformer (abs_dom_t& inv, SumTable*, CallCtxTable*):
        m_inv (inv) { }

    abs_dom_t& inv () { return m_inv; }

    void exec (z_bin_op_t& stmt)  {

      z_lin_exp_t op1 = stmt.left ();
      z_lin_exp_t op2 = stmt.right ();

      if (op1.get_variable () && op2.get_variable ())
      {
        apply (m_inv, stmt.op (),
               stmt.lhs ().name(),
               (*op1.get_variable ()).name(),
               (*op2.get_variable ()).name());
      }
      else
      {
        assert ( op1.get_variable () && op2.is_constant ());
        apply (m_inv, stmt.op (),
               stmt.lhs ().name (),
               (*op1.get_variable ()).name(),
               op2.constant ());
      }
    }

    void exec (z_select_t& stmt) {

      abs_dom_t inv1 (m_inv);
      abs_dom_t inv2 (m_inv);
      inv1 += stmt.cond ();
      inv2 += stmt.cond ().negate ();
      if (inv2.is_bottom()) {
        inv1.assign (stmt.lhs().name (),stmt.left());
        m_inv = inv1;
      }
      else if (inv1.is_bottom ()) {
        inv2.assign (stmt.lhs().name (),stmt.right());
        m_inv = inv2;
      }
      else {
        inv1.assign (stmt.lhs().name (),stmt.left());
        inv2.assign (stmt.lhs().name (),stmt.right());
        m_inv = inv1 | inv2;
      }
    }

    void exec (z_assign_t& stmt) {
      m_inv.assign (stmt.lhs().name (), z_lin_exp_t (stmt.rhs()));
    }

    void exec (z_assume_t& stmt) {
      m_inv += stmt.constraint();
    }

    void exec (havoc_t& stmt)  {
      m_inv -= stmt.variable();
    }

    void exec (unreach_t& stmt) {
      m_inv = abs_dom_t::bottom ();
    }

    // --- default implementation is intra-procedural
    // TODO/FIXME: havoc also all the pointer actual parameters.
    // XXX: no need to havoc array parameters because compilation
    // ensures input/output arrays so in this case output arrays will
    // be already top.
    void exec (callsite_t &cs) {
      auto lhs_opt = cs.get_lhs_name ();
      if (lhs_opt) { // havoc
        m_inv -= *lhs_opt;
      }
    }

    void exec (z_assert_t& stmt) {
      abs_dom_t cst;
      cst += stmt.constraint();
      abs_dom_t meet = cst & m_inv;
      if (meet.is_bottom ()) {
        m_inv = abs_dom_t::bottom (); // assertion does not definitely hold.
        return;
      }
      m_inv += stmt.constraint ();
    }

    // arrays

    void exec (z_arr_init_t &stmt) {
      m_inv.array_init (stmt.variable (), stmt.values ());
    }

    void exec (z_assume_arr_t &stmt) {
      m_inv.array_assume (stmt.variable (),
                          stmt.val().lb().number(), stmt.val().ub().number());
    }

    void exec (z_arr_store_t &stmt) {
      if (stmt.index ().get_variable ())
      {
        auto arr = stmt.array ().name ();
        auto idx = *(stmt.index ().get_variable ());
        m_inv.array_store (arr, idx.name(), stmt.value (), stmt.elem_size(),
                           stmt.is_singleton ());
      }
    }

    void exec (z_arr_load_t  &stmt) {
      if (stmt.index ().get_variable ())
      {
        auto idx = *(stmt.index ().get_variable ());
        m_inv.array_load (stmt.lhs ().name (), stmt.array ().name (), idx.name (),
                          stmt.elem_size());
      }
    }

    // pointers

    void exec (ptr_null_t & stmt) {
      m_inv.pointer_mk_null (stmt.lhs ());
    }

    void exec (ptr_object_t & stmt) {
      m_inv.pointer_mk_obj (stmt.lhs (), stmt.rhs());
    }

    void exec (ptr_function_t & stmt) {
      m_inv.pointer_function (stmt.lhs (), stmt.rhs ());
    }

    void exec (ptr_assign_t & stmt) {
      m_inv.pointer_assign (stmt.lhs (), stmt.rhs (), stmt.offset ());
    }

    void exec (ptr_load_t & stmt) {
      m_inv.pointer_load (stmt.lhs (), stmt.rhs ());
    }

    void exec (ptr_store_t & stmt) {
      m_inv.pointer_store (stmt.lhs (), stmt.rhs ());
    }

    void exec (ptr_assume_t& stmt) {
      m_inv.pointer_assume (stmt.constraint ());
    }

    void exec (ptr_assert_t& stmt) {
      m_inv.pointer_assert (stmt.constraint ());
    }

  };

  //! Transformer specialized for computing numerical summaries
  // TODO/FIXME: pointer operands are ignored
  template<typename SumTable, typename CallCtxTable /*unused*/>
  class bu_summ_num_abs_transformer:
        public num_abs_transformer <typename SumTable::abs_domain_t,
                                   SumTable, CallCtxTable> {

   public:

    typedef typename SumTable::abs_domain_t summ_abs_domain_t;
    typedef typename CallCtxTable::abs_domain_t call_abs_domain_t;
    typedef summ_abs_domain_t abs_dom_t;

   public:

    typedef num_abs_transformer <abs_dom_t, SumTable, CallCtxTable> num_abs_transform_t;
    typedef typename num_abs_transform_t::abs_transform_t abs_transform_t;

    using typename abs_transform_t::varname_t;
    using typename abs_transform_t::z_var_t;
    using typename abs_transform_t::z_lin_exp_t;
    using typename abs_transform_t::z_bin_op_t;
    using typename abs_transform_t::z_assign_t;
    using typename abs_transform_t::z_assume_t;
    using typename abs_transform_t::z_select_t;
    using typename abs_transform_t::havoc_t;
    using typename abs_transform_t::unreach_t;
    using typename abs_transform_t::z_assert_t;
    using typename abs_transform_t::z_arr_init_t;
    using typename abs_transform_t::z_assume_arr_t;
    using typename abs_transform_t::z_arr_load_t;
    using typename abs_transform_t::z_arr_store_t;
    using typename abs_transform_t::callsite_t;

   private:

    SumTable* m_sum_tbl;

   public:

    static void reuse_summary (abs_dom_t& caller,
                               const callsite_t& cs,
                               const typename SumTable::Summary& summ) {

      //crab::ScopedCrabStats st("Inter.ReuseSummary");

      // --- meet caller's inv with summ
      auto sum_inv = summ.get_sum ();
      caller = caller & sum_inv;
      CRAB_LOG("inter",
               crab::outs() << "--- After meet: " <<  caller << "\n");
      // --- matching formal and actual parameters
      auto pars = summ.get_params ();
      unsigned i=0;
      std::set<varname_t> actuals, formals;
      for (auto p : pars) {
        auto a = cs.get_arg_name (i);
        if (!(a == p)) {
          caller += (z_var_t (a) == z_var_t (p));
        }
        ++i;
        actuals.insert (a); formals.insert (p);
      }
      // --- matching callsite's lhs and callee's return value
      auto lhs_opt = cs.get_lhs_name ();
      auto ret_opt = summ.get_ret_val ();
      if (lhs_opt && ret_opt) {
        caller.assign(*lhs_opt, z_lin_exp_t (z_var_t (*ret_opt)));
        actuals.insert (*lhs_opt); formals.insert (*ret_opt);
      }
      CRAB_LOG ("inter",
                crab::outs() << "--- After matching formals and actuals: "
                <<  caller << "\n");
      // --- remove from caller only formal parameters so we can keep
      //     as much context from the caller as possible
      std::set<varname_t> s;
      boost::set_difference (formals, actuals, std::inserter(s, s.end ()));
      domains::domain_traits<abs_dom_t>::forget (caller, s.begin (), s.end ());
    }

   public:

    bu_summ_num_abs_transformer(abs_dom_t& inv, SumTable* sum_tbl)
        : num_abs_transform_t(inv),
          m_sum_tbl (sum_tbl) { }

    bu_summ_num_abs_transformer(abs_dom_t& inv, SumTable* sum_tbl, CallCtxTable*)
        : num_abs_transform_t(inv),
          m_sum_tbl (sum_tbl) { }

    abs_dom_t& inv () { return this->m_inv; }

    void exec (z_bin_op_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_select_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assign_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assume_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (havoc_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (unreach_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assert_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_init_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assume_arr_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_store_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_load_t  &stmt) { num_abs_transform_t::exec (stmt); }

    // If there is a summary for the callee we use it to compute the
    // caller's summary
    void exec (callsite_t &cs) {
      if (!m_sum_tbl) {
        CRAB_WARN ("The summary table is empty: ignored analysis of callsite");
        return;
      }

      if (m_sum_tbl->hasSummary (cs)) {
        auto &sum = m_sum_tbl->get (cs);
        auto pars = sum.get_params ();
        if (pars.size () == cs.get_num_args ()) {
          reuse_summary (this->m_inv, cs, sum);
          return;
        }
        else
          CRAB_WARN ("Ignored callsite due to mismatch of caller and callee's parameters");
      }
      else
        CRAB_LOG("inter",
                 crab::outs() << "Summary not found for " << cs << "\n");

      auto lhs_opt = cs.get_lhs_name ();
      // TODO/FIXME: havoc also all the pointer actual parameters.
      if (lhs_opt) { // havoc
        this->m_inv -= *lhs_opt;
      }
    }
  };

  /// Conversion between domains
  template<typename Domain>
  inline void convert_domains (Domain from, Domain& to) {
    to = from;
  }

  template<typename Domain1, typename Domain2>
  inline void convert_domains (Domain1 from, Domain2& to) {
    CRAB_WARN("Converting from " + Domain1::getDomainName () + " to " +
              Domain2::getDomainName () + " might lose precision " +
              "if " + Domain1::getDomainName () + " is an abstraction of " +
              Domain2::getDomainName ());
    for (auto cst : from.to_linear_constraint_system ())
    { to += cst; }
  }

  // Transformer specialized for performing top-down forward
  // traversal while reusing numerical summaries at the callsites
  // TODO/FIXME: pointer operands are ignored
  template<typename SumTable, typename CallCtxTable>
  class td_summ_num_abs_transformer:
        public num_abs_transformer<typename CallCtxTable::abs_domain_t,
                                  SumTable, CallCtxTable> {

   public:

    typedef typename SumTable::abs_domain_t summ_abs_domain_t;
    typedef typename CallCtxTable::abs_domain_t call_abs_domain_t;
    typedef call_abs_domain_t abs_dom_t;

   public:

    typedef num_abs_transformer<abs_dom_t, SumTable,CallCtxTable> num_abs_transform_t;
    typedef typename num_abs_transform_t::abs_transform_t abs_transform_t;

    using typename abs_transform_t::varname_t;
    using typename abs_transform_t::z_var_t;
    using typename abs_transform_t::z_lin_exp_t;
    using typename abs_transform_t::z_lin_cst_t;
    using typename abs_transform_t::z_lin_cst_sys_t;
    using typename abs_transform_t::z_bin_op_t;
    using typename abs_transform_t::z_assign_t;
    using typename abs_transform_t::z_assume_t;
    using typename abs_transform_t::z_select_t;
    using typename abs_transform_t::havoc_t;
    using typename abs_transform_t::unreach_t;
    using typename abs_transform_t::z_assert_t;
    using typename abs_transform_t::z_arr_init_t;
    using typename abs_transform_t::z_assume_arr_t;
    using typename abs_transform_t::z_arr_load_t;
    using typename abs_transform_t::z_arr_store_t;
    using typename abs_transform_t::callsite_t;

   private:

    typedef bu_summ_num_abs_transformer<SumTable,CallCtxTable> bu_abs_transformer_t;

    SumTable* m_sum_tbl;
    CallCtxTable* m_call_tbl;

   public:

    td_summ_num_abs_transformer (abs_dom_t& inv,
                                 SumTable* sum_tbl, CallCtxTable* call_tbl)
        : num_abs_transform_t(inv),
          m_sum_tbl (sum_tbl),
          m_call_tbl (call_tbl) { }

    abs_dom_t& inv () { return this->m_inv; }

    void exec (z_bin_op_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_select_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assign_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assume_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (havoc_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (unreach_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assert_t& stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_init_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_assume_arr_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_store_t &stmt) { num_abs_transform_t::exec (stmt); }
    void exec (z_arr_load_t  &stmt) { num_abs_transform_t::exec (stmt); }

    void exec (callsite_t &cs) {

      if (!m_sum_tbl) {
        CRAB_WARN ("The summary table is empty");
      }
      else if (m_sum_tbl->hasSummary (cs)) {

        auto &sum = m_sum_tbl->get (cs);
        auto pars = sum.get_params ();

        if (pars.size () == cs.get_num_args ()) {

          ///////
          /// Generate the callee context and store it.
          ///////
          abs_dom_t callee_ctx_inv (this->m_inv);
          // --- matching formal and actual parameters
          unsigned i=0;
          for (auto p : pars) {
            auto a = cs.get_arg_name (i);
            if (!(a == p)) {
              callee_ctx_inv += (z_var_t (p) == z_var_t (a));
            }
            ++i;
          }
          // --- project only onto formal parameters
          domains::domain_traits<abs_dom_t>::project (callee_ctx_inv,
                                                      pars.begin (),
                                                      pars.end ());
          // --- store the callee context
          CRAB_LOG ("inter",
                    crab::outs() << "--- Callee context stored: "
                                 << callee_ctx_inv << "\n");
          m_call_tbl->insert (cs, callee_ctx_inv);

          /////
          // Generate the continuation at the caller
          /////

          // --- convert this->m_inv to the language of summ_abs_dom_t (sum)
          summ_abs_domain_t caller_ctx_inv;
          convert_domains(this->m_inv, caller_ctx_inv);

          CRAB_LOG ("inter",
                    crab::outs() << "--- Caller context: " <<  caller_ctx_inv << "\n");

          // --- reuse summary to do the continuation
          bu_abs_transformer_t::reuse_summary (caller_ctx_inv, cs, sum);
          CRAB_LOG ("inter",
                    crab::outs() << "--- Caller context after plugin summary: "
                                 << caller_ctx_inv << "\n");

          // --- convert back inv to the language of abs_dom_t
          convert_domains(caller_ctx_inv, this->m_inv);
          CRAB_LOG ("inter",
                    crab::outs() << "--- Caller continuation after "
                                 <<  cs << "=" <<  this->m_inv << "\n");
          return;
        }
        else
          CRAB_WARN ("mismatch of parameters between caller and callee");
      }
      else {
        // no summary found: do nothing
      }

      // We could not reuse a summary so we just havoc lhs of the call
      // site. TODO/FIXME: havoc also all the pointer actual
      // parameters.
      auto lhs_opt = cs.get_lhs_name ();
      if (lhs_opt) {
        this->m_inv -= *lhs_opt;
      }
    }
  };

  } // end namespace
} // end namespace
#endif


#pragma once

#include <src/tacklelib_private.hpp>

#include <tacklelib/utility/platform.hpp>

#include <boost/functional/hash.hpp>

namespace utility
{
    template <class T>
    FORCE_INLINE void hash_combine(std::size_t & seed, const T & v)
    {
        boost::hash<T> hasher;
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

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

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

 /*!

    @ingroup group-boolean
    Function object implementing if_dec capabilities

    Decrements a value by @ref One if a predicate is true.

    This is a convenient alias of @ref seldec
  **/
  const boost::dispatch::functor<tag::if_dec_> if_dec = {};
} }
#endif

#include <boost/simd/function/scalar/seldec.hpp>
#include <boost/simd/function/simd/if_dec.hpp>

#endif


#ifndef IEOMPP_TYPES_EIGEN_HPP_
#define IEOMPP_TYPES_EIGEN_HPP_

#include "ieompp/string.hpp"
#include "ieompp/types/eigen/description.hpp"
#include "ieompp/types/eigen/dot_product.hpp"
#include "ieompp/types/eigen/init.hpp"
#include "ieompp/types/eigen/traits.hpp"

#include <Eigen/src/Core/util/Macros.h>

namespace ieompp
{
    auto eigen_version()
    {
        static const auto str =
            compose(EIGEN_WORLD_VERSION, '.', EIGEN_MAJOR_VERSION, '.', EIGEN_MINOR_VERSION);
        return str;
    }
}

#endif