#define BOOST_TEST_MODULE MasterMind
#include <boost/test/included/unit_test.hpp>

#include <vector>

int return_number_good_position_in_sequence(const std::vector<int> & code_sequence,
                                            const std::vector<int> & proposed_sequence)
{
    if (code_sequence == proposed_sequence)
        {
            return 1;
        }
    return 0;
}

BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)

BOOST_AUTO_TEST_CASE(code_sequence_1_proposed_sequence_1_return_1)
{
    std::vector<int> code_sequence(1,1);
    std::vector<int> proposed_sequence(1,1);
    BOOST_CHECK_EQUAL(1, return_number_good_position_in_sequence(code_sequence, proposed_sequence));
}

BOOST_AUTO_TEST_CASE(code_sequence_1_proposed_sequence_2_return_0)
{
    std::vector<int> code_sequence(1,1);
    std::vector<int> proposed_sequence(1,2);
    BOOST_CHECK_EQUAL(0, return_number_good_position_in_sequence(code_sequence, proposed_sequence));
}

BOOST_AUTO_TEST_SUITE_END()
#undef BOOST_TEST_MODULE


#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/properties.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

// the predefined property types are listed here:
// http://www.boost.org/doc/libs/1_64_0/libs/graph/doc/using_adjacency_list.html#sec:adjacency-list-properties

struct VertexData
{
  std::string first_name;
  int num;

  // this is used in predecessor map. The type shouldn't really be
  // hardcoded to int. Instead, it should be set to vertex_descriptor
  // (perhaps via template). I use it directly here, to keep the examples
  // below a little simpler.
  int pred;

  // this is used in distance map
  int dist;
};

struct EdgeData
{
  std::string edge_name;
  double dist;
};

void example1()
{

  typedef boost::adjacency_list<boost::vecS, boost::vecS,
                                boost::directedS,
                                boost::no_property,
                                boost::property<boost::edge_weight_t, int> > MyGraphType;

  // when instantiating without args, don't make the mistake of writing
  // MyGraphType G();
  // you'll get cryptic/misleading errors if you do.
  MyGraphType G;

  int v0 = add_vertex(G);
  int v1 = add_vertex(G);
  int v2 = add_vertex(G);

  // don't forget the .first, .second tells you if this is a new edge was added
  // (alternately, the edge already existed)
  auto e1 = add_edge(v0, v1, 10, G).first;
  auto e2 = add_edge(v1, v2, 20, G).first;

  // http://www.boost.org/doc/libs/1_64_0/libs/property_map/doc/iterator_property_map.html

  typedef typename boost::graph_traits<MyGraphType>::vertex_descriptor vertex_descriptor;

  // while adjacency list is indexed by ints, this doesn't have to be true.
  // a vertex or edge may be a point. In that case, you'd need a mapping from
  // those pointers into int.
  // Because the algorithms try to be generic, you need to give a (trivial mapping)
  // for ints.
  //
  // iterator property takes an iterator and provides a mapping to it indexed by vertex.
  // an array pointer is also an iterator.
  //
  // adjacency list comes with a vertex_index mapping. Still, to be generic, you
  // need to tell iterator_property_map explicitly.
  typedef boost::property_map<MyGraphType, boost::vertex_index_t>::type IdMap;

  std::vector<vertex_descriptor> pred(num_vertices(G));
  boost::iterator_property_map<std::vector<vertex_descriptor>::iterator,
                                 IdMap,
                                 vertex_descriptor,
                                 vertex_descriptor&>
    predmap(pred.begin(), get(boost::vertex_index, G));

  // this is property map based on a c style array. The predmap above and distmap_vect use
  // stl vectors
  int* dist = (int*) malloc(num_vertices(G)*sizeof(int));
  boost::iterator_property_map<int*,
                                 IdMap,
                                 int,
                                 int&>
    distmap_ptr(dist, get(boost::vertex_index, G));

  std::vector<int> distvector(num_vertices(G));
  boost::iterator_property_map<std::vector<int>::iterator,
                                 IdMap,
                                 int,
                                 int&>
    distmap_vect(distvector.begin(), get(boost::vertex_index, G));

  // note that the operator between pred map and distance map is a '.'
  // and not a ','. That's the named parameter mechanism.
  // The first of these two gets passed a std::vector. The other is a c array.
  dijkstra_shortest_paths(G, v0,
                          predecessor_map(predmap)
                          .distance_map(distmap_vect));

  // the order of named parameters doesn't matter.
  dijkstra_shortest_paths(G, v0,
                          distance_map(distmap_ptr)
                          .predecessor_map(predmap));

}

void example2()
{

  // this example uses the bundeled property mechanism. It's probably the
  // nicest way to associate data with the graph.
  // note that bundeled properties are used both for inputs and outputs to
  // dijkstra
  typedef boost::adjacency_list<boost::vecS, boost::vecS,
                                boost::directedS,
                                VertexData,
                                EdgeData> MyGraphType;

  // when instantiating without args, don't make the mistake of writing
  // MyGraphType G();
  // you'll get cryptic/misleading errors if you do.
  MyGraphType G;

  int v0 = add_vertex(G);
  int v1 = add_vertex(G);
  int v2 = add_vertex(G);

  // don't forget the .first
  auto e1 = add_edge(v0, v1, {"edge1", 10}, G).first;
  auto e2 = add_edge(v1, v2, {"edge2", 20}, G).first;

  // http://www.boost.org/doc/libs/1_64_0/libs/property_map/doc/iterator_property_map.html

  // note that the operator between pred map and distance map is a '.'
  // and not a ','. That's the named parameter mechanism.
  // if you omit the weight, you'll get a compile error like this:
  // /usr/include/boost/graph/detail/adjacency_list.hpp:2696:29: error: forming reference to void
  dijkstra_shortest_paths(G, v0,
                          predecessor_map(get(&VertexData::pred, G))
                          .distance_map(get(&VertexData::pred, G))
                          .weight_map(get(&EdgeData::dist, G)));

}

typedef boost::adjacency_list<boost::vecS, boost::vecS,
                              boost::undirectedS,
                              boost::no_property,
                              // need edge_index for use in distmap below
                              boost::property<boost::edge_index_t, std::size_t> > MyBareGraphType;

// you don't have the use the helper stuff for property maps.
// you can implement them yourself.
// You just need a get() function if the property is just readable.
// to write, you need a put() and to be in the lvalue class, you need
// operator[]. I don't know when you'd need lvalue beyond read/write,
// however an operator[] makes get/put easier to implement.

// a couple important notes about implementing property maps:
//   don't try to skip the map part by just giving a data structure. The data
//   structure has to be separate. You can refer to it in the map, but the map
//   itself can't own the data. There are two reasons why
//   * The property maps are copied by value in many places in the algorithms. So
//     in the best case or read only, you incur a heavy runtime cost copying them.
//     In the not so good case, writes to properties would be lost.
//   * The alogorithms hold the property maps as consts, so unless you cheat, you
//     won't be able to write to the property.
//
// note that in the get/put functions below, the map is passed as a reference and
// the other one or two arguments are not by reference. If you try to pass a reference
// you'll get confusing compiler errors.
class my_weightmap {
 public:
  typedef boost::readable_property_map_tag category;
  typedef int value_type;
  typedef int reference;
  typedef typename boost::graph_traits<MyBareGraphType>::edge_descriptor key_type;

  my_weightmap(std::map<key_type, value_type> &weights) : weights(weights) { /* empty */ }
  value_type getweight(key_type &k) const { return weights[k]; }

private:
  std::map<key_type, value_type> &weights;
};

my_weightmap::value_type
get(const my_weightmap& wm, my_weightmap::key_type k)
{
  return wm.getweight(k);
}

class my_distmap {
 public:
  typedef boost::read_write_property_map_tag category;
  typedef int value_type;
  typedef int reference;
  typedef typename boost::graph_traits<MyBareGraphType>::vertex_descriptor key_type;

  my_distmap(MyBareGraphType &G, std::vector<value_type> &dists)
    : G(G), dists(dists) { /* empty */ }

  value_type& operator[](key_type vh) const {
    return dists[get(boost::vertex_index, G)[vh]];
  }

private:
  std::vector<value_type> &dists;
  MyBareGraphType &G;
};

my_distmap::value_type
get(my_distmap& dm, my_distmap::key_type k)
{
  return dm[k];
}

void
put(const my_distmap& dm, my_distmap::key_type k, my_distmap::value_type v)
{
  dm[k] = v;
}

//http://www.boost.org/doc/libs/1_64_0/libs/property_map/doc/property_map.html
class my_predmap : public boost::put_get_helper<int, my_predmap> {
public:
  typedef boost::read_write_property_map_tag category;
  typedef typename boost::graph_traits<MyBareGraphType>::vertex_descriptor key_type;
  typedef key_type value_type;
  typedef key_type& reference;

  // the put_get_helper, really wants the property map to be const.
  // this makes sense, since a property map is just supposed to be a map and not a container
  my_predmap(std::map<key_type, value_type> &preds) : preds(preds) { /* empty */ }

  value_type& operator[](const key_type &vh) const {
    return preds[vh];
  }

private:
  std::map<key_type, value_type> &preds;
};

void example3()
{

  // when instantiating without args, don't make the mistake of writing
  // MyGraphType G();
  // you'll get cryptic/misleading errors if you do.
  MyBareGraphType G;

  int v0 = add_vertex(G);
  int v1 = add_vertex(G);
  int v2 = add_vertex(G);

  // don't forget the .first
  auto e1 = add_edge(v0, v1, G).first;
  auto e2 = add_edge(v1, v2, G).first;

  std::map<MyBareGraphType::vertex_descriptor, MyBareGraphType::vertex_descriptor> preds;
  my_predmap predmap(preds);
  std::vector<int> dists;
  my_distmap distmap(G, dists);
  std::map<MyBareGraphType::edge_descriptor, int> weights;
  my_weightmap weightmap(weights);

  dijkstra_shortest_paths(G, v0,
                          predecessor_map(predmap)
                          .distance_map(distmap)
                          .weight_map(weightmap));
}
int
main(int, char *[])
{

  example1();
  example2();
  example3();
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   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_IEEE_FUNCTIONS_ILOGB_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_ILOGB_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

namespace boost { namespace simd { namespace tag
  {
   /*!
     @brief ilogb generic tag

     Represents the ilogb function in generic contexts.

     @par Models:
        Hierarchy
   **/
    struct ilogb_ : ext::elementwise_<ilogb_>
    {
      /// @brief Parent hierarchy
      typedef ext::elementwise_<ilogb_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_ilogb_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::ilogb_, Site> dispatching_ilogb_(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::ilogb_, Site>();
   }
   template<class... Args>
   struct impl_ilogb_;
  }
  /*!
    This function returns the integer truncation
    of the base 2 logarithm of a0.
    It coincides with the exponent function
    on all platforms supported.

    @par Semantic:

    @code
    T r = ilogb(a0);
    @endcode

    is similar to:

    @code
    T r = toints(log2(a0));
    @endcode

    @see @funcref{exponent}
    @param a0

    @return a value of same type as the input
  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::ilogb_, ilogb, 1)
} }

#endif


#ifndef WAVEO1_SPARSE_GRIDS_HANDLER_HPP
#define WAVEO1_SPARSE_GRIDS_HANDLER_HPP

#include "mfem.hpp"
using namespace mfem;

#include <Eigen/Core>

#include "../core/config.hpp"
#include "../mymfem/utilities.hpp"

#include "discretisation.hpp"
#include "observer.hpp"
#include "solver.hpp"

class SparseGridsHandler
{
public:
    SparseGridsHandler (const nlohmann::json& config);

    SparseGridsHandler
    (const nlohmann::json& config,
     std::shared_ptr<WaveO1TestCases>& testCase);

    SparseGridsHandler (const nlohmann::json& config,
                        std::string mesh_dir,
                        const int Lx,
                        const int L0x,
                        const int L0t,
                        bool load_init_mesh=false);

    SparseGridsHandler
    (const nlohmann::json& config,
     std::shared_ptr<WaveO1TestCases>& testCase,
     std::string mesh_dir,
     const int Lx, const int L0x, const int L0t,
     bool load_init_mesh=false);

    ~SparseGridsHandler ();

    void set(std::string mesh_dir,
             const bool load_init_mesh=false);
    void set(const int Lx);
    void set(const int Lx, const int L0x, const int L0t);

    // sets minimum resolution levels
    void set_minLevels(const int L0x, const int L0t);

    void reset();

    std::tuple<int, double, double, Eigen::VectorXd>
    operator() ();

    void run ();

    // point locators
    void set_xPointLocators();
    void set_tPointLocators();

    // mesh face
    void set_xMeshFaceLocators();

    // error computation
    Eigen::VectorXd compute_error();

    void init_eval_error();

    //! Computes L2 error
    std::tuple <double, double, double, double>
    eval_xL2Error(Array<GridFunction *> pColl,
                  Array<GridFunction *> vColl) const;

    std::tuple <double, double, double, double>
    eval_xL2Error_at_endTime() const;

    std::tuple <double, double, double, double>
    eval_xtL2Error() const;

    //! Computes L2-projection
    std::tuple<int, double, double, Eigen::VectorXd>
    compute_projection();

    //! Computes DG error
    double eval_dgFtimeError(Array<GridFunction *>,
                             Array<GridFunction *>,
                             double t) const;

    double eval_dgFspaceError
    (Array<GridFunction *>,
     Array<GridFunction *>,
     double t,
     std::shared_ptr<WaveO1InvSqMediumCoeff>&) const;

    double eval_dgFspaceError
    (Array<GridFunction *>,
     Array<GridFunction *>,
     Array<GridFunction *>,
     Array<GridFunction *>,
     double t,
     std::shared_ptr<WaveO1InvSqMediumCoeff>&) const;

    std::tuple <double, double>
    eval_xtDgError() const;

    //! Computes DG+ error
    double eval_dgpFtimeError(Array<GridFunction *>,
                             Array<GridFunction *>,
                             double t) const;

    double eval_dgpFspaceError
    (Array<GridFunction *>,
     Array<GridFunction *>,
     double t,
     std::shared_ptr<WaveO1InvSqMediumCoeff>&) const;

    std::tuple <double, double>
    eval_xtDgpError() const;

    inline std::pair<double, double>
    get_meshChars() const
    {
        double ht_min, ht_max, kappat_min, kappat_max;
        m_tMesh_finest->GetCharacteristics(ht_min,
                                           ht_max,
                                           kappat_min,
                                           kappat_max);

        double hx_min, hx_max, kappax_min, kappax_max;
        m_xMesh_finest->GetCharacteristics(hx_min,
                                           hx_max,
                                           kappax_min,
                                           kappax_max);

        return {std::move(ht_max), std::move(hx_max)};
    }

    inline int get_ndofs() const
    {
        int ndofs = 0;
        for (int k=0; k<m_numSubSols; k++) {
            ndofs += m_subSols[k]->Size();
        }
        return std::move(ndofs);
    }

private:
    const nlohmann::json& m_config;
    std::string m_mesh_dir;
    bool m_load_init_mesh;

    int m_stabParamsType;

    int m_xndim;
    double m_endTime;
    bool m_bool_error = false;
    std::string m_errorType;

    int m_numSubSols;
    int m_Lx, m_L0x, m_L0t;
    int m_numUniqueLevels;

    Array2D<int> m_levels;
    Array<int> m_combCoeffs;

    Array<std::unique_ptr<BlockVector>> m_subSols;
    Array<std::shared_ptr<Mesh>> m_tMeshes;
    Array<std::shared_ptr<Mesh>> m_xMeshes;
    Array<std::shared_ptr<WaveO1XtDG>> m_discrs;
    Array<std::shared_ptr<WaveO1Observer>> m_obsrvs;

    std::shared_ptr<WaveO1TestCases> m_testCase;
    std::unique_ptr<WaveO1ExactPressureCoeff> m_pE_coeff;
    std::unique_ptr<WaveO1ExactVelocityCoeff> m_vE_coeff;

    std::shared_ptr<Mesh> m_tMesh_finest;
    std::shared_ptr<Mesh> m_xMesh_finest;

    FiniteElementSpace* m_tWspace_finest = nullptr;
    FiniteElementSpace* m_xW1space_finest = nullptr;
    FiniteElementSpace* m_xW2space_finest = nullptr;

    std::shared_ptr<GridFunction> m_pE;
    std::shared_ptr<GridFunction> m_vE;

    Array<PointLocator *> m_xPointLocators;
    Array<PointLocator *> m_tPointLocators;

    Array<MeshFaceLocator *> m_xMeshFaceLocators;
};

#endif /// WaveO1_SPARSE_GRIDS_HANDLER_HPP


/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE vxc_potential_test

// Standard includes
#include <fstream>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/xtp/orbitals.h"
#include "votca/xtp/vxc_grid.h"
#include "votca/xtp/vxc_potential.h"
#include <libint2/initialize.h>
#include <votca/tools/eigenio_matrixmarket.h>
using namespace votca::xtp;
using namespace std;

BOOST_AUTO_TEST_SUITE(vxc_potential_test)

AOBasis CreateBasis(const QMMolecule& mol) {
  BasisSet basis;
  basis.Load(std::string(XTP_TEST_DATA_FOLDER) + "/vxc_potential/3-21G.xml");
  AOBasis aobasis;
  aobasis.Fill(basis, mol);
  return aobasis;
}

Eigen::MatrixXd DMat() {
  Eigen::MatrixXd dmat = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/vxc_potential/dmat.mm");
  return dmat;
}

BOOST_AUTO_TEST_CASE(vxc_test) {
  libint2::initialize();
  QMMolecule mol("none", 0);

  mol.LoadFromFile(std::string(XTP_TEST_DATA_FOLDER) +
                   "/vxc_potential/molecule.xyz");
  AOBasis aobasis = CreateBasis(mol);

  Eigen::MatrixXd dmat = DMat();
  Vxc_Grid grid;
  grid.GridSetup("medium", mol, aobasis);
  Vxc_Potential<Vxc_Grid> num(grid);
  num.setXCfunctional("XC_GGA_X_PBE XC_GGA_C_PBE");

  BOOST_CHECK_EQUAL(grid.getGridSize(), grid.getGridpoints().size());
  BOOST_CHECK_EQUAL(grid.getGridSize(), 53404);
  BOOST_CHECK_EQUAL(grid.getBoxesSize(), 51);

  BOOST_CHECK_CLOSE(num.getExactExchange("XC_GGA_X_PBE XC_GGA_C_PBE"), 0.0,
                    1e-5);
  BOOST_CHECK_CLOSE(num.getExactExchange("XC_HYB_GGA_XC_PBEH"), 0.25, 1e-5);
  Mat_p_Energy e_vxc = num.IntegrateVXC(dmat);

  Eigen::MatrixXd vxc_ref = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/vxc_potential/vxc_ref.mm");

  bool check_vxc = e_vxc.matrix().isApprox(vxc_ref, 0.0001);

  BOOST_CHECK_CLOSE(e_vxc.energy(), -4.6303432151572643, 1e-5);
  if (!check_vxc) {
    std::cout << "ref" << std::endl;
    std::cout << vxc_ref << std::endl;
    std::cout << "calc" << std::endl;
    std::cout << e_vxc.matrix() << std::endl;
  }
  BOOST_CHECK_EQUAL(check_vxc, 1);

  libint2::finalize();
}

BOOST_AUTO_TEST_SUITE_END()


//==================================================================================================
/*!
  @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_DIVTRUNC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_DIVTRUNC_HPP_INCLUDED

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

 /*!

    @ingroup group-arithmetic
    Function object implementing divtrunc capabilities

    Computes the truncated division of its parameters.

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

#include <boost/simd/function/scalar/divfix.hpp>
#include <boost/simd/function/simd/divtrunc.hpp>

#endif


// Server for the DedupController service

#include "controller_server.h"

#include <grpcpp/grpcpp.h>

#include <boost/asio/post.hpp>
#include <boost/foreach.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <chrono>
#include <cmath>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <thread>
#include <vector>

#include "INIReader.h"
#include "controller.grpc.pb.h"
#include "main.grpc.pb.h"

using boost::asio::post;
using dedup::Ack;
using dedup::BaseContainerResponse;
using dedup::Container;
using dedup::ContainerPages;
using dedup::Controller;
using dedup::DecisionResponse;
using dedup::MemoryResponse;
using dedup::SpawnArgs;
using dedup::StatusArgs;
using dedup::StatusArgs_Status;
using grpc::ClientContext;
using grpc::ServerContext;
using grpc::Status;
using grpc::StatusCode;
using std::getline;
using std::ifstream;
using std::istringstream;
using std::make_pair;
using std::make_shared;
using std::map;
using std::pair;
using std::shared_ptr;
using std::string;
using std::thread;
using std::unordered_map;
using std::vector;
using std::chrono::duration;
using std::chrono::duration_cast;
using std::chrono::system_clock;
using std::chrono::time_point;

using Decision = dedup::DecisionResponse_Decision;
using PageStruct = dedup::ContainerPages_HashStruct;
using BasePage = dedup::BaseContainerResponse_BasePage;

DecisionResponse::Decision ControllerServerImpl::DedupNoneOpenwhisk(
    int cid, string env) {
  // Dedup Agent seeks decision after ten minute period - and hence, the
  // decision is to purge.
  return DecisionResponse::PURGE;
}

DecisionResponse::Decision ControllerServerImpl::DedupNoneHeuristic(
    int cid, string appl, string env) {
  auto stats = data_structures_->GetEnvironmentStats(env);
  auto appl_info = data_structures_->GetApplicationInfo(appl);

  // OBSOLETE (POLICY 2) := [Alpha, Beta] thresholds
  // int requirement = stats->arrival_rate_ *
  //                   (stats->exec_time_ + data_structures_->reuse_period());
  // BOOST_LOG_TRIVIAL(info) << "Requirement " << requirement << " Available "
  //                         << num_available;
  // double threshold = data_structures_->GetMaxArrivalRate(env);

  double reuse = appl_info->exec_time_ + data_structures_->reuse_period();
  double warm_reuse_period = (stats->warm_start_times_ + reuse) / 1000;

  double provisioned_req =
      ((double)(stats->num_warm_ + stats->num_base_) / warm_reuse_period);

  // For requirement - use maximum and moving average rates
  double upper_threshold = data_structures_->GetMaxArrivalRate(env);

  if (provisioned_req > upper_threshold) {
    return DecisionResponse::PURGE;
  }
  return DecisionResponse::WARM;
}

DecisionResponse::Decision ControllerServerImpl::DedupHeuristic(int cid,
                                                                string appl,
                                                                string env) {
  // Heuristic policy
  auto stats = data_structures_->GetEnvironmentStats(env);
  auto appl_info = data_structures_->GetApplicationInfo(appl);
  Decision tentative_decision;

  // OBSOLETE (POLICY 1) := Lambda * KW / 2
  // if (stats->num_warm_ >= stats->arrival_rate_ * stats->keep_warm_ / 2) {
  //   return DecisionResponse::DEDUP;
  // }

  // OBSOLETE (POLICY 2) := [Alpha, Beta] thresholds
  // Use the requests vector directly instead of the arrival rate
  // int requirement = stats->arrival_rate_ *
  //                   (stats->exec_time_ + data_structures_->reuse_period());
  // int num_available = stats->num_warm_ + stats->num_dedup_;
  // BOOST_LOG_TRIVIAL(info) << "Requirement " << requirement << " Available "
  //                         << num_available << " Exp Req " << expected_req;

  // Use the start-up times directly (In seconds)
  double reuse = appl_info->exec_time_ + data_structures_->reuse_period();
  double warm_reuse_period = (stats->warm_start_times_ + reuse) / 1000;
  double dedup_reuse_period = (stats->dedup_start_times_ + reuse) / 1000;

  double provisioned_req = 0.0;
  if (provisioned_ == 0) {
    // Use only warm containers
    provisioned_req =
        ((double)(stats->num_warm_ + stats->num_base_) / warm_reuse_period);
  } else {
    // Use warm+dedup containers
    provisioned_req =
        ((double)(stats->num_warm_ + stats->num_base_) / warm_reuse_period) +
        ((double)stats->num_dedup_ / dedup_reuse_period);
  }

  // For requirement - use maximum and moving average rates
  double upper_threshold = data_structures_->GetMaxArrivalRate(env);
  double lower_threshold = data_structures_->GetMovingWindowArrivalRate(env);

  BOOST_LOG_TRIVIAL(info) << "Provisioned " << provisioned_req << " LT "
                          << lower_threshold << " UT " << upper_threshold;

  auto c_info = data_structures_->GetContainerInfo(cid);
  auto m_info = data_structures_->GetMachineInfo(c_info->machine_id());

  // Purge only if memory on machine is limited
  if (provisioned_req > gamma_ * upper_threshold) {
    tentative_decision = DecisionResponse::PURGE;
  } else if (provisioned_req > lower_threshold && base_ready_) {
    tentative_decision = DecisionResponse::DEDUP;
  } else {
    tentative_decision = DecisionResponse::WARM;
  }

  // Check if it is already a base container
  if (c_info->is_base()) {
    return DecisionResponse::BASE;
  }

  // Check if more BASE containers needed, and container is not dedup
  if (stats->num_dedup_ >= stats->num_base_ * dedup_per_base_) {
    if (!c_info->is_dedup()) {
      stats->num_base_++;
      return DecisionResponse::BASE;
    }
  }

  // Check if tentative decision is feasible := DEDUP to WARM not possible
  if (c_info->is_dedup() && tentative_decision == DecisionResponse::WARM) {
    return DecisionResponse::DEDUP;
  }

  return tentative_decision;
}

DecisionResponse::Decision ControllerServerImpl::DedupBoundaryHeuristic(
    int cid, string appl, string env) {
  // Heuristic policy
  auto stats = data_structures_->GetEnvironmentStats(env);
  auto appl_info = data_structures_->GetApplicationInfo(appl);
  Decision tentative_decision = DecisionResponse::WARM;

  // Use the start-up times directly (In seconds)
  double reuse = appl_info->exec_time_ + data_structures_->reuse_period();
  double warm_reuse_period = (stats->warm_start_times_ + reuse) / 1000;
  double dedup_start_time =
      stats->dedup_start_times_ > 0.0001 ? stats->dedup_start_times_ : 300;
  double dedup_reuse_period = (dedup_start_time + reuse) / 1000;

  auto c_info = data_structures_->GetContainerInfo(cid);
  auto m_info = data_structures_->GetMachineInfo(c_info->machine_id());

  // Purge as per the keep alive policy
  auto curr_time = system_clock::now();
  auto time_in_idle = duration_cast<duration<double, std::milli>>(
                          curr_time - c_info->idle_state())
                          .count();

  if (time_in_idle >= 600 * 1000) {
    if (c_info->status() == kWarm) {
      BOOST_LOG_TRIVIAL(info) << "Purge warm container";
      tentative_decision = DecisionResponse::PURGE;
    } else if (c_info->status() == kDedup) {
      if (time_in_idle >= 900 * 1000) {
        BOOST_LOG_TRIVIAL(info) << "Purge dedup container " << time_in_idle;
        tentative_decision = DecisionResponse::PURGE;
      }
    }
  } else {
    // Dedup so as to minimize objective, given certain constraints.
    // Useful expressions
    int total_containers =
        stats->num_warm_ + stats->num_base_ + stats->num_dedup_;
    double dedup_cost = warm_reuse_period / dedup_reuse_period;

    // First need to check if the load constraint has a valid solution.
    double d_lambda =
        (total_containers -
         data_structures_->GetMaxArrivalRate(env) * warm_reuse_period) /
        (1 - dedup_cost);

    BOOST_LOG_TRIVIAL(trace)
        << "Total containers: " << total_containers << " "
        << " arr rate: " << data_structures_->GetMaxArrivalRate(env)
        << " cost: " << dedup_cost;

    // Now, make decision depending on whether we want to minimize the memory
    // usage or the latency bound.
    if (latency_constraint_) {
      if (d_lambda < 0) {
        // Even if all current containers are warm, we cannot fulfill the
        // requests
        BOOST_LOG_TRIVIAL(trace)
            << "Load cannot be fulfilled " << d_lambda << " "
            << data_structures_->GetMaxArrivalRate(env);
        tentative_decision = DecisionResponse::WARM;
      } else {
        double latency_threshold = 2.5;
        double frac =
            (dedup_start_time * dedup_cost / stats->warm_start_times_) - 1;

        BOOST_LOG_TRIVIAL(info) << "Frac " << frac << " cost " << dedup_cost
                                << " WRP " << warm_reuse_period;

        double d_latency = (latency_threshold * warm_reuse_period *
                                data_structures_->GetMaxArrivalRate(env) -
                            total_containers) /
                           frac;

        double d_opt = d_latency < d_lambda ? d_latency : d_lambda;

        BOOST_LOG_TRIVIAL(info) << "Optimal " << d_opt << " latency "
                                << d_latency << " lambda " << d_lambda;

        if (d_latency < 0) {
          // NOTE: This implies that even if we keep all dedup containers then
          // also we can meet the load constraints.
          tentative_decision = DecisionResponse::PURGE;
        } else if (stats->num_dedup_ < d_opt) {
          tentative_decision = DecisionResponse::DEDUP;
        } else {
          tentative_decision = DecisionResponse::WARM;
        }
      }
    } else {
      // Get the memory fraction that can be allotted to this application
      double memory_fraction = data_structures_->GetEnvMemoryFraction(env);
      // double d_memory =
      //     (total_containers - (memory_fraction / stats->memory_)) /
      //     (stats->dedup_benefit_ - ((double)15 / stats->memory_));

      BOOST_LOG_TRIVIAL(info) << "Stats mem " << stats->memory_ << " Bene "
                              << stats->dedup_benefit_;

      double d_memory =
          (total_containers - (memory_fraction / stats->memory_)) /
          stats->dedup_benefit_;

      BOOST_LOG_TRIVIAL(info)
          << "Required " << d_memory << " provisioned " << stats->num_dedup_
          << " mem " << memory_fraction;

      if (d_memory < 0) {
        // Enough memory available -- Keep warm
        tentative_decision = DecisionResponse::WARM;
      } else if (d_memory > total_containers) {
        // NOTE: This implies we have too many containers. Hence, must be purged
        // to meet the memory bounds. DEDUP gives too much memory benefits
        tentative_decision = DecisionResponse::DEDUP;
        // if (d_lambda > total_containers) {
        //   tentative_decision = DecisionResponse::PURGE;
        // } else {
        //   tentative_decision = DecisionResponse::DEDUP;
        // }
      } else {
        if (d_memory < d_lambda) {
          // Feasible solution to the constraints existing - policy will try to
          // converge to the solution.
          if (stats->num_dedup_ < d_memory) {
            tentative_decision = DecisionResponse::DEDUP;
          } else {
            tentative_decision = DecisionResponse::WARM;
          }
        } else {
          // No feasible solution to the problem found -- implies the current
          // memory usage is not sufficient to serve the incoming load, but we
          // need to keep the containers around, therefore DEDUP.
          tentative_decision = DecisionResponse::DEDUP;
        }
      }
    }
  }

  // Check if it is already a base container
  if (c_info->is_base()) {
    return DecisionResponse::BASE;
  }

  // Check if more BASE containers needed, and container is not dedup
  if (stats->num_dedup_ >= stats->num_base_ * dedup_per_base_) {
    if (!c_info->is_dedup()) {
      stats->num_base_++;
      return DecisionResponse::BASE;
    }
  }

  // if (stats->num_dedup_ == 0) {
  //   return DecisionResponse::DEDUP;
  // }

  // Check if tentative decision is feasible := DEDUP to WARM not possible
  if (c_info->is_dedup() && tentative_decision == DecisionResponse::WARM) {
    return DecisionResponse::DEDUP;
  }

  // Check if the container is blacklisted - Keep Warm instead of BASE or DEDUP
  if (c_info->blacklisted()) {
    if (tentative_decision == DecisionResponse::BASE ||
        tentative_decision == DecisionResponse::DEDUP) {
      return DecisionResponse::WARM;
    }
  }

  return tentative_decision;
}

DecisionResponse::Decision ControllerServerImpl::DedupHeuristicOpenwhisk(
    int cid, string appl, string env) {
  // Increase the threshold number of decisions before purging container
  // Heuristic policy with base Openwhisk policy
  auto stats = data_structures_->GetEnvironmentStats(env);
  auto appl_info = data_structures_->GetApplicationInfo(appl);
  Decision tentative_decision;

  auto c_info = data_structures_->GetContainerInfo(cid);
  auto m_info = data_structures_->GetMachineInfo(c_info->machine_id());

  // Use the start-up times directly (In seconds)
  double reuse = appl_info->exec_time_ + data_structures_->reuse_period();
  double warm_reuse_period = (stats->warm_start_times_ + reuse) / 1000;
  double dedup_reuse_period = (stats->dedup_start_times_ + reuse) / 1000;
  double provisioned_req = 0.0;
  if (provisioned_ == 0) {
    // Use only warm containers
    provisioned_req =
        ((double)(stats->num_warm_ + stats->num_base_) / warm_reuse_period);
  } else {
    // Use warm+dedup containers
    provisioned_req =
        ((double)(stats->num_warm_ + stats->num_base_) / warm_reuse_period) +
        ((double)stats->num_dedup_ / dedup_reuse_period);
  }

  // For deduplication -- use the same policy
  double lower_threshold = data_structures_->GetMovingWindowArrivalRate(env);

  // Purge only if exceeded the number of decision limits
  // int threshold = 10;
  // if (c_info->num_decisions() > gamma_ * threshold) {
  //   tentative_decision = DecisionResponse::PURGE;
  // } else {
  //   c_info->IncrementNumDecisions();
  //   if (provisioned_req > lower_threshold && base_ready_) {
  //     tentative_decision = DecisionResponse::DEDUP;
  //   } else {
  //     tentative_decision = DecisionResponse::WARM;
  //   }
  // }

  // Check if it is already a base container
  if (c_info->is_base()) {
    return DecisionResponse::BASE;
  }

  // Check if more BASE containers needed, and container is not dedup
  if (stats->num_dedup_ >= stats->num_base_ * dedup_per_base_) {
    if (!c_info->is_dedup()) {
      stats->num_base_++;
      return DecisionResponse::BASE;
    }
  }

  // Check if tentative decision is feasible := DEDUP to WARM not possible
  if (c_info->is_dedup() && tentative_decision == DecisionResponse::WARM) {
    return DecisionResponse::DEDUP;
  }

  return tentative_decision;
}

Status ControllerServerImpl::GetDecision(ServerContext* ctxt,
                                         const Container* args,
                                         DecisionResponse* resp) {
  // BOOST_LOG_TRIVIAL(trace) << "In GetDecision() function";
  int container_id = args->containerid();
  shared_ptr<ContainerInfo> c_info;

  try {
    c_info = data_structures_->GetContainerInfo(container_id);
  } catch (...) {
    BOOST_LOG_TRIVIAL(error) << "GetDecision " << container_id << " cancelled.";
    return Status(StatusCode::CANCELLED, "Container already purged.");
  }

  string appl = c_info->appl();
  string env = c_info->env();
  auto appl_info = data_structures_->GetApplicationInfo(appl);

  // If the container is already dummy -- do not send any decision
  auto container_status = c_info->status();
  if (container_status == kDummy) {
    return Status(StatusCode::CANCELLED, "Container already in a DUMMY state.");
  }

  // TODO: Improvise heuristic
  DecisionResponse::Decision decision;
  // if (container_id % 2 == 0) {
  //   decision = DecisionResponse::BASE;
  // } else {
  //   decision = DecisionResponse::DEDUP;
  // }
  if (appl_info->policy_ == kOpenwhisk) {
    decision = DedupNoneOpenwhisk(container_id, env);
  } else if (appl_info->policy_ == kHeuristicOpenwhisk) {
    decision = DedupHeuristicOpenwhisk(container_id, appl, env);
  } else if (appl_info->policy_ == kNone) {
    decision = DedupNoneHeuristic(container_id, appl, env);
  } else if (appl_info->policy_ == kHeuristic) {
    decision = DedupHeuristic(container_id, appl, env);
  } else if (appl_info->policy_ == kBoundary) {
    decision = DedupBoundaryHeuristic(container_id, appl, env);
  }

  switch (decision) {
    case DecisionResponse::BASE:
    case DecisionResponse::DEDUP:
    case DecisionResponse::WARM:
      // Will be updated when the machine responds
      data_structures_->UpdateContainerStatus(c_info, kDummy);
      break;

    case DecisionResponse::PURGE:
      data_structures_->RemoveContainer(container_id);
      break;

    default:
      break;
  }
  resp->set_decision(decision);
  BOOST_LOG_TRIVIAL(info) << "Decision for " << container_id << " " << decision;

  return Status::OK;
}

// RPC called to notify of a blacklisted container
Status ControllerServerImpl::Blacklist(ServerContext* ctxt,
                                       const Container* args, Ack* ack) {
  int container_id = args->containerid();
  auto c_info = data_structures_->GetContainerInfo(container_id);
  c_info->set_blacklisted();
  return Status::OK;
}

// RPC called to notify the successful change of Status of a container
Status ControllerServerImpl::UpdateStatus(ServerContext* ctxt,
                                          const StatusArgs* args, Ack* ack) {
  int container_id = args->containerid();
  BOOST_LOG_TRIVIAL(info) << "Update status for " << container_id;

  try {
    auto c_info = data_structures_->GetContainerInfo(container_id);
    StatusArgs::Status status = args->status();

    if (status == StatusArgs_Status::StatusArgs_Status_BASE) {
      data_structures_->UpdateContainerStatus(c_info, kBase);
    } else if (status == StatusArgs_Status::StatusArgs_Status_DEDUP) {
      data_structures_->UpdateContainerStatus(c_info, kDedup);
    } else {
      // Used to calculate the number of partial dedup containers
      BOOST_LOG_TRIVIAL(info) << "Update warm container " << container_id;
      data_structures_->UpdateContainerStatus(c_info, kWarm);
    }
  } catch (...) {
    // Implies that the container was already purged.
    BOOST_LOG_TRIVIAL(error)
        << "Container " << container_id << " does not exist";
    return Status::CANCELLED;
  }

  return Status::OK;
}

Status ControllerServerImpl::UpdateAvailableMemory(ServerContext* ctxt,
                                                   const MemoryArgs* args,
                                                   Ack* ack) {
  // Update memory in machine_map_
  int machine_id = args->machine();
  float used_mem = args->usedmemory();

  data_structures_->GetMachineInfo(machine_id)->set_used_memory(used_mem);

  return Status::OK;
}

// RPC called by a BASE container
Status ControllerServerImpl::RegisterPages(ServerContext* ctxt,
                                           const ContainerPages* args,
                                           Ack* ack) {
  // BOOST_LOG_TRIVIAL(trace) << "In RegisterPages() function";

  // Register the hashes from ContainerPages in the global hash table
  int container_id = args->containerid();
  int machine_id = args->machineid();
  int num_pages = args->payload_size();

  int num_hashes;
  for (int i = 0; i < num_pages; i++) {
    PageStruct page_struct = args->payload(i);
    unsigned long int page_addr = page_struct.addr();
    unsigned long int mr_id = page_struct.mrid();
    num_hashes = page_struct.hashes_size();
    auto chunk =
        make_shared<ChunkHashInfo>(container_id, machine_id, page_addr, mr_id);
    for (int j = 0; j < num_hashes; j++) {
      data_structures_->AddChunkHash(page_struct.hashes(j), chunk);
    }
  }

  if (!base_ready_) {
    base_ready_ = true;
  }

  return Status::OK;
}

// RPC called by a DEDUP container
Status ControllerServerImpl::GetBaseContainers(ServerContext* ctxt,
                                               const ContainerPages* args,
                                               BaseContainerResponse* resp) {
  // BOOST_LOG_TRIVIAL(trace) << "In GetBaseContainers() function";

  // Register the hashes from ContainerPages in the global hash table
  int container_id = args->containerid();
  int machine_id = args->machineid();
  int num_pages = args->payload_size();

  // Collect stats about dedup
  int same_func_pages = 0;
  int diff_func_pages = 0;
  auto c_info = data_structures_->GetContainerInfo(container_id);
  string c_appl = c_info->appl();

  // Evaluate the timings
  auto curr_time = system_clock::now();

  /* 1. Iterate over all the pages in the argument and get the hashes of chunks
   * on the page.
   * 2. For each page, compute the heuristic for each distinct base container.
   * 3. Greedily choose base container for each page and add into the
   * BaseContainerResponse object.
   */

  // Map of base containers
  unordered_map<int, int> base_containers;
  int num_dedup_pages = 0;

  for (int i = 0; i < num_pages; i++) {
    PageStruct page_struct = args->payload(i);
    unsigned long int page_addr = page_struct.addr();
    int num_hashes = page_struct.hashes_size();

    // Declare a map for storing duplication information, as follows:
    // Base Container ID versus information of base container pages.
    // NOTE: For each base container - the first match page is taken
    unordered_map<int, shared_ptr<ChunkHashInfo>> dupl_map;

    // List of prospective base containers and their heuristics
    unordered_map<int, double> heursitic_values;

    bool hit = false;
    for (int j = 0; j < num_hashes; j++) {
      // Add the vector of containers to the map for duplication
      vector<shared_ptr<ChunkHashInfo>> addr_vector =
          data_structures_->FindHash(page_struct.hashes(j));
      for (auto& chunk : addr_vector) {
        int base_cid = chunk->cid();
        heursitic_values[base_cid] = 0;

        // If a dedup page has already been found for this page in the base
        // container, do not push another page
        auto search_page = dupl_map.find(base_cid);
        if (search_page != dupl_map.end()) {
          continue;
        }

        hit = true;
        dupl_map.insert(make_pair(base_cid, chunk));
      }
    }

    // Continue if no match for any hash found
    if (!hit) {
      continue;
    }

    // Calculate heuristics for each of the base containers
    double max_heuristic = -1;
    int best_container_id = -1;
    for (auto& base_pair : heursitic_values) {
      int base_cid = base_pair.first;
      auto base_info = data_structures_->GetContainerInfo(base_cid);

      // COMPLETED: Calculate heuristic
      double heuristic = 0.0;
      // heuristic += base_choice_weights_.at(0) * dupl_map[base_cid].size();
      heuristic += base_choice_weights_.at(1) * base_info->refcount();
      if (machine_id == base_info->machine_id()) {
        heuristic += base_choice_weights_.at(2);
      }

      if (heuristic > max_heuristic) {
        max_heuristic = heuristic;
        best_container_id = base_cid;
      }
    }

    auto base_info = dupl_map[best_container_id];
    BasePage* base_page = resp->add_basepagelist();
    base_page->set_addr(page_addr);
    base_page->set_machineid(base_info->mid());
    base_page->set_mrid(base_info->mr_id());
    base_page->set_baseaddr(base_info->addr());

    auto base_cinfo = data_structures_->GetContainerInfo(best_container_id);
    string base_appl = base_cinfo->appl();
    if (base_appl == c_appl) {
      same_func_pages++;
    } else {
      diff_func_pages++;
    }

    // Add to base_containers map to update refcount later
    base_containers[best_container_id] = 0;
    num_dedup_pages++;
  }

  for (auto& base_pair : base_containers) {
    auto base_info = data_structures_->GetContainerInfo(base_pair.first);
    base_info->IncrementRefcount();
  }

  BOOST_LOG_TRIVIAL(trace) << "Num dedup pages: " << num_dedup_pages;

  auto exec_time = duration_cast<duration<double, std::milli>>(
                       system_clock::now() - curr_time)
                       .count();

  if (num_dedup_pages != 0) {
    BOOST_LOG_TRIVIAL(trace) << "Same func pages: "
                            << ((float)same_func_pages / num_dedup_pages);
    BOOST_LOG_TRIVIAL(trace) << "Diff func pages: "
                            << ((float)diff_func_pages / num_dedup_pages);
  }

  return Status::OK;
}

void ControllerServerImpl::SpawnSingleContainer(int mid) {
  // TODO: Which container to spawn? Application & execution environment?
  int cid = data_structures_->AddNewContainer(mid, "1", "3");
  MemoryResponse mr;
  SpawnArgs args;
  ClientContext ctx;

  args.set_containerid(cid);
  args.set_application("1");
  args.set_environment("3");
  Status rpc_status =
      data_structures_->GetMachineInfo(mid)->stub_->Spawn(&ctx, args, &mr);

  BOOST_LOG_TRIVIAL(info) << "Added a new container on machine " << mid;

  // Update memory for machine
  data_structures_->GetMachineInfo(mid)->set_used_memory(mr.usedmemory());

  if (!rpc_status.ok()) {
    data_structures_->RemoveContainer(cid);
  }
}

void ControllerServerImpl::SpawnContainers() {
  // Iterate continuously
  while (true) {
    // If more than 20% memory is available on machine then, we should spawn a
    // new container.
    for (int m = 0; m < num_machines_; m++) {
      auto m_info = data_structures_->GetMachineInfo(m);
      if (m_info->has_enough_memory()) {
        // Spawn a new container on the thread pool
        post(workers_,
             std::bind(&ControllerServerImpl::SpawnSingleContainer, this, m));
      } else {
        // BOOST_LOG_TRIVIAL(info)
        //     << "Machine " << m << " is out of memory " << m_info->memory()
        //     << " " << m_info->total_memory_;
      }
    }

    // Sleep for a little while before checking for more space again.
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
}

/**
 * Read the requests from a file, and pass to the Controller as per timestamp.
 * Request file format: <timestamp (in ms)> <application> <execution env>
 */
void ControllerServerImpl::ServeRequests(string filename) {
  BOOST_LOG_TRIVIAL(debug) << "Starting workload trace";
  ifstream request_file(filename);

  int reqs = 0;
  string timestamp, appl, exec_env;
  string next_timestamp, next_appl, next_env;
  request_file >> timestamp >> appl >> exec_env;
  while (request_file >> next_timestamp >> next_appl >> next_env) {
    // Update the arrival rates stats
    auto curr_time = system_clock::now();
    data_structures_->UpdateArrivalRates(exec_env, curr_time);

    // Spawn thread for the current <timestamp, env>
    post(workers_, std::bind(&ControllerServerImpl::Scheduler::ScheduleRequest,
                             scheduler_.get(), appl, exec_env));
    reqs++;

    if (stoi(next_timestamp) == -1) {
      // Wait for all requests to complete execution
      BOOST_LOG_TRIVIAL(trace) << "Closing up execution now";
      while ((reqs - scheduler_->completed_reqs_) > 20) {
        BOOST_LOG_TRIVIAL(trace)
            << "Completed reqs: " << scheduler_->completed_reqs_ << " " << reqs;
        std::this_thread::sleep_for(std::chrono::duration<double>(10));
      }

      // No further requests. Terminate all. But sleep for ten seconds to ensure
      // any pending operations on machines.
      std::this_thread::sleep_for(std::chrono::duration<double>(10));

      for (int mid = 0; mid < num_machines_; mid++) {
        Ack ack;
        Ack args;
        ClientContext ctx;
        data_structures_->GetMachineInfo(mid)->stub_->Terminate(&ctx, args,
                                                                &ack);
      }

      // Before terminating: print the number of dropped reqs
      BOOST_LOG_TRIVIAL(info)
          << "Dropped requests: " << scheduler_->dropped_reqs_;

      // Wait for another couple seconds and exit
      std::this_thread::sleep_for(std::chrono::duration<double>(2));
      exit(0);
    }

    // Read next line and sleep for the time difference
    int sleep_time = stoi(next_timestamp) - stoi(timestamp);
    // BOOST_LOG_TRIVIAL(debug) << "Sleeping for " << sleep_time;
    std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));

    // Switch current timestamp and exec_env
    timestamp = next_timestamp;
    appl = next_appl;
    exec_env = next_env;
  }
}

void ControllerServerImpl::SetupConnections() {
  boost::property_tree::ptree tree;
  boost::property_tree::read_json("config/cluster.json", tree);

  BOOST_FOREACH (boost::property_tree::ptree::value_type& v,
                 tree.get_child("grpc_nodes")) {
    int mid = v.second.get<int>("machine_id");
    string addr = v.second.get<string>("addr");
    string port = v.second.get<string>("port");
    string conn = addr + ":" + port;

    BOOST_LOG_TRIVIAL(debug)
        << "Opening connection with: " << conn << " " << mid;

    data_structures_->AddMachineInfo(mid, conn, mem_cap_);
    num_machines_ += 1;
  }
}

void ControllerServerImpl::ReadConfig() {
  INIReader reader("config/controller.ini");

  // Read heuristic weights from config file
  string base_str = reader.Get("heuristics", "basechoice", "");
  string state_str = reader.Get("heuristics", "statepolicy", "");

  string token;
  istringstream base_ss(base_str);
  while (base_ss >> token) {
    base_choice_weights_.push_back(std::stod(token));
  }

  istringstream state_ss(state_str);
  while (state_ss >> token) {
    state_policy_weights_.push_back(std::stod(token));
  }

  // Read default policy
  Policy default_policy =
      static_cast<Policy>(reader.GetInteger("policy", "policy", 2));
  data_structures_->set_default_policy(default_policy);
  latency_constraint_ =
      reader.GetInteger("policy", "constraint", 1) == 1 ? true : false;

  dedup_per_base_ = reader.GetInteger("policy", "dedupperbase", 10);
  threshold_ = reader.GetFloat("policy", "threshold", 10.0);
  alpha_ = reader.GetFloat("policy", "alpha", 4);
  beta_ = reader.GetFloat("policy", "beta", 10);
  gamma_ = reader.GetFloat("policy", "gamma", 2);
  provisioned_ = reader.GetInteger("policy", "provisioned", 0);

  // Read params
  data_structures_->set_reuse_period(
      reader.GetInteger("params", "reuseperiod", 500));
  data_structures_->set_request_window(
      reader.GetInteger("params", "window", 10));
  mem_cap_ = (float)reader.GetInteger("params", "memcap", 500);

  // Read application configuration from agent config
  INIReader agent_reader("config/agent.ini");

  data_structures_->set_idle_period(
      agent_reader.GetInteger("parameters", "idletime", 60));

  int num_env = agent_reader.GetInteger("configuration", "numenv", 4);
  int num_appl = agent_reader.GetInteger("configuration", "numappl", 1);
  for (int i = 0; i < num_appl; i++) {
    string appl_name = "appl" + std::to_string(i);
    string conf_str = agent_reader.Get("configuration", appl_name, "");

    string keep_alive, exec_time, memory, dedup_benefit;
    istringstream conf_ss(conf_str);
    conf_ss >> keep_alive;
    conf_ss >> exec_time;
    conf_ss >> memory;
    conf_ss >> dedup_benefit;

    data_structures_->AddNewApplication(
        std::to_string(i), std::stoi(keep_alive), std::stoi(exec_time));
    data_structures_->AddNewEnvironment(std::to_string(i), std::stod(memory),
                                        std::stod(dedup_benefit));
  }
}


#include <iostream>
#include <iomanip>
#include <string>

#define CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE

//#define CGAL_SLS_PRINT_QUEUE_BEFORE_EACH_POP
//#define CGAL_STRAIGHT_SKELETON_ENABLE_TRACE 100
//#define CGAL_STRAIGHT_SKELETON_TRAITS_ENABLE_TRACE 10000000
//#define CGAL_STRAIGHT_SKELETON_ENABLE_VALIDITY_TRACE
//#define CGAL_POLYGON_OFFSET_ENABLE_TRACE 10000000

void Straight_skeleton_external_trace(std::string m)
{
  std::cout << std::setprecision(17) << m << std::endl << std::endl ;
}

void Straight_skeleton_traits_external_trace(std::string m)
{
  std::cout << std::setprecision(17) << m << std::endl << std::endl ;
}

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel_with_sqrt.h>

#include <CGAL/compute_outer_frame_margin.h>
#include <CGAL/create_straight_skeleton_2.h>
#include <CGAL/create_straight_skeleton_from_polygon_with_holes_2.h>
#include <CGAL/draw_straight_skeleton_2.h>
#include <CGAL/Straight_skeleton_builder_2.h>
#include "print.h"

#include <CGAL/Polygon_2.h>
#include <CGAL/draw_polygon_2.h>

#include <boost/shared_ptr.hpp>

#include <cassert>
#include <iostream>
#include <set>
#include <string>
#include <vector>

typedef CGAL::Exact_predicates_inexact_constructions_kernel          EPICK;
typedef CGAL::Exact_predicates_exact_constructions_kernel            EPECK;
typedef CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt  EPECK_w_sqrt;

template <typename K>
void test_API()
{
  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;

  typedef CGAL::Straight_skeleton_2<EPICK>                           Straight_skeleton_EPICK;
  typedef boost::shared_ptr<Straight_skeleton_EPICK>                 Straight_skeleton_Ptr_EPICK;

  typedef CGAL::Straight_skeleton_2<K>                               Straight_skeleton;
  typedef boost::shared_ptr<Straight_skeleton>                       Straight_skeleton_Ptr;

  Polygon_2 p;
  Straight_skeleton_Ptr_EPICK ss0 = CGAL::create_interior_straight_skeleton_2(p);
  Straight_skeleton_Ptr ss1 = CGAL::create_interior_straight_skeleton_2(p, K());
  Straight_skeleton_Ptr_EPICK ss2 = CGAL::create_exterior_straight_skeleton_2(double(1.01), p);
  Straight_skeleton_Ptr ss3 = CGAL::create_exterior_straight_skeleton_2(int(2), p, K());

  Polygon_with_holes_2 pwh;
  Straight_skeleton_Ptr_EPICK ss4 = CGAL::create_interior_straight_skeleton_2(pwh);
  Straight_skeleton_Ptr ss5 = CGAL::create_interior_straight_skeleton_2(pwh, K());
  Straight_skeleton_Ptr_EPICK ss6 = CGAL::create_exterior_straight_skeleton_2(double(1.01), p);
  Straight_skeleton_Ptr ss7 = CGAL::create_exterior_straight_skeleton_2(int(2), p, K());
}

template <typename K, typename StraightSkeleton>
bool is_valid(const boost::shared_ptr<StraightSkeleton>& ss)
{
  typedef typename StraightSkeleton::Traits::Point_2                 Point;
  typedef CGAL::Polygon_2<K>                                         Polygon_2;

  if(!ss)
    return false;

  if(!ss->is_valid())
    return false;

  assert(static_cast<std::size_t>(std::distance(ss->vertices_begin(), ss->vertices_end())) ==
           static_cast<std::size_t>(ss->size_of_vertices()));

  std::set<Point> unique_vertices;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
    unique_vertices.insert(vit->point());

  std::cout << unique_vertices.size() << " unique vertices (" << ss->size_of_vertices() << ")" << std::endl;

  // Can't guarantee that the embedding is correct with EPICK or EPECK (non-exact sqrt)
  if(std::is_same<K, EPECK_w_sqrt>::value)
  {
    if(unique_vertices.size() != ss->size_of_vertices())
      return false;

    for(auto hit=ss->halfedges_begin(); hit!=ss->halfedges_end(); ++hit)
      if(hit->vertex()->point() == hit->opposite()->vertex()->point())
        return false;

    for (auto fit=ss->faces_begin(); fit!=ss->faces_end(); ++fit)
    {
      Polygon_2 p;
      auto h = fit->halfedge();
      do
      {
        p.push_back(h->vertex()->point());
        h = h->next();
      }
      while(h != fit->halfedge());

      if(!p.is_simple())
        return false;
    }
  }

  return true;
}

template <typename K>
void test_skeleton(const char* filename,
                   const std::size_t expected_nv = -1,
                   const std::size_t expected_nh = -1,
                   const std::size_t expected_nf = -1)
{
  std::cout << "Construct straight skeleton of input: " << filename << std::endl;
  std::cout << "Kernel: " << typeid(K).name() << std::endl;

  typedef typename K::Point_2                                        Point;

  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;

  typedef CGAL::Straight_skeleton_2<K>                               Straight_skeleton;
  typedef boost::shared_ptr<Straight_skeleton>                       Straight_skeleton_Ptr;

  std::ifstream in(filename);
  assert(in);

  CGAL::IO::set_ascii_mode(in);

  std::vector<Polygon_2> polys;

  int ccb_count = 0;
  in >> ccb_count;
  for(int i=0; i<ccb_count && in; ++i)
  {
    std::vector<Point> poly;

    int v_count = 0;
    in >> v_count;
    for(int j=0; j<v_count && in; ++j)
    {
      double x = 0., y = 0.;
      in >> x >> y;
      poly.emplace_back(x, y);
    }

    if(poly.size() >= 3)
    {
      bool is_simple = CGAL::is_simple_2(poly.begin(), poly.end(), K());
      if(!is_simple)
        std::cerr << "Input polygon not simple (hopefully it is strictly simple...)" << std::endl;

      CGAL::Orientation expected = (i == 0 ? CGAL::COUNTERCLOCKWISE : CGAL::CLOCKWISE);

      const double area = CGAL::to_double(CGAL::polygon_area_2(poly.begin(), poly.end(), K()));
      CGAL::Orientation orientation = area > 0 ? CGAL::COUNTERCLOCKWISE : area < 0 ? CGAL::CLOCKWISE : CGAL::COLLINEAR;

      if(orientation == expected)
        polys.push_back(Polygon_2(poly.begin(), poly.end()));
      else
        polys.push_back(Polygon_2(poly.rbegin(), poly.rend()));
    }
  }

  assert(!polys.empty());

  std::cout <<"Polygon with border of size: " << polys[0].size() << std::endl;
  if(polys.size() > 1)
    std::cout << polys.size() - 1 << " holes" << std::endl;

  Polygon_with_holes_2 p(polys[0]);
  for(std::size_t i=0; i<polys.size()-1; ++i)
    p.add_hole(polys[i+1]);

  std::cout << p.outer_boundary().size() << " vertices" << std::endl;

  Straight_skeleton_Ptr ss = CGAL::create_interior_straight_skeleton_2(p, K());
  assert(ss);
  assert(is_valid<K>(ss));

  std::cout << ss->size_of_vertices() << " vertices" << std::endl;
  std::cout << ss->size_of_halfedges() << " halfedges" << std::endl;
  std::cout << ss->size_of_faces() << " faces" << std::endl;

  if(expected_nv != std::size_t(-1))
  {
    if(expected_nv != ss->size_of_vertices() ||
       expected_nh != ss->size_of_halfedges() ||
       expected_nf != ss->size_of_faces())
    {
      std::cout << "expected: " << expected_nv << " NV " << expected_nh << " NH " << expected_nf << " NF" << std::endl;
      std::cout << "got: " << ss->size_of_vertices() << " NV " << ss->size_of_halfedges() << " NH " << ss->size_of_faces() << " NF" << std::endl;
      assert(false);
    }
  }

  std::cout << "Exterior..." << std::endl;
  Straight_skeleton_Ptr ss_ext = CGAL::create_exterior_straight_skeleton_2(1., p, K());
  assert(ss_ext);
  assert(is_valid<K>(ss_ext));
}

template <typename K>
void test_kernel()
{
//  CGAL_STSKEL_TRAITS_ENABLE_TRACE

#ifndef CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE
  // test_API<K>();
#endif

  test_skeleton<K>("data/pseudo_split_0.poly", 13, 40, 8);
  test_skeleton<K>("data/pseudo_split_1.poly", 21, 68, 12);
  test_skeleton<K>("data/pseudo_split_2.poly", 21, 68, 12);
  test_skeleton<K>("data/pseudo_split_3.poly", 25, 86, 16);
  test_skeleton<K>("data/pseudo_split_4.poly", 25, 86, 16);
  test_skeleton<K>("data/pseudo_split_5.poly"/*, 29, 104, 20*/); // almost duplicates
  test_skeleton<K>("data/pseudo_split_5b.poly"/*, 29, 104, 20*/); // almost duplicates
  test_skeleton<K>("data/pseudo_split_6.poly", 13, 40, 8);
  test_skeleton<K>("data/pseudo_split_7.poly", 65, 216, 44);
  test_skeleton<K>("data/pseudo_split_8.poly");
  test_skeleton<K>("data/pseudo_split_9.poly");
  test_skeleton<K>("data/pseudo_split_10.poly", 13, 40, 8);
  test_skeleton<K>("data/pseudo_split_11.poly", 14, 44, 9);
  test_skeleton<K>("data/pseudo_split_12.poly");
  test_skeleton<K>("data/pseudo_split_13b.poly", 11, 34, 7);
  test_skeleton<K>("data/pseudo_split_13.poly", 11, 34, 7);

  test_skeleton<K>("data/1_Example.poly", 119, 364, 64);
  test_skeleton<K>("data/1_Example_Working.poly", 104, 318, 56);
  test_skeleton<K>("data/2_Example.poly", 37, 112, 20);
  test_skeleton<K>("data/5-SPOKE2.poly"/*, 32, 102, 22*/); // weird almost-duplicates
  test_skeleton<K>("data/5-SPOKE.poly"/*, 32, 102, 22*/);
  test_skeleton<K>("data/7-SPOKE.poly");
  test_skeleton<K>("data/alley_0.poly", 18, 58, 12);
  test_skeleton<K>("data/alley_1.poly", 20, 62, 12);
  test_skeleton<K>("data/alley_2.poly", 26, 78, 12);
  test_skeleton<K>("data/alley_3.poly", 34, 102, 16);
  test_skeleton<K>("data/AlmostClosed.poly");
  test_skeleton<K>("data/A.poly", 58, 174, 29);
  test_skeleton<K>("data/closer_edge_event_0.poly", 11, 34, 7);
  test_skeleton<K>("data/closer_edge_event_1.poly", 11, 34, 7);
  test_skeleton<K>("data/consecutive_coincident_vertices_0.poly", 6, 18, 4);
  test_skeleton<K>("data/consecutive_coincident_vertices_1.poly", 6, 18, 4);
  test_skeleton<K>("data/consecutive_coincident_vertices_2.poly", 6, 18, 4);
  test_skeleton<K>("data/consecutive_coincident_vertices_3.poly", 6, 18, 4);
  test_skeleton<K>("data/consecutive_coincident_vertices_4.poly", 6, 18, 4);
  test_skeleton<K>("data/degenerate0.poly", 8, 24, 5);
  test_skeleton<K>("data/degenerate0a.poly", 8, 24, 5);
  test_skeleton<K>("data/degenerate1.poly", 9, 28, 6);
  test_skeleton<K>("data/degenerate2.poly", 7, 22, 5);
  test_skeleton<K>("data/degenerate3.poly", 10, 32, 7);
  test_skeleton<K>("data/degenerate4.poly", 11, 36, 8);
  test_skeleton<K>("data/degenerate5a.poly", 10, 30, 6);
  test_skeleton<K>("data/degenerate5.poly", 10, 30, 6);
  test_skeleton<K>("data/degenerate6.poly", 8, 26, 6);
  test_skeleton<K>("data/degenerate7.poly", 12, 36, 7);
  test_skeleton<K>("data/degenerate8.poly", 15, 52, 12);
  test_skeleton<K>("data/degenerate9.poly", 16, 48, 9);
  test_skeleton<K>("data/degenerate10.poly", 19, 58, 11);
  test_skeleton<K>("data/degenerate11.poly", 17, 56, 12);
  test_skeleton<K>("data/degenerate12.poly", 17, 52, 10);
  test_skeleton<K>("data/degenerate13.poly", 9, 32, 8);
  test_skeleton<K>("data/degenerate20.poly");
  test_skeleton<K>("data/degenerate21.poly");
  test_skeleton<K>("data/degenerate22.poly", 36, 108, 18);
  test_skeleton<K>("data/degenerate22b.poly", 35, 106, 18);
  test_skeleton<K>("data/degenerate22c.poly", 29, 88, 15);
  test_skeleton<K>("data/degenerate24.poly", 36, 108, 18);
  test_skeleton<K>("data/degenerate25.poly", 19, 58, 10);
  test_skeleton<K>("data/degenerate26.poly", 24, 74, 14);
  test_skeleton<K>("data/degenerate27b.poly", 19, 58, 11);
  test_skeleton<K>("data/degenerate27c.poly", 19, 58, 11);
  test_skeleton<K>("data/degenerate27d.poly", 25, 76, 14);
  test_skeleton<K>("data/degenerate27e.poly", 26, 80, 15);
  test_skeleton<K>("data/degenerate27.poly", 18, 56, 11);
  test_skeleton<K>("data/degenerate28aa.poly"/*, 58, 178, 32*/); // should be 58, but almost-duplicate so 59
  test_skeleton<K>("data/degenerate28a.poly"/*, 58, 178, 32*/); // same as above
  test_skeleton<K>("data/degenerate28bb.poly");
  test_skeleton<K>("data/degenerate28b.poly");
  test_skeleton<K>("data/degenerate28c.poly");
  test_skeleton<K>("data/degenerate28x.poly");
  test_skeleton<K>("data/degenerate29.poly");
  test_skeleton<K>("data/degenerate_multinode0.poly", 41, 136, 28);
  test_skeleton<K>("data/double_edge.poly", 7, 22, 5);
  test_skeleton<K>("data/double_edge_0.poly", 7, 22, 5);
  test_skeleton<K>("data/double_edge_1.poly", 7, 22, 5);
  test_skeleton<K>("data/double_edge_2.poly", 35, 108, 19);
  test_skeleton<K>("data/double_split.poly", 12, 36, 7);
  test_skeleton<K>("data/equal_times_0.poly", 16, 48, 9);
  test_skeleton<K>("data/ExtraEdge_1.poly");
  test_skeleton<K>("data/ExtraEdge_2.poly"/*, 41, 130, 25*/); // almost duplicates
  test_skeleton<K>("data/hole.poly", 16, 48, 8);
  test_skeleton<K>("data/inputcircle.poly"/* 61, 188, 34*/); // almost duplicates
  test_skeleton<K>("data/inputsquare.poly", 29, 104, 20);
  test_skeleton<K>("data/inputsquare2.poly", 21, 68, 12);
  test_skeleton<K>("data/many_holes.poly");
  test_skeleton<K>("data/masked_double_split.poly", 10, 30, 6);
  test_skeleton<K>("data/multinode0.poly", 29, 96, 20);
  test_skeleton<K>("data/multinode1.poly", 29, 96, 20);
  test_skeleton<K>("data/near_degenerate_0.poly", 10, 30, 6);
  test_skeleton<K>("data/near_degenerate_1.poly", 14, 42, 8);
  test_skeleton<K>("data/nearly_collinear.poly");
  test_skeleton<K>("data/parallels0_b.poly");
  test_skeleton<K>("data/parallels0.poly");
  test_skeleton<K>("data/parallels_1.poly", 30, 106, 20);
  test_skeleton<K>("data/poly6.poly"/*, 15, 48, 10*/); // almost duplicates
  test_skeleton<K>("data/rect_4_spokes.poly", 21, 72, 16);
  test_skeleton<K>("data/rectangle.poly", 6, 18, 4);
  test_skeleton<K>("data/region_4.poly", 4, 12, 3);
  test_skeleton<K>("data/rombus_4_spokes.poly", 17, 56, 12);
  test_skeleton<K>("data/issue5177.poly");

  // The embedding of those below is bad when using EPICK
  test_skeleton<K>("data/poly4.poly"/*, 15, 48, 10*/); // almost duplicates
  test_skeleton<K>("data/poly4b.poly"/*, 15, 48, 10*/); // almost duplicates
  test_skeleton<K>("data/Detmier.poly");
  test_skeleton<K>("data/Detmier_b.poly"/*, 28, 86, 16*/); // almost duplicates
  test_skeleton<K>("data/Detmier_c.poly");
  test_skeleton<K>("data/Detmier_d.poly"/*, 27, 84, 16*/); // almost duplicates
  test_skeleton<K>("data/Detmier_e.poly");

#ifndef CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE
  test_skeleton<K>("data/sample_0.poly");
  test_skeleton<K>("data/sample_101.poly");
  test_skeleton<K>("data/sample_102.poly");
  test_skeleton<K>("data/sample_147.poly");
  test_skeleton<K>("data/sample_1.poly", 24, 72, 12);
  test_skeleton<K>("data/sample_235.poly");
  test_skeleton<K>("data/sample_298.poly");
  test_skeleton<K>("data/sample_2.poly");
  test_skeleton<K>("data/sample2.poly", 12, 36, 7);
  test_skeleton<K>("data/sample_319.poly");
  test_skeleton<K>("data/sample_325.poly");
  test_skeleton<K>("data/sample_333.poly");
  test_skeleton<K>("data/sample_3.poly", 16, 48, 8);
  test_skeleton<K>("data/sample3.poly");
  test_skeleton<K>("data/sample_46.poly");
  test_skeleton<K>("data/sample_4.poly");
  test_skeleton<K>("data/sample_5.poly");
  test_skeleton<K>("data/sample_638.poly");
  test_skeleton<K>("data/sample_698.poly");
  test_skeleton<K>("data/sample_6.poly", 18, 54, 10);
  test_skeleton<K>("data/sample_73.poly");
  test_skeleton<K>("data/sample_85.poly");
  test_skeleton<K>("data/sample.poly", 22, 66, 12);
  test_skeleton<K>("data/simple_0.poly");
  test_skeleton<K>("data/simple_1.poly", 24, 72, 12);
  test_skeleton<K>("data/simple_2.poly");
  test_skeleton<K>("data/simple_3.poly");
  test_skeleton<K>("data/single_split.poly", 8, 24, 5);
  test_skeleton<K>("data/split_at_end_0.poly", 11, 34, 7);
  test_skeleton<K>("data/split_at_end_1.poly", 11, 36, 8);
  test_skeleton<K>("data/split_at_end_2.poly", 11, 34, 7);
  test_skeleton<K>("data/split_at_zero_0.poly", 20, 60, 11);
  test_skeleton<K>("data/square.poly", 5, 16, 4);
  test_skeleton<K>("data/triangle.poly", 4, 12, 3);
  test_skeleton<K>("data/star.poly", 9, 32, 8);
  test_skeleton<K>("data/StrayCenterlines.poly");
  test_skeleton<K>("data/wheel_13_spokes.poly");
  test_skeleton<K>("data/wheel_14_spokes.poly");
  test_skeleton<K>("data/wheel_15_spokes.poly");
  test_skeleton<K>("data/wheel_16_spokes.poly");
  test_skeleton<K>("data/wheel_16_spokes_b.poly");
  test_skeleton<K>("data/wheel_128_spokes.poly");
  test_skeleton<K>("data/wiggly_03_cgal.poly");
  test_skeleton<K>("data/WingChiu.poly");

  test_skeleton<K>("data/large_1.poly");
  test_skeleton<K>("data/large_2.poly");
  test_skeleton<K>("data/large_3.poly");
  test_skeleton<K>("data/large_4.poly");
  test_skeleton<K>("data/complex_0.poly");
  test_skeleton<K>("data/complex_1.poly");
  test_skeleton<K>("data/complex_2.poly");
  test_skeleton<K>("data/complex_3.poly");
  test_skeleton<K>("data/complex_4.poly");
  test_skeleton<K>("data/complex_5.poly");
  test_skeleton<K>("data/inputc.poly");
  test_skeleton<K>("data/inputd1.poly");
  test_skeleton<K>("data/inputd.poly");
  test_skeleton<K>("data/inputG.poly");
  test_skeleton<K>("data/input_K.poly");
  test_skeleton<K>("data/inputPa.poly");
  test_skeleton<K>("data/inputP.poly");
  test_skeleton<K>("data/inputq.poly");
  test_skeleton<K>("data/inputT.poly");
  test_skeleton<K>("data/inputu.poly");
  test_skeleton<K>("data/inputq1.poly", 21, 64, 12);
#endif
}

int main(int, char**)
{
  std::cout.precision(17);
  std::cerr.precision(17);

  test_kernel<EPICK>();
  test_kernel<EPECK>();
  test_kernel<EPECK_w_sqrt>();

  std::cout << "Done!" << std::endl;

  return EXIT_SUCCESS;
}


/**
 * The MIT License (MIT)
 * Copyright (c) 2017-2017 Daniel Sebastian Iliescu, http://dansil.net
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "raytracer/scene.hpp"

#include <boost/property_tree/json_parser.hpp>
#include <raytracer/geometry/object/sphere.hpp>

namespace raytracer
{
	scene::scene( const std::string filename )
	{
		read_json( filename, this->property_tree );

		this->parse_root();
	}

	const metadata& scene::get_metadata() const
	{
		return this->metadata;
	}

	const elements& scene::get_elements() const
	{
		return this->elements;
	}

	void scene::parse_root()
	{
		const auto node = this->property_tree.get_child( "scene" );

		this->parse_metadata( node.get_child( "metadata" ) );
		this->parse_element( node.get_child( "element" ) );
	}

	void scene::parse_metadata( const boost::property_tree::ptree& metadata_node )
	{
		this->metadata.rows = metadata_node.get< metadata::size_type >( "height" );
		this->metadata.columns = metadata_node.get< metadata::size_type >( "width" );
	}

	void scene::parse_element( const boost::property_tree::ptree& element_node )
	{
		this->parse_illumination( element_node.get_child( "illumination" ) );
		this->parse_object( element_node.get_child( "object" ) );
	}

	void scene::parse_illumination( const boost::property_tree::ptree& illumination_node )
	{
		for ( const auto& point_node : illumination_node )
		{
			const auto point = parse_array< vector_type >( point_node.second.get_child( "point" ) );

			this->elements.illuminations.emplace_back( point.data(), point.size() );
		}
	}

	void scene::parse_object( const boost::property_tree::ptree& object_node )
	{
		this->parse_sphere( object_node.get_child( "sphere" ) );
	}

	void scene::parse_sphere( const boost::property_tree::ptree& sphere_node )
	{
		for ( const auto& sphere : sphere_node )
		{
			const auto point = parse_array< vector_type >( sphere.second.get_child( "point" ) );
			const auto radius = sphere.second.get< vector_type >( "radius" );
			const auto color = parse_array< image::channel_type >( sphere.second.get_child( "color" ) );

			image::channels channels;

			std::copy( std::cbegin( color ), std::cend( color ), std::begin( channels ) );

			this->elements.objects.emplace_back(
				std::make_shared< geometry::object::sphere >(
					geometry::point( point.data(), point.size() ),
					radius,
					channels ) );
		}
	}
}


#include<iostream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

using namespace std;

static boost::random::mt19937 gen;

float get_random(){
  static boost::random::uniform_real_distribution<> dist(0, 1);
  return dist(gen);
}

float get_rand(){
  return ((float) rand()) / (float) RAND_MAX;
}

int main(){
  // srand(time(NULL));
  gen.seed(time(0));
  cout << get_random() << endl;
  cout << get_random() << endl;
  return 0;
}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// #define EIGEN_DONT_VECTORIZE
// #define EIGEN_MAX_ALIGN_BYTES 0
#include "sparse_solver.h"
#include <Eigen/IterativeLinearSolvers>
#include <unsupported/Eigen/IterativeSolvers>

template<typename T, typename I> void test_incomplete_cholesky_T()
{
  typedef SparseMatrix<T,0,I> SparseMatrixType;
  ConjugateGradient<SparseMatrixType, Lower, IncompleteCholesky<T, Lower, AMDOrdering<I> > >     cg_illt_lower_amd;
  ConjugateGradient<SparseMatrixType, Lower, IncompleteCholesky<T, Lower, NaturalOrdering<I> > > cg_illt_lower_nat;
  ConjugateGradient<SparseMatrixType, Upper, IncompleteCholesky<T, Upper, AMDOrdering<I> > >     cg_illt_upper_amd;
  ConjugateGradient<SparseMatrixType, Upper, IncompleteCholesky<T, Upper, NaturalOrdering<I> > >     cg_illt_upper_nat;

  CALL_SUBTEST( check_sparse_spd_solving(cg_illt_lower_amd) );
  CALL_SUBTEST( check_sparse_spd_solving(cg_illt_lower_nat) );
  CALL_SUBTEST( check_sparse_spd_solving(cg_illt_upper_amd) );
  CALL_SUBTEST( check_sparse_spd_solving(cg_illt_upper_nat) );
}

void test_incomplete_cholesky()
{
  CALL_SUBTEST_1(( test_incomplete_cholesky_T<double,int>() ));
  CALL_SUBTEST_2(( test_incomplete_cholesky_T<std::complex<double>, int>() ));
  CALL_SUBTEST_3(( test_incomplete_cholesky_T<double,long int>() ));
}


/**
 * Heavily modified version of Ralf Wimmer's XML parser
 */

#include <algorithm> // for std::sort
#include <cstddef> // to fix errors with gmp
#include <iomanip>
#include <boost/lexical_cast.hpp>
#include "parse_xml.hpp"
#include <gmp.h>
#include <sylvan_gmp.h>
#include <sigref.h>

namespace sigref {

using namespace sylvan;

/**
 * @brief Tests if an xml-node has a given attribute
 */
inline bool
hasAttribute(const TiXmlNode* node, const char* att)
{
    assert(node->ToElement() != NULL);
    return node->ToElement()->Attribute(att) != NULL;
}

/**
 * @brief Reads the value of the given attribute of an xml-node and converts it to a string.
 */
inline std::string
readStringAttribute(const TiXmlNode* node, const char* att)
{
    assert(node != NULL && att != NULL && hasAttribute(node, att));
    const char *s = node->ToElement()->Attribute(att);
    if (s == NULL) throw ParseError("[ERROR] Attribute not found");
    return std::string(s);
}

/**
 * @brief Reads the value of the given attribute of an xml-node and converts it to an integer.
 */
inline unsigned int
readIntAttribute(const TiXmlNode* node, const char* att)
{
    const std::string numberString = readStringAttribute(node, att);
    try {
        return boost::lexical_cast<unsigned int>(numberString);
    } catch (boost::bad_lexical_cast &) {
        throw ParseError("[ERROR] String " + numberString + " is not a number");
    }
}

/**
  \brief Reads the value of the given attribute of an xml-node and converts it to a double.
 */
inline Mtbdd
readDoubleAttribute(const TiXmlNode* node, const char* att)
{
    const std::string numberString = readStringAttribute(node, att);
    try {
        size_t pos = 0;
        if ((pos = numberString.find('/')) != std::string::npos) {
            const std::string numerator = numberString.substr(0,pos);
            const std::string denominator = numberString.substr(pos+1);
            double value = boost::lexical_cast<double>(numerator)/boost::lexical_cast<double>(denominator);
            if (value == 0.0) return mtbdd_false;
            return Mtbdd::doubleTerminal(value);
        } else {
            double value = boost::lexical_cast<double>(numberString);
            if (value == 0.0) return mtbdd_false;
            return Mtbdd::doubleTerminal(value);
        }
    } catch(boost::bad_lexical_cast&) {
        throw ParseError("[ERROR] String " + numberString + " is not a number");
    }
}

inline Mtbdd
readMPQAttribute(const TiXmlNode* node, const char* att)
{
    const std::string numberString = readStringAttribute(node, att);
    mpq_t gmp_value;
    mpq_init(gmp_value);
    try {
        size_t pos = 0;
        if ((pos = numberString.find('.')) != std::string::npos) {
            mpf_t f_value;
            mpf_init(f_value);
            mpf_set_str(f_value, numberString.c_str(), 10);
            mpq_set_f(gmp_value, f_value);
            mpf_clear(f_value);
        } else {
            mpq_set_str(gmp_value, numberString.c_str(), 10);
        }
        if (mpq_sgn(gmp_value) == 0) {
            mpq_clear(gmp_value);
            return mtbdd_false;
        }
        MTBDD res = mtbdd_gmp(gmp_value);
        mpq_clear(gmp_value);
        return res;
    } catch(boost::bad_lexical_cast&) {
        throw ParseError("[ERROR] String " + numberString + " is not a number");
    }
}

inline Mtbdd
readSimpleFractionAttribute(const TiXmlNode* node, const char* att)
{
    const std::string numberString = readStringAttribute(node, att);

    try {
        size_t pos = 0;
        if ((pos = numberString.find('/')) != std::string::npos) {
            const std::string numerator = numberString.substr(0,pos);
            const std::string denominator = numberString.substr(pos+1);

            uint64_t num = ((uint64_t)boost::lexical_cast<unsigned int>(numerator));
            uint64_t den = ((uint64_t)boost::lexical_cast<unsigned int>(denominator));
            if (num == 0) return mtbdd_false;
            if (num > 0xffffffff || den > 0xffffffff) throw ParseError("[ERROR] Fraction " + numberString + " does not fit in 32-bit integers");
            return Mtbdd::fractionTerminal(num, den);
        } else if ((pos = numberString.find('.')) != std::string::npos) {
            const std::string before = numberString.substr(0,pos);
            const std::string after = numberString.substr(pos+1);
            uint64_t num = boost::lexical_cast<uint64_t>(before+after);
            uint64_t den = 1;
            for (unsigned int i=0; i<after.length(); i++) den *= 10;
            if (num > 0xffffffff || den > 0xffffffff) throw ParseError("[ERROR] Fraction " + numberString + " does not fit in 32-bit integers");
            if (num == 0) return mtbdd_false;
            return Mtbdd::fractionTerminal(num, den);
        } else {
            uint64_t num = (uint64_t)boost::lexical_cast<unsigned int>(numberString);
            if (num == 0) return mtbdd_false;
            if (num > 0xffffffff) throw ParseError("[ERROR] Fraction " + numberString + " does not fit in 32-bit integers");
            return Mtbdd::fractionTerminal(num, 1);
        }
    } catch(boost::bad_lexical_cast&) {
        throw ParseError("[ERROR] String " + numberString + " is not a number, or x/y does not fit in 32-bit integers");
    }
}

SystemParser::SystemParser(const char* filename, unsigned int verbosity, LeafType leaf_type)
{
    // Open the document
    TiXmlDocument document = TiXmlDocument(filename);
    if (!document.LoadFile()) {
        throw ParseError("[ERROR] Could not load the input file.");
    }

    // Get the root node of the XML-document
    TiXmlNode* rootNode = document.RootElement();
    if (rootNode == NULL) {
        throw ParseError("[ERROR] Could not access the root node of the XML-tree");
    }

    // Find out the type of the transition system (lts, ctmc, imc)
    const std::string type_str = readStringAttribute(rootNode, "type");
    if (type_str == "lts") system_type = lts_type;
    else if (type_str == "ctmc") system_type = ctmc_type;
    else if (type_str == "imc") system_type = imc_type;
    else system_type = lts_type;

    this->leaf_type = leaf_type;

    // Traverse the children of the root node and collect the pointers to the parts we need.
    TiXmlNode* varinfoNode = NULL;
    TiXmlNode* initialstateNode = NULL;
    TiXmlNode* transNode = NULL;
    TiXmlNode* markovtransNode = NULL;
    TiXmlNode* initialpartitionNode = NULL;
    TiXmlNode* tauNode = NULL;

    for (TiXmlNode* currentNode = rootNode->FirstChild();
         currentNode != NULL;
         currentNode = currentNode->NextSibling()) {

        if (currentNode->ToElement() == NULL) continue;

        const char* const name = currentNode->ToElement()->Value();

        if (strcmp(name, "variables") == 0) {
            varinfoNode = currentNode;
        } else if (strcmp(name, "dd") == 0) {
            std::string bdd_type = readStringAttribute(currentNode, "type");
            if (bdd_type == "initial_state") {
                initialstateNode = currentNode;
            } else if (bdd_type == "trans") {
                transNode = currentNode;
            } else if (bdd_type == "markov_trans") {
                markovtransNode = currentNode;
            } else if (bdd_type == "tau") {
                tauNode = currentNode;
            }
        } else if (strcmp(name, "initial_partition") == 0) {
            initialpartitionNode = currentNode;
        }
    }

    // Check if we have found the variable information
    if (varinfoNode == NULL) {
        throw ParseError("[ERROR] No variable information found!");
    }

    // Check if the correct information exists for the system type
    switch (system_type) {
    case lts_type:
        if (markovtransNode != NULL) {
            throw ParseError("[ERROR] LTS must not have any Markov transitions!");
        }
        if (transNode == NULL) {
            throw ParseError("[ERROR] LTS must have an interactive transition relation!");
        }
        break;
    case ctmc_type:
        if (transNode != NULL) {
            throw ParseError("[ERROR] CTMCs must not have any interactive transitions!");
        }
        if (markovtransNode == NULL) {
            throw ParseError("[ERROR] CTMCs must have a Markov transition relation!");
        }
        break;
    case imc_type:
        if (transNode == NULL) {
            throw ParseError("[ERROR] IMCs must have an interactive transition relation!");
        }
        if (markovtransNode == NULL) {
            throw ParseError("[ERROR] IMCs must have a Markov transition relation!");
        }
        break;
    }

        // Parse the variable information and create the BDD variables
        // together with an appropriate order for refinement
        if (verbosity > 0) std::cout << "[INFO] Creating BDD variables ... " << std::flush;
        createVariables(varinfoNode);
        if (verbosity > 0) std::cout << "finished." << std::endl;

        // Build the BDDs/ADDs for all parts
        if (verbosity > 0) std::cout << "[INFO] Building BDDs ... " << std::flush;

        std::vector<std::pair<Bdd, Bdd>> transitions;
        Bdd _transitions = Bdd::bddZero();
        if (transNode != NULL) {
            _transitions = nodeToBdd(transNode);
            transitions.push_back(std::make_pair(_transitions, varS * varT));
        }

        Mtbdd markov_transitions;
        if (markovtransNode != NULL) markov_transitions = nodeToMtbdd(markovtransNode);

        Bdd tau;
        if (tauNode != NULL) tau = nodeToBdd(tauNode);
        else {
            // Default value of tau: 0
            int action_bits = sylvan_set_count(varA.GetBDD());
            std::vector<uint8_t> tau_value;
            for (int i=0; i<action_bits; i++) {
                tau_value.push_back(tau_action & (1LL<<(action_bits-i-1)) ? 1 : 0);
            }
            tau = Bdd::bddCube(varA, tau_value);
        }

        Bdd initial_state;
        if (initialstateNode != NULL) initial_state = nodeToBdd(initialstateNode);

        Bdd states = computeStateSpace(_transitions, markov_transitions);

        std::vector<Bdd> initial_partition;
        if (initialpartitionNode != NULL) {
            for (TiXmlNode* currentNode = initialpartitionNode->FirstChild();
                 currentNode != NULL; currentNode = currentNode->NextSibling()) {
                initial_partition.push_back(nodeToBdd(currentNode));
            }
        }
        if (initial_partition.size() == 0) initial_partition.push_back(states);
        if (verbosity > 0) std::cout << "finished." << std::endl;

        // Fill the right system with information
        switch (system_type) {
        case lts_type:
            lts.transitions = transitions;
            lts.states = states;
            lts.tau = tau;
            lts.initialStates = initial_state;
            lts.initialPartition = initial_partition;
            lts.varS = varS;
            lts.varT = varT;
            lts.varA = varA;
            break;

        case imc_type:
            imc.transitions = transitions;
            imc.markov_transitions = markov_transitions;
            imc.states = states;
            imc.tau = tau;
            imc.initialStates = initial_state;
            imc.initialPartition = initial_partition;
            imc.varS = varS;
            imc.varT = varT;
            imc.varA = varA;
            break;

        case ctmc_type:
            ctmc.markov_transitions = markov_transitions;
            ctmc.states = states;
            ctmc.initialStates = initial_state;
            ctmc.initialPartition = initial_partition;
            ctmc.varS = varS;
            ctmc.varT = varT;
            break;

        default:
            break;
        }
}

SystemParser::~SystemParser()
{
}

void
SystemParser::createVariables(TiXmlNode *varinfoNode) {
    unsigned int maxIndex = 0;
    int numS = 0;
    int numA = 0;

    // Calculate highest index, number of state variables, number of action variables
    for (TiXmlNode* currentNode = varinfoNode->FirstChild();
         currentNode != NULL;
         currentNode = currentNode->NextSibling()) {
        const unsigned int index = readIntAttribute(currentNode, "index");
        if (index > maxIndex) maxIndex = index;

        const std::string type = readStringAttribute(currentNode, "type");
        if (type == "ps") numS++;
        else if (type == "in") numA++;
    }

    std::vector<int> state_vars;
    std::vector<uint32_t> action_vars;
    std::map<int, int> state_to_next_vars;

    // Gather all
    for (TiXmlNode* currentNode = varinfoNode->FirstChild();
         currentNode != NULL; currentNode = currentNode->NextSibling()) {
        const unsigned int index = readIntAttribute(currentNode, "index");
        const std::string type = readStringAttribute(currentNode, "type");
        if (type == "ps") {
            state_vars.push_back(index);
            state_to_next_vars[index] = readIntAttribute(currentNode, "corr");
        } else if (type == "in") {
            action_vars.push_back(index);
        }
    }

    std::sort(state_vars.begin(), state_vars.end());
    std::sort(action_vars.begin(), action_vars.end());

    std::vector<uint32_t> bdd_state_vars;
    std::vector<uint32_t> bdd_prime_vars;
    std::vector<uint32_t> bdd_action_vars;

    for (int i=0; i < numS; i++) {
        bdd_state_vars.push_back(i*2);
        bdd_prime_vars.push_back(i*2+1);
        var_to_mtbdd[state_vars[i]] = Mtbdd::mtbddVar(i*2);
        var_to_mtbdd[state_to_next_vars[state_vars[i]]] = Mtbdd::mtbddVar(i*2+1);
    }

    for (int i=0; i < numA; i++) {
        bdd_action_vars.push_back(1000000+i);
        var_to_mtbdd[action_vars[i]] = Mtbdd::mtbddVar(1000000+i);
    }

    varS = Bdd::VariablesCube(bdd_state_vars);
    varT = Bdd::VariablesCube(bdd_prime_vars);
    varA = Bdd::VariablesCube(bdd_action_vars);
}

Bdd
SystemParser::nodeToBdd(const TiXmlNode *node)
{
    LACE_ME;
    if (leaf_type == mpq_type) return gmp_strict_threshold_d(nodeToMtbdd(node).GetMTBDD(), 0);
    else return nodeToMtbdd(node).BddStrictThreshold(0);
}

Mtbdd
SystemParser::nodeToMtbdd(const TiXmlNode* node)
{
    // Have we reached a leaf of the (MT)BDD?
    if (hasAttribute(node, "const_value")) {
        if (leaf_type == float_type) {
            return readDoubleAttribute(node, "const_value");
        } else if (leaf_type == simple_fraction_type) {
            return readSimpleFractionAttribute(node, "const_value");
        } else if (leaf_type == mpq_type) {
            return readMPQAttribute(node, "const_value");
        }
    }

    // The node references another node
    if (hasAttribute(node, "node_ref")) {
        const std::map<std::string, Mtbdd>::const_iterator it =
                build_table.find(readStringAttribute(node, "node_ref"));
        assert(it != build_table.end());
        return it->second;
    }

    // Proper internal node
    const TiXmlNode* child = node->FirstChild();

    // Must have internal id and a variable index
    assert(strcmp(child->Value(), "dd_node")==0);
    assert(hasAttribute(child, "id"));
    assert(hasAttribute(child, "index"));

    // Read the attributes of the node
    const std::string id = readStringAttribute(child, "id");
    const unsigned int index = readIntAttribute(child, "index");

    // Get the then-child of the node
    const TiXmlNode* then_node = child->FirstChild();
    assert(then_node != NULL);
    assert(strcmp(then_node->Value(), "dd_then") == 0);
    Mtbdd then_result = nodeToMtbdd(then_node);

    // Get the else-child of the node
    const TiXmlNode* else_node = then_node->NextSibling();
    assert(else_node != NULL);
    assert(strcmp(else_node->Value(), "dd_else") == 0);
    Mtbdd else_result = nodeToMtbdd(else_node);

    const Mtbdd result = var_to_mtbdd[index].Ite(then_result, else_result);

    if (build_table.find(id) != build_table.end()) {
        assert(build_table[id] == result);
    } else {
        build_table[id] = result;
    }

    return result;
}

Bdd
SystemParser::computeStateSpace(const Bdd& transitions, const Mtbdd& markov_transitions) const {
    LACE_ME;
    Bdd markovs;
    if (leaf_type == mpq_type) markovs = Bdd(gmp_strict_threshold_d(markov_transitions.GetMTBDD(), 0));
    else markovs = markov_transitions.BddStrictThreshold(0);

    // Compute all transitions
    const Bdd all_trans = transitions.ExistAbstract(varA) + markovs;

    // Take all transitions.
    Bdd states = Bdd::bddOne().RelNext(all_trans, varS*varT); // all "to" states
    states += all_trans.ExistAbstract(varT); // all "from" states
    return states;
}

}