#ifndef STAN_MCMC_HMC_HAMILTONIANS_SOFTABS_METRIC_HPP
#define STAN_MCMC_HMC_HAMILTONIANS_SOFTABS_METRIC_HPP

#include <stan/math/mix.hpp>
#include <stan/mcmc/hmc/hamiltonians/base_hamiltonian.hpp>
#include <stan/mcmc/hmc/hamiltonians/softabs_point.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/normal_distribution.hpp>

namespace stan {
namespace mcmc {

template <typename Model>
struct softabs_fun {
  const Model& model_;
  std::ostream* o_;

  softabs_fun(const Model& m, std::ostream* out) : model_(m), o_(out) {}

  template <typename T>
  T operator()(const Eigen::Matrix<T, Eigen::Dynamic, 1>& x) const {
    // log_prob() requires non-const but doesn't modify its argument
    return model_.template log_prob<true, true, T>(
        const_cast<Eigen::Matrix<T, -1, 1>&>(x), o_);
  }
};

// Riemannian manifold with SoftAbs metric
template <class Model, class BaseRNG>
class softabs_metric : public base_hamiltonian<Model, softabs_point, BaseRNG> {
 private:
  typedef typename stan::math::index_type<Eigen::VectorXd>::type idx_t;

 public:
  explicit softabs_metric(const Model& model)
      : base_hamiltonian<Model, softabs_point, BaseRNG>(model) {}

  double T(softabs_point& z) { return this->tau(z) + 0.5 * z.log_det_metric; }

  double tau(softabs_point& z) {
    Eigen::VectorXd Qp = z.eigen_deco.eigenvectors().transpose() * z.p;
    return 0.5 * Qp.transpose() * z.softabs_lambda_inv.cwiseProduct(Qp);
  }

  double phi(softabs_point& z) { return this->V(z) + 0.5 * z.log_det_metric; }

  double dG_dt(softabs_point& z, callbacks::logger& logger) {
    return 2 * T(z) - z.q.dot(dtau_dq(z, logger) + dphi_dq(z, logger));
  }

  Eigen::VectorXd dtau_dq(softabs_point& z, callbacks::logger& logger) {
    Eigen::VectorXd a = z.softabs_lambda_inv.cwiseProduct(
        z.eigen_deco.eigenvectors().transpose() * z.p);
    Eigen::MatrixXd A
        = a.asDiagonal() * z.eigen_deco.eigenvectors().transpose();
    Eigen::MatrixXd B = z.pseudo_j.selfadjointView<Eigen::Lower>() * A;
    Eigen::MatrixXd C = A.transpose() * B;

    Eigen::VectorXd b(z.q.size());
    stan::math::grad_tr_mat_times_hessian(softabs_fun<Model>(this->model_, 0),
                                          z.q, C, b);

    return 0.5 * b;
  }

  Eigen::VectorXd dtau_dp(softabs_point& z) {
    return z.eigen_deco.eigenvectors()
           * z.softabs_lambda_inv.cwiseProduct(
                 z.eigen_deco.eigenvectors().transpose() * z.p);
  }

  Eigen::VectorXd dphi_dq(softabs_point& z, callbacks::logger& logger) {
    Eigen::VectorXd a
        = z.softabs_lambda_inv.cwiseProduct(z.pseudo_j.diagonal());
    Eigen::MatrixXd A
        = a.asDiagonal() * z.eigen_deco.eigenvectors().transpose();
    Eigen::MatrixXd B = z.eigen_deco.eigenvectors() * A;

    stan::math::grad_tr_mat_times_hessian(softabs_fun<Model>(this->model_, 0),
                                          z.q, B, a);

    return -0.5 * a + z.g;
  }

  void sample_p(softabs_point& z, BaseRNG& rng) {
    boost::variate_generator<BaseRNG&, boost::normal_distribution<> >
        rand_unit_gaus(rng, boost::normal_distribution<>());

    Eigen::VectorXd a(z.p.size());

    for (idx_t n = 0; n < z.p.size(); ++n)
      a(n) = sqrt(z.softabs_lambda(n)) * rand_unit_gaus();

    z.p = z.eigen_deco.eigenvectors() * a;
  }

  void init(softabs_point& z, callbacks::logger& logger) {
    update_metric(z, logger);
    update_metric_gradient(z, logger);
  }

  void update_metric(softabs_point& z, callbacks::logger& logger) {
    math::hessian<softabs_fun<Model> >(softabs_fun<Model>(this->model_, 0), z.q,
                                       z.V, z.g, z.hessian);
    z.V = -z.V;
    z.g = -z.g;
    z.hessian = -z.hessian;
    // Compute the eigen decomposition of the Hessian,
    // then perform the SoftAbs transformation
    z.eigen_deco.compute(z.hessian);

    for (idx_t i = 0; i < z.q.size(); ++i) {
      double lambda = z.eigen_deco.eigenvalues()(i);
      double alpha_lambda = z.alpha * lambda;

      double softabs_lambda = 0;

      // Thresholds defined such that the approximation
      // error is on the same order of double precision
      if (std::fabs(alpha_lambda) < lower_softabs_thresh) {
        softabs_lambda
            = (1.0 + (1.0 / 3.0) * alpha_lambda * alpha_lambda) / z.alpha;
      } else if (std::fabs(alpha_lambda) > upper_softabs_thresh) {
        softabs_lambda = std::fabs(lambda);
      } else {
        softabs_lambda = lambda / std::tanh(alpha_lambda);
      }

      z.softabs_lambda(i) = softabs_lambda;
      z.softabs_lambda_inv(i) = 1.0 / softabs_lambda;
    }

    // Compute the log determinant of the metric
    z.log_det_metric = 0;
    for (idx_t i = 0; i < z.q.size(); ++i)
      z.log_det_metric += std::log(z.softabs_lambda(i));
  }

  void update_metric_gradient(softabs_point& z, callbacks::logger& logger) {
    // Compute the pseudo-Jacobian of the SoftAbs transform
    for (idx_t i = 0; i < z.q.size(); ++i) {
      for (idx_t j = 0; j <= i; ++j) {
        double delta
            = z.eigen_deco.eigenvalues()(i) - z.eigen_deco.eigenvalues()(j);

        if (std::fabs(delta) < jacobian_thresh) {
          double lambda = z.eigen_deco.eigenvalues()(i);
          double alpha_lambda = z.alpha * lambda;

          // Thresholds defined such that the approximation
          // error is on the same order of double precision
          if (std::fabs(alpha_lambda) < lower_softabs_thresh) {
            z.pseudo_j(i, j)
                = (2.0 / 3.0) * alpha_lambda
                  * (1.0 - (2.0 / 15.0) * alpha_lambda * alpha_lambda);
          } else if (std::fabs(alpha_lambda) > upper_softabs_thresh) {
            z.pseudo_j(i, j) = lambda > 0 ? 1 : -1;
          } else {
            double sdx = std::sinh(alpha_lambda) / lambda;
            z.pseudo_j(i, j)
                = (z.softabs_lambda(i) - z.alpha / (sdx * sdx)) / lambda;
          }
        } else {
          z.pseudo_j(i, j)
              = (z.softabs_lambda(i) - z.softabs_lambda(j)) / delta;
        }
      }
    }
  }

  void update_gradients(softabs_point& z, callbacks::logger& logger) {
    update_metric_gradient(z, logger);
  }

  // Threshold below which a power series
  // approximation of the softabs function is used
  static double lower_softabs_thresh;

  // Threshold above which an asymptotic
  // approximation of the softabs function is used
  static double upper_softabs_thresh;

  // Threshold below which an exact derivative is
  // used in the Jacobian calculation instead of
  // finite differencing
  static double jacobian_thresh;
};

template <class Model, class BaseRNG>
double softabs_metric<Model, BaseRNG>::lower_softabs_thresh = 1e-4;

template <class Model, class BaseRNG>
double softabs_metric<Model, BaseRNG>::upper_softabs_thresh = 18;

template <class Model, class BaseRNG>
double softabs_metric<Model, BaseRNG>::jacobian_thresh = 1e-10;
}  // namespace mcmc
}  // namespace stan
#endif


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "test_random"

#include <boost/test/unit_test.hpp>
#include "nanocv/math/random.hpp"

BOOST_AUTO_TEST_CASE(test_random)
{
        using namespace ncv;

        const size_t tests = 1024;
        const size_t test_size = 1024;

        for (size_t t = 0; t < tests; t ++)
        {
                const int32_t min = 17 + t;
                const int32_t max = min + t * 25 + 4;

                // initialize (uniform) random number generator
                ncv::random_t<int32_t> rgen(min, max);

                // check generator
                for (size_t tt = 0; tt < test_size; tt ++)
                {
                        const int32_t v = rgen();
                        BOOST_CHECK_GE(v, min);
                        BOOST_CHECK_LE(v, max);
                }
        }
}


#ifndef PRECOMPUTED_HPP
#define PRECOMPUTED_HPP

#include <boost/array.hpp>
// Precompute OceanMesh element type (as function of element dimension, and
// number of vertices) at compile time, using template metaprogramming
// to emulate a switch statement. See :
// http://ubiety.uwaterloo.ca/~tveldhui/papers/Template-Metaprograms/meta-art.html

// Class declarations -- n: number of vertices, d: dimension
template<int n, unsigned int d>
class OceanMesh_TYPE {
public:
  static inline int value() { return 0; }
};

template<>			// point
class OceanMesh_TYPE<1, 0> {
public:
  static inline int value() { return 15; }
};

template<>			// line
class OceanMesh_TYPE<2, 1> {
public:
  static inline int value() { return 1; }
};

template<>			// triangle
class OceanMesh_TYPE<3, 2> {
public:
  static inline int value() { return 2; }
};

template<>			// quadrangle
class OceanMesh_TYPE<4, 2> {
public:
  static inline int value() { return 3; }
};

template<>			// tetrahedron
class OceanMesh_TYPE<4, 3> {
public:
  static inline int value() { return 4; }
};

template<>			// hexahedron
class OceanMesh_TYPE<8, 3> {
public:
  static inline int value() { return 5; }
};

// To get the OceanMesh type for an element: OceanMesh_TYPE<N, d>::value().
// Examples : OceanMesh_TYPE<3, 2>::value() for a triangle, OceanMesh_TYPE<8, 3>
// for an hexahedron.

template<int n, unsigned int d>
class OceanMesh_FACE_TYPE {
public:
  static inline int value() { return n-1; }
};

template<>
class OceanMesh_FACE_TYPE<2, 1> {
public:				// line --> point
  static inline int value() { return 15; }
};

template<>
class OceanMesh_FACE_TYPE<3, 2> {
public:				// triangle --> line
  static inline int value() { return 1; }
};

template<>			// quadrangle --> line
class OceanMesh_FACE_TYPE<4, 2> {
public:
  static inline int value() { return 1; }
};

template<>			// tetrahedron --> triangle
class OceanMesh_FACE_TYPE<4, 3> {
public:
  static inline int value() { return 2; }
};

template<>			// hexahedron --> quadrangle
class OceanMesh_FACE_TYPE<8, 3> {
public:
  static inline int value() { return 4; }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

template<int n, unsigned int d>
class OceanMesh_FACE_LENGTH {
public:
  static inline int value() { return n-1; }
};

template<>
class OceanMesh_FACE_LENGTH<2, 1> {
public:				// line --> point
  static inline int value() { return 0; }
};

template<>
class OceanMesh_FACE_LENGTH<3, 2> {
public:				// triangle --> line
  static inline int value() { return 2; }
};

template<>			// quadrangle --> line
class OceanMesh_FACE_LENGTH<4, 2> {
public:
  static inline int value() { return 2; }
};

template<>			// tetrahedron --> triangle
class OceanMesh_FACE_LENGTH<4, 3> {
public:
  static inline int value() { return 3; }
};

template<>			// hexahedron --> quadrangle
class OceanMesh_FACE_LENGTH<8, 3> {
public:
  static inline int value() { return 4; }
};

///////////////////////////////////////////////////////////////////////////////

// Precompute gmsh element type (as function of element dimension, and
// number of vertices) at compile time, using template metaprogramming
// to emulate a switch statement. See :
// http://ubiety.uwaterloo.ca/~tveldhui/papers/Template-Metaprograms/meta-art.html

// Class declarations -- n: number of vertices, d: dimension
template<int n, unsigned int d>
class GMSH_TYPE {
public:
  static inline int value() { return 0; }
};

template<>			// point
class GMSH_TYPE<1, 0> {
public:
  static inline int value() { return 15; }
};

template<>			// line
class GMSH_TYPE<2, 1> {
public:
  static inline int value() { return 1; }
};

template<>			// triangle
class GMSH_TYPE<3, 2> {
public:
  static inline int value() { return 2; }
};

template<>			// quadrangle
class GMSH_TYPE<4, 2> {
public:
  static inline int value() { return 3; }
};

template<>			// tetrahedron
class GMSH_TYPE<4, 3> {
public:
  static inline int value() { return 4; }
};

template<>			// hexahedron
class GMSH_TYPE<8, 3> {
public:
  static inline int value() { return 5; }
};

// To get the gmsh type for an element: GMSH_TYPE<N, d>::value().
// Examples : GMSH_TYPE<3, 2>::value() for a triangle, GMSH_TYPE<8, 3>
// for an hexahedron.

template<int n, unsigned int d>
class GMSH_FACE_TYPE {
public:
  static inline int value() { return n-1; }
};

template<>
class GMSH_FACE_TYPE<2, 1> {
public:				// line --> point
  static inline int value() { return 15; }
};

template<>
class GMSH_FACE_TYPE<3, 2> {
public:				// triangle --> line
  static inline int value() { return 1; }
};

template<>			// quadrangle --> line
class GMSH_FACE_TYPE<4, 2> {
public:
  static inline int value() { return 1; }
};

template<>			// tetrahedron --> triangle
class GMSH_FACE_TYPE<4, 3> {
public:
  static inline int value() { return 2; }
};

template<>			// hexahedron --> quadrangle
class GMSH_FACE_TYPE<8, 3> {
public:
  static inline int value() { return 4; }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

template<int n, unsigned int d>
class GMSH_FACE_LENGTH {
public:
  static inline int value() { return n-1; }
};

template<>
class GMSH_FACE_LENGTH<2, 1> {
public:				// line --> point
  static inline int value() { return 0; }
};

template<>
class GMSH_FACE_LENGTH<3, 2> {
public:				// triangle --> line
  static inline int value() { return 2; }
};

template<>			// quadrangle --> line
class GMSH_FACE_LENGTH<4, 2> {
public:
  static inline int value() { return 2; }
};

template<>			// tetrahedron --> triangle
class GMSH_FACE_LENGTH<4, 3> {
public:
  static inline int value() { return 3; }
};

template<>			// hexahedron --> quadrangle
class GMSH_FACE_LENGTH<8, 3> {
public:
  static inline int value() { return 4; }
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Precompute VTK element type (as function of element dimension, and
// number of vertices) at compile time, using template metaprogramming
// dark magic (emulating a switch statement)
// http://ubiety.uwaterloo.ca/~tveldhui/papers/Template-Metaprograms/meta-art.html

// Class declarations -- n: number of vertices, d: dimension
template<int n, unsigned int d>
class VTK_TYPE {
public:
  static inline int value() { return 0; }
};

template<>			// point
class VTK_TYPE<1, 2> {
public:
  static inline int value() { return 1; }
};

template<>			// line
class VTK_TYPE<2, 2> {
public:
  static inline int value() { return 3; }
};

template<>			// triangle
class VTK_TYPE<3, 2> {
public:
  static inline int value() { return 5; }
};

template<>			// quadrangle
class VTK_TYPE<4, 2> {
public:
  static inline int value() { return 9; }
};

template<>			// tetrahedron
class VTK_TYPE<4, 3> {
public:
  static inline int value() { return 10; }
};

template<>			// hexahedron
class VTK_TYPE<8, 3> {
public:
  static inline int value() { return 12; }
};

template<>			// prism (aka wedge in vtk lingo)
class VTK_TYPE<6, 3> {
public:
  static inline int value() { return 13; }
};

template<>			// pyramid
class VTK_TYPE<5, 3> {
public:
  static inline int value() { return 14; }
};

// To get the vtk type for an element: VTK_TYPE<N, d>::value(),
// where N is the number of vertices, and d the dimension (for
// instance, VTK_TYPE<3, 2>::value() for a triangle, or
// VTK_TYPE<8, 3> for an hexahedron).

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Precompute METIS element type (as function of element dimension, and
// number of vertices) at compile time, using template metaprogramming
// dark magic (emulating a switch statement)
// http://ubiety.uwaterloo.ca/~tveldhui/papers/Template-Metaprograms/meta-art.html

// Class declarations -- n: number of vertices, d: dimension
template<int n, unsigned int d>
class METIS_TYPE {
public:
  static inline int value() { return 0; }
};

template<>			// triangle
class METIS_TYPE<3, 2> {
public:
  static inline int value() { return 1; }
};

template<>			// quadrangle
class METIS_TYPE<4, 2> {
public:
  static inline int value() { return 4; }
};

template<>			// tetrahedron
class METIS_TYPE<4, 3> {
public:
  static inline int value() { return 2; }
};

template<>			// hexahedron
class METIS_TYPE<8, 3> {
public:
  static inline int value() { return 3; }
};

// To get the vtk type for an element: METIS_TYPE<N, d>::value(),
// where N is the number of vertices, and d the dimension (for
// instance, METIS_TYPE<3, 2>::value() for a triangle, or
// METIS_TYPE<8, 3> for an hexahedron).

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Precompute elements facets -- return a serialized list of facet indices

// Class declarations -- n: number of vertices, d: dimension
template<size_t N, unsigned int d>
class ELEM_FACETS_INDICES {
public:
  static inline boost::array<int, N> value() {
    boost::array<int, N> foo;
    return foo; }
};

template<>			// triangle
class ELEM_FACETS_INDICES<3, 2> {
public:
  static inline boost::array<int, 6> value() {
    boost::array<int, 6> result =
      {{ 0, 1, 1, 2, 2, 0 }};
    return result;
  }
};

template<>			// quadrangle
class ELEM_FACETS_INDICES<4, 2> {
public:
  static inline boost::array<int, 8> value() {
    boost::array<int, 8> result =
      {{ 0, 1, 1, 2, 2, 3, 3, 0 }};
    return result;
  }
};

template<>			// tetrahedron
class ELEM_FACETS_INDICES<4, 3> {
public:
  static inline boost::array<int, 12> value() {
    boost::array<int, 12> result =
      {{ 0, 1, 2, 0, 3, 2, 0, 3, 1, 1, 2, 3}};
    return result;
  }
};

template<>			// hexahedron
class ELEM_FACETS_INDICES<8, 3> {
public:
  static inline boost::array<int, 24> value() {
    boost::array<int, 24> result  =
      { { 0, 3, 2, 1, 0, 3, 5, 4, 0, 4, 7, 3,
	  1, 2, 6, 5, 2, 3, 7, 6, 4, 5, 6, 7 }};
    return result;
  }
};

#endif // PRECOMPUTED_HPP


/**
 * \file dcs/testbed/albano2013v5_fuzzyqe_application_manager.hpp
 *
 * \brief Application manager based on a variation of the work by (Albano et al., 2013)
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright 2014   Marco Guazzone (marco.guazzone@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DCS_TESTBED_ALBANO2013V5_FUZZYQE_APPLICATION_MANAGER_HPP
#define DCS_TESTBED_ALBANO2013V5_FUZZYQE_APPLICATION_MANAGER_HPP

#include <boost/smart_ptr.hpp>
#include <cmath>
#include <cstddef>
#include <dcs/assert.hpp>
#include <dcs/debug.hpp>
#include <dcs/exception.hpp>
#include <dcs/logging.hpp>
#include <dcs/math/traits/float.hpp>
#include <dcs/testbed/application_performance_category.hpp>
#include <dcs/testbed/base_application_manager.hpp>
#include <dcs/testbed/data_estimators.hpp>
#include <dcs/testbed/data_smoothers.hpp>
#include <dcs/testbed/virtual_machine_performance_category.hpp>
#include <fl/Headers.h>
#include <fstream>
#include <limits>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

namespace dcs { namespace testbed {

/**
 * \brief Application Manager based on the work by (Albano et al., 2013)
 *
 * This class implements the Fuzzy-Q&E fuzzy controller proposed in [1].
 *
 * References:
 * -# L. Albano, C. Anglano, M. Canonico, and M. Guazzone,
 *    "Fuzzy-Q&E: achieving QoS guarantees and energy savings for cloud applications with fuzzy control,"
 *    Proc. of the 3rd International Conference on Cloud and Green Computing (CGC 2013), 2013.
 * .
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 */
template <typename TraitsT>
class albano2013v5_fuzzyqe_application_manager: public base_application_manager<TraitsT>
{
	private: typedef base_application_manager<TraitsT> base_type;
	public: typedef typename base_type::traits_type traits_type;
	public: typedef typename traits_type::real_type real_type;
	private: typedef typename base_type::app_type app_type;
	private: typedef typename base_type::app_pointer app_pointer;
	private: typedef typename base_type::vm_identifier_type vm_identifier_type;
	private: typedef typename app_type::sensor_type sensor_type;
	private: typedef typename app_type::sensor_pointer sensor_pointer;
	private: typedef ::std::map<application_performance_category,sensor_pointer> out_sensor_map;
	private: typedef ::std::map<virtual_machine_performance_category,::std::map<vm_identifier_type,sensor_pointer> > in_sensor_map;

	private: static const ::std::string rgain_fuzzy_var_name;
	private: static const ::std::string cres_fuzzy_var_name;
	private: static const ::std::string deltac_fuzzy_var_name;

	public: albano2013v5_fuzzyqe_application_manager()
	: beta_(0.9),
	  p_fuzzy_eng_(new fl::Engine()),
	  ctl_count_(0),
	  ctl_skip_count_(0),
	  ctl_fail_count_(0)
	{
		init();
	}

	public: void smoothing_factor(real_type value)
	{
		beta_ = value;
	}

	public: real_type smoothing_factor() const
	{
		return beta_;
	}

	public: void export_data_to(::std::string const& fname)
	{
		dat_fname_ = fname;
	}

	private: void init()
	{
		DCS_DEBUG_ASSERT( p_fuzzy_eng_ );

		fl::InputVariable* p_iv = 0;

		p_iv = new fl::InputVariable();
		p_iv->setEnabled(true);
		p_iv->setName(cres_fuzzy_var_name);
		p_iv->setRange(0.0, 1.0);
		p_iv->addTerm(new fl::Ramp("LOW", 0.25, 0.10));
		p_iv->addTerm(new fl::Triangle("FINE", 0.10, 0.25, 0.40));
		p_iv->addTerm(new fl::Ramp("HIGH", 0.25, 0.65));
		p_fuzzy_eng_->addInputVariable(p_iv);

		p_iv = new fl::InputVariable();
		p_iv->setEnabled(true);
		p_iv->setName(rgain_fuzzy_var_name);
		p_iv->setRange(-1, 1);
		p_iv->addTerm(new fl::Ramp("LOW", 0.10, -0.40));
		p_iv->addTerm(new fl::Triangle("FINE", -0.10, 0.10, 0.30));
		p_iv->addTerm(new fl::Ramp("HIGH", 0.10, 0.65));
		p_fuzzy_eng_->addInputVariable(p_iv);

		fl::OutputVariable* p_ov = 0;
		p_ov = new fl::OutputVariable();
		p_ov->setEnabled(true);
		p_ov->setName(deltac_fuzzy_var_name);
		p_ov->setRange(-1, 1);
#if defined(FL_VERSION) // Until fuzzylite v. 5.x (the FL_VERSION macro was removed since fuzzylite 6.x)
		p_ov->fuzzyOutput()->setAccumulation(new fl::AlgebraicSum());
#else // Since fuzzylite v. 6.x
		p_ov->fuzzyOutput()->setAggregation(new fl::AlgebraicSum());
#endif // FL_VERSION
		p_ov->setDefuzzifier(new fl::Centroid());
		p_ov->setDefaultValue(fl::nan);
		p_ov->setPreviousOutputValue(false);
		p_ov->addTerm(new fl::Triangle("BDW", -1.00, -0.55, -0.10));
		p_ov->addTerm(new fl::Triangle("DWN", -0.20, -0.125, -0.05));
		p_ov->addTerm(new fl::Triangle("STY", -0.10, 0.0, 0.10));
		p_ov->addTerm(new fl::Triangle("UP", 0.05, 0.125, 0.20));
		p_ov->addTerm(new fl::Triangle("BUP", 0.10, 0.55, 1.00));
		p_fuzzy_eng_->addOutputVariable(p_ov);

		fl::RuleBlock* p_rules = new fl::RuleBlock();
		p_rules->setEnabled(true);
		p_rules->setConjunction(new fl::Minimum());
		p_rules->setDisjunction(new fl::Maximum());
		p_rules->setActivation(new fl::AlgebraicProduct());
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is LOW and " + rgain_fuzzy_var_name + " is LOW then " + deltac_fuzzy_var_name + " is BUP", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is LOW and " + rgain_fuzzy_var_name + " is FINE then " + deltac_fuzzy_var_name + " is UP", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is LOW and " + rgain_fuzzy_var_name + " is HIGH then " + deltac_fuzzy_var_name + " is UP", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is FINE and " + rgain_fuzzy_var_name + " is LOW then " + deltac_fuzzy_var_name + " is UP", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is FINE and " + rgain_fuzzy_var_name + " is FINE then " + deltac_fuzzy_var_name + " is STY", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is FINE and " + rgain_fuzzy_var_name + " is HIGH then " + deltac_fuzzy_var_name + " is DWN", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is HIGH and " + rgain_fuzzy_var_name + " is LOW then " + deltac_fuzzy_var_name + " is STY", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is HIGH and " + rgain_fuzzy_var_name + " is FINE then " + deltac_fuzzy_var_name + " is DWN", p_fuzzy_eng_.get()));
		p_rules->addRule(fl::Rule::parse("if " + cres_fuzzy_var_name + " is HIGH and " + rgain_fuzzy_var_name + " is HIGH then " + deltac_fuzzy_var_name + " is BDW", p_fuzzy_eng_.get()));
		p_fuzzy_eng_->addRuleBlock(p_rules);
	}

	private: void do_reset()
	{
		typedef typename base_type::target_value_map::const_iterator target_iterator;
		typedef typename app_type::vm_pointer vm_pointer;

		const ::std::vector<vm_pointer> vms = this->app().vms();
		const ::std::size_t nvms = this->app().num_vms();

		// Reset output sensors
		out_sensors_.clear();
		const target_iterator tgt_end_it = this->target_values().end();
		for (target_iterator tgt_it = this->target_values().begin();
			 tgt_it != tgt_end_it;
			 ++tgt_it)
		{
			const application_performance_category cat = tgt_it->first;

			out_sensors_[cat] = this->app().sensor(cat);
		}

		// Reset input sensors
		in_sensors_.clear();
		for (::std::size_t i = 0; i < nvms; ++i)
		{
			const virtual_machine_performance_category cat = cpu_util_virtual_machine_performance;
			vm_pointer p_vm = vms[i];

			in_sensors_[cat][p_vm->id()] = p_vm->sensor(cat);
		}

		// Reset counters
		ctl_count_ = ctl_skip_count_
				   = ctl_fail_count_
				   = 0;

		// Reset fuzzy controller
		p_fuzzy_eng_->restart();

		//// Reset Cres estimator and smoother
		//this->data_estimator(cpu_util_virtual_machine_performance).reset();
		//this->data_smoother(cpu_util_virtual_machine_performance).reset();
		for (::std::size_t i = 0; i < nvms; ++i)
		{
			this->data_smoother(cpu_util_virtual_machine_performance, vms[i]->id(), ::boost::make_shared< testbed::brown_single_exponential_smoother<real_type> >(beta_));
			//this->data_estimator(cpu_util_virtual_machine_performance, vms[i]->id(), ::boost::make_shared< testbed::mean_estimator<real_type> >());
		}

		// Reset output data file
		if (p_dat_ofs_ && p_dat_ofs_->is_open())
		{
			p_dat_ofs_->close();
		}
		p_dat_ofs_.reset();
		if (!dat_fname_.empty())
		{
			p_dat_ofs_ = ::boost::make_shared< ::std::ofstream >(dat_fname_.c_str());
			if (!p_dat_ofs_->good())
			{
				::std::ostringstream oss;
				oss << "Cannot open output data file '" << dat_fname_ << "'";

				DCS_EXCEPTION_THROW(::std::runtime_error, oss.str());
			}

			*p_dat_ofs_ << "\"ts\"";

			for (::std::size_t i = 0; i < nvms; ++i)
			{
				*p_dat_ofs_ << ",\"Cap_{" << i << "}\",\"Share_{" << i << "}\"";
			}
			for (target_iterator tgt_it = this->target_values().begin();
				 tgt_it != tgt_end_it;
				 ++tgt_it)
			{
				const application_performance_category cat = tgt_it->first;

				*p_dat_ofs_ << ",\"y_{" << cat << "}\",\"yn_{" << cat << "}\",\"r_{" << cat << "}\"";
			}
			*p_dat_ofs_ << ",\"# Controls\",\"# Skip Controls\",\"# Fail Controls\"";
			*p_dat_ofs_ << ::std::endl;
		}
	}

	private: void do_sample()
	{
		typedef typename in_sensor_map::const_iterator in_sensor_iterator;
		typedef typename out_sensor_map::const_iterator out_sensor_iterator;
		typedef ::std::vector<typename sensor_type::observation_type> obs_container;
		typedef typename obs_container::const_iterator obs_iterator;

		DCS_DEBUG_TRACE("(" << this << ") BEGIN Do SAMPLE - Count: " << ctl_count_ << "/" << ctl_skip_count_ << "/" << ctl_fail_count_);

		// Collect input values
		const in_sensor_iterator in_sens_end_it = in_sensors_.end();
		for (in_sensor_iterator in_sens_it = in_sensors_.begin();
			 in_sens_it != in_sens_end_it;
			 ++in_sens_it)
		{
			const virtual_machine_performance_category cat = in_sens_it->first;

			//const ::std::size_t n = in_sens_it->second.size();
			//for (::std::size_t i = 0; i < n; ++i)
			//{
			//	sensor_pointer p_sens = in_sens_it->second.at(i);
			const typename in_sensor_map::mapped_type::const_iterator vm_end_it = in_sens_it->second.end();
			for (typename in_sensor_map::mapped_type::const_iterator vm_it = in_sens_it->second.begin();
				 vm_it != vm_end_it;
				 ++vm_it)
			{
				const vm_identifier_type vm_id = vm_it->first;
				sensor_pointer p_sens = vm_it->second;

				// check: p_sens != null
				DCS_DEBUG_ASSERT( p_sens );

				p_sens->sense();
				if (p_sens->has_observations())
				{
					const obs_container obs = p_sens->observations();
					const obs_iterator end_it = obs.end();
					for (obs_iterator it = obs.begin();
						 it != end_it;
						 ++it)
					{
						//this->data_estimator(cat, vm_id).collect(it->value());
						this->data_smoother(cat, vm_id).smooth(it->value());
					}
				}
			}
		}

		// Collect output values
		const out_sensor_iterator out_sens_end_it = out_sensors_.end();
		for (out_sensor_iterator out_sens_it = out_sensors_.begin();
			 out_sens_it != out_sens_end_it;
			 ++out_sens_it)
		{
			const application_performance_category cat = out_sens_it->first;

			sensor_pointer p_sens = out_sens_it->second;

			// check: p_sens != null
			DCS_DEBUG_ASSERT( p_sens );

			p_sens->sense();
			if (p_sens->has_observations())
			{
				const obs_container obs = p_sens->observations();
				const obs_iterator end_it = obs.end();
				for (obs_iterator it = obs.begin();
					 it != end_it;
					 ++it)
				{
					this->data_estimator(cat).collect(it->value());
				}
			}
		}

		DCS_DEBUG_TRACE("(" << this << ") END Do SAMPLE - Count: " << ctl_count_ << "/" << ctl_skip_count_ << "/" << ctl_fail_count_);
	}

	private: void do_control()
	{
		typedef typename base_type::target_value_map::const_iterator target_iterator;
		typedef typename app_type::vm_pointer vm_pointer;

		DCS_DEBUG_TRACE("(" << this << ") BEGIN Do CONTROL - Count: " << ctl_count_ << "/" << ctl_skip_count_ << "/" << ctl_fail_count_);

		++ctl_count_;

		bool skip_ctl = false;

		::std::map<virtual_machine_performance_category,::std::vector<real_type> > cress;
		::std::map<application_performance_category,real_type> rgains;

		::std::vector<vm_pointer> vms = this->app().vms();
		const ::std::size_t nvms = vms.size();

		for (::std::size_t i = 0; i < nvms; ++i)
		{
			const virtual_machine_performance_category cat = cpu_util_virtual_machine_performance;
			const vm_pointer p_vm = vms[i];

//			if (this->data_estimator(cat, vms[i]->id()).count() > 0)
//			{
//				//const real_type uh = this->data_estimator(cat, p_vm->id()).estimate();
//				const real_type uh = this->data_smoother(cat, p_vm->id()).forecast(0);
//				const real_type c = p_vm->cpu_share();
//
//				cress[cat].push_back(c-uh);
//			}
//			else
//			{
//				// No observation collected during the last control interval
//				DCS_DEBUG_TRACE("No input observation collected during the last control interval -> Skip control");
//				skip_ctl = true;
//				break;
//			}
			const real_type uh = this->data_smoother(cat, p_vm->id()).forecast(0);
			const real_type c = p_vm->cpu_share();
			const real_type cres = c-uh;

			cress[cat].push_back(cres);
DCS_DEBUG_TRACE("VM " << p_vm->id() << " - Performance Category: " << cat << " - Uhat(k): " << uh << " - C(k): " << c << " -> Cres(k+1): " << cres << " (Relative Cres(k+1): " << cres/c << ")");//XXX
		}

		if (!skip_ctl)
		{
			const target_iterator tgt_end_it = this->target_values().end();
			for (target_iterator tgt_it = this->target_values().begin();
				 tgt_it != tgt_end_it;
				 ++tgt_it)
			{
				const application_performance_category cat(tgt_it->first);

				// Compute a summary statistics of collected observation
				if (this->data_estimator(cat).count() > 0)
				{
					const real_type yh = this->data_estimator(cat).estimate();
					const real_type yr = this->target_value(cat);

					switch (cat)
					{
						case response_time_application_performance:
							rgains[cat] = (yr-yh)/yr;
							break;
						case throughput_application_performance:
							rgains[cat] = (yh-yr)/yr;
							break;
					}
DCS_DEBUG_TRACE("APP Performance Category: " << cat << " - Yhat(k): " << yh << " - R: " << yr << " -> Rgain(k+1): " << rgains.at(cat));//XXX
				}
				else
				{
					// No observation collected during the last control interval
					DCS_DEBUG_TRACE("No output observation collected during the last control interval -> Skip control");
					skip_ctl = true;
					break;
				}
#ifdef DCSXX_TESTBED_EXP_APP_MGR_RESET_ESTIMATION_EVERY_INTERVAL
				this->data_estimator(cat).reset();
#endif // DCSXX_TESTBED_EXP_APP_MGR_RESET_ESTIMATION_EVERY_INTERVAL
			}
		}
        if (!skip_ctl)
        {
			//FIXME: actually we only handle SISO systems
			DCS_ASSERT(cress.size() == 1 && rgains.size() == 1,
					   DCS_EXCEPTION_THROW(::std::runtime_error,
					   "Only SISO system are currently managed"));

			// Perform fuzzy control
			bool ok = false;
			::std::vector<real_type> deltacs(nvms);
			try
			{
				for (::std::size_t i = 0; i < nvms; ++i)
				{
					vm_pointer p_vm = vms[i];

					const real_type cres = cress.begin()->second.at(i);
					const real_type rgain = rgains.begin()->second;

					p_fuzzy_eng_->setInputValue(cres_fuzzy_var_name, cres/p_vm->cpu_share());
					p_fuzzy_eng_->setInputValue(rgain_fuzzy_var_name, rgain);

					p_fuzzy_eng_->process();

					const real_type deltac = p_fuzzy_eng_->getOutputValue(deltac_fuzzy_var_name);

					deltacs[i] = deltac;
DCS_DEBUG_TRACE("VM " << vms[i]->id() << " -> DeltaC(k+1): " << deltacs.at(i));//XXX
				}

				ok = true;
			}
			catch (fl::Exception const& fe)
			{
				DCS_DEBUG_TRACE( "Caught exception: " << fe.what() );

				::std::ostringstream oss;
				oss << "Unable to compute optimal control: " << fe.what();
				::dcs::log_warn(DCS_LOGGING_AT, oss.str());
			}
			catch (::std::exception const& e)
			{
				DCS_DEBUG_TRACE( "Caught exception: " << e.what() );

				::std::ostringstream oss;
				oss << "Unable to compute optimal control: " << e.what();
				::dcs::log_warn(DCS_LOGGING_AT, oss.str());
			}

			// Apply fuzzy control results
			if (ok)
			{
				for (::std::size_t i = 0; i < nvms; ++i)
				{
					vm_pointer p_vm = vms[i];

					const real_type old_share = p_vm->cpu_share();
					const real_type new_share = ::std::max(::std::min(old_share+deltacs[i], 1.0), 0.0);

					DCS_DEBUG_TRACE("VM '" << p_vm->id() << "' - old-share: " << old_share << " - new-share: " << new_share);

					if (::std::isfinite(new_share) && !::dcs::math::float_traits<real_type>::essentially_equal(old_share, new_share))
					{
						p_vm->cpu_share(new_share);
DCS_DEBUG_TRACE("VM " << vms[i]->id() << " -> C(k+1): " << new_share);//XXX
					}
				}
DCS_DEBUG_TRACE("Optimal control applied");//XXX
			}
			else
			{
				++ctl_fail_count_;

				::std::ostringstream oss;
				oss << "Control not applied: failed to solve the control problem";
				::dcs::log_warn(DCS_LOGGING_AT, oss.str());
			}
		}
		else
		{
			++ctl_skip_count_;
		}

		// Export to file
		if (p_dat_ofs_)
		{
			*p_dat_ofs_ << ::std::time(0) << ",";
			for (::std::size_t i = 0; i < nvms; ++i)
			{
				const vm_pointer p_vm = vms[i];

				// check: p_vm != null
				DCS_DEBUG_ASSERT( p_vm );

				if (i != 0)
				{
					*p_dat_ofs_ << ",";
				}
				*p_dat_ofs_ << p_vm->cpu_cap() << "," << p_vm->cpu_share();
			}
			*p_dat_ofs_ << ",";
			const target_iterator tgt_end_it = this->target_values().end();
			for (target_iterator tgt_it = this->target_values().begin();
			tgt_it != tgt_end_it;
			++tgt_it)
			{
				const application_performance_category cat = tgt_it->first;

				if (tgt_it != this->target_values().begin())
				{
					*p_dat_ofs_ << ",";
				}
				const real_type yh = this->data_estimator(cat).estimate();
				const real_type yr = tgt_it->second;
				const real_type yn = yh/yr;
				*p_dat_ofs_ << yh << "," << yn << "," << yr;
			}
			*p_dat_ofs_ << "," << ctl_count_ << "," << ctl_skip_count_ << "," << ctl_fail_count_;
			*p_dat_ofs_ << ::std::endl;
		}

		DCS_DEBUG_TRACE("(" << this << ") END Do CONTROL - Count: " << ctl_count_ << "/" << ctl_skip_count_ << "/" << ctl_fail_count_);
	}

	private: real_type beta_; ///< The EWMA smoothing factor for Cres
	private: ::boost::shared_ptr<fl::Engine> p_fuzzy_eng_; ///< The fuzzy control engine
	private: ::std::size_t ctl_count_; ///< Number of times control function has been invoked
	private: ::std::size_t ctl_skip_count_; ///< Number of times control has been skipped
	private: ::std::size_t ctl_fail_count_; ///< Number of times control has failed
	private: in_sensor_map in_sensors_;
	private: out_sensor_map out_sensors_;
	private: ::std::string dat_fname_;
	private: ::boost::shared_ptr< ::std::ofstream > p_dat_ofs_;
}; // albano2013v5_fuzzyqe_application_manager

template <typename T>
const ::std::string albano2013v5_fuzzyqe_application_manager<T>::rgain_fuzzy_var_name = "Rgain";

template <typename T>
const ::std::string albano2013v5_fuzzyqe_application_manager<T>::cres_fuzzy_var_name = "Cres";

template <typename T>
const ::std::string albano2013v5_fuzzyqe_application_manager<T>::deltac_fuzzy_var_name = "DeltaC";

}} // Namespace dcs::testbed

#endif // DCS_TESTBED_ALBANO2013V5_FUZZYQE_APPLICATION_MANAGER_HPP


#pragma once
#include <Eigen/Dense>
#include <stdexcept>
#include <string>
#include <vector>

#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>

enum class Pauli : char
{
	I = 'I',
	X = 'X',
	Y = 'Y',
	Z = 'Z'
};

std::optional<Pauli> charToPauli(char c)
{
	switch(c)
	{
	case 'I':
		return Pauli::I;
	case 'X':
		return Pauli::X;
	case 'Y':
		return Pauli::Y;
	case 'Z':
		return Pauli::Z;
	}
	return {};
}

class PauliString
{
private:
	std::vector<std::pair<uint32_t, Pauli>> string_;

public:
	PauliString() { }

	PauliString(std::string str)
	{
		if(str.empty())
			return;
		while(true)
		{
			std::size_t pos = str.find(' ');
			size_t where;
			std::string token = str.substr(0, pos);
			where = atoi(token.c_str() + 1);
			auto p = charToPauli(token[0]);

			if(!p.has_value())
				throw std::invalid_argument("Pauli operator cannot be parsed");
			string_.emplace_back(where, p.value());
			if(pos != std::string::npos)
				str.erase(0, pos + 1);
			else
				break;
		}
	}

	uint32_t countX() const
	{
		uint32_t res = 0;
		for(const auto& p : string_)
		{
			if(p.second == Pauli::X)
			{
				++res;
			}
		}
		return res;
	}

	uint32_t countY() const
	{
		uint32_t res = 0;
		for(const auto& p : string_)
		{
			if(p.second == Pauli::Y)
			{
				++res;
			}
		}
		return res;
	}

	uint32_t countZ() const
	{
		uint32_t res = 0;
		for(const auto& p : string_)
		{
			if(p.second == Pauli::Z)
			{
				++res;
			}
		}
		return res;
	}

	/**
	 * Check whether the term is diagonal in the computational basis
	 */
	bool isDiagonal() const
	{
		for(const auto& p : string_)
		{
			if((p.second != Pauli::Z) && (p.second != Pauli::I))
			{
				return false;
			}
		}
		return true;
	}

	bool hasU1() const { return (countX() % 2 == 0) && (countY() % 2 == 0); }

	void add(uint32_t pos, Pauli p) { string_.emplace_back(pos, p); }

	std::pair<std::complex<double>, std::vector<int>>
	operator()(const Eigen::VectorXi& sigma) const
	{
		constexpr std::complex<int> I(0, 1);
		std::complex<int> r = 1;
		std::vector<int> toFlip;
		for(const auto p : string_)
		{
			switch(p.second)
			{
			case Pauli::I:
				break;
			case Pauli::X:
				toFlip.push_back(p.first);
				break;
			case Pauli::Y:
				toFlip.push_back(p.first);
				r *= sigma(p.first) * I;
				break;
			case Pauli::Z:
				r *= sigma(p.first);
				break;
			}
		}
		return std::make_pair(std::complex<double>(real(r), imag(r)), toFlip);
	}
	std::pair<std::complex<double>, uint32_t> operator()(uint32_t col) const
	{
		constexpr std::complex<int> I(0, 1);
		std::complex<int> r = 1;
		std::vector<int> toFlip;
		for(const auto p : string_)
		{
			int sgn = 1 - 2 * ((col >> p.first) & 1);
			switch(p.second)
			{
			case Pauli::I:
				break;
			case Pauli::X:
				col ^= (1 << p.first);
				break;
			case Pauli::Y:
				col ^= (1 << p.first);
				r *= sgn * I;
				break;
			case Pauli::Z:
				r *= sgn;
				break;
			}
		}
		return std::make_pair(std::complex<double>(real(r), imag(r)), col);
	}

	friend std::ostream& operator<<(std::ostream& os, const PauliString& ps)
	{
		const char* sep = "";
		for(const auto& p : ps.string_)
		{
			os << sep << static_cast<char>(p.second) << p.first;
			sep = " ";
		}
		return os;
	}
};

class PauliHamiltonian
{
private:
	uint32_t n_;
	uint32_t nup_; // only effective when the Hamiltonain has U1 symmetry.
	std::vector<std::pair<double, PauliString>> terms_;

public:
	PauliHamiltonian(uint32_t n, uint32_t jz = std::numeric_limits<uint32_t>::max())
		: n_{n}, nup_{jz}
	{
	}

	uint32_t getN() const { return n_; }

	PauliHamiltonian(const PauliHamiltonian&) = default;
	PauliHamiltonian(PauliHamiltonian&&) = default;

	PauliHamiltonian& operator=(const PauliHamiltonian&) = default;
	PauliHamiltonian& operator=(PauliHamiltonian&&) = default;

	nlohmann::json params() const
	{
		using nlohmann::json;
		json res;
		res["name"] = "PauliHamiltonian";
		res["n"] = n_;
		json terms = json::array();
		;
		for(auto [coeff, pauliString] : terms_)
		{
			std::ostringstream ss;
			ss << pauliString;
			std::string s = ss.str();
			terms.push_back({coeff, std::move(s)});
		}
		res["terms"] = terms;
		return res;
	}

	template<typename... Ts> void emplaceTerm(Ts&&... args)
	{
		terms_.emplace_back(std::forward<Ts>(args)...);
	}

	bool hasTermwiseU1() const
	{
		return std::all_of(terms_.begin(), terms_.end(),
		                   [](const std::pair<double, PauliString>& p)
		                   { return p.second.hasU1(); });
	}

	bool isReal() const
	{
		return std::all_of(terms_.begin(), terms_.end(),
		                   [](const std::pair<double, PauliString>& p)
		                   { return (p.second.countY() % 2) == 0; });
	}

	uint32_t getNup() const { return nup_; }

	static PauliHamiltonian fromFile(const std::filesystem::path& filePath)
	{
		std::ifstream fin(filePath);
		std::string line;
		std::getline(fin, line);
		uint32_t n;
		uint32_t m;
		std::istringstream ss(line);
		ss >> n >> m;

		PauliHamiltonian res(n, m);
		while(std::getline(fin, line))
		{
			auto to = line.find("\t");
			double coeff = stof(line.substr(0, to));
			PauliString s(line.substr(to + 1, std::string::npos));
			res.emplaceTerm(coeff, s);
		}
		return res;
	}

	template<typename State> typename State::Scalar operator()(const State& state) const
	{
		typename State::Scalar res = 0;
		for(auto [coeff, pauliString] : terms_)
		{
			const auto& [c, toFlip] = pauliString(state.getSigma());
			res += c * coeff * state.ratio(toFlip);
		}
		return res;
	}

	std::map<uint32_t, std::complex<double>> operator()(const uint32_t col) const
	{
		std::map<uint32_t, std::complex<double>> m;
		for(auto [coeff, pauliString] : terms_)
		{
			const auto& [c, s] = pauliString(col);
			m[s] += c * coeff;
		}
		return m;
	}

	friend PauliHamiltonian diagonalHamiltonian(const PauliHamiltonian& ph)
	{
		PauliHamiltonian res(ph.n_, ph.nup_);
		for(auto [coeff, pauliString] : ph.terms_)
		{
			if(pauliString.isDiagonal())
				res.emplaceTerm(coeff, pauliString);
		}
		return res;
	}
};


#include "mex.h"
#include <igl/matlab/parse_rhs.h>
#include <igl/matlab_format.h>
#include <igl/matlab/prepare_lhs.h>
#include <igl/matlab/validate_arg.h>
#include <igl/matlab/MexStream.h>
#include <igl/matlab/mexErrMsgTxt.h>
#include <igl/triangle/triangulate.h>
#include <igl/copyleft/cgal/triangulate.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <Eigen/Core>
#include <Eigen/Sparse>

void mexFunction(
  int          nlhs,
  mxArray      *plhs[],
  int          nrhs,
  const mxArray *prhs[])
{
  //mexPrintf("Compiled at %s on %s\n",__TIME__,__DATE__);
  using namespace igl;
  using namespace igl::matlab;
  using namespace Eigen;
  igl::matlab::MexStream mout;
  std::streambuf *outbuf = std::cout.rdbuf(&mout);

  Eigen::MatrixXd V;
  Eigen::MatrixXi E;
  Eigen::MatrixXd H;
  Eigen::VectorXi VM,EM;
  mexErrMsgTxt(nrhs>=2,"nrhs should be >= 2");
  parse_rhs_double(prhs+0,V);
  parse_rhs_index(prhs+1,E);

  std::string flags = "";
  bool verbose = false;
  enum MethodType
  {
    TRIANGLE,
    CGAL_EPECK,
    CGAL_EPICK,
  } method = TRIANGLE;

  {
    int i = 2;
    while(i<nrhs)
    {
      mexErrMsgTxt(mxIsChar(prhs[i]),"Parameter names should be strings");
      // Cast to char
      const char * name = mxArrayToString(prhs[i]);
      if(strcmp("Flags",name) == 0)
      {
        validate_arg_char(i,nrhs,prhs,name);
        flags = mxArrayToString(prhs[++i]);
      }else if(strcmp("EdgeMarkers",name) == 0)
      {
        validate_arg_double(i,nrhs,prhs,name);
        parse_rhs_index(prhs+(++i),EM);
      }else if(strcmp("Holes",name) == 0)
      {
        validate_arg_double(i,nrhs,prhs,name);
        parse_rhs_double(prhs+(++i),H);
      }else if(strcmp("VertexMarkers",name) == 0)
      {
        validate_arg_double(i,nrhs,prhs,name);
        parse_rhs_index(prhs+(++i),VM);
      }else if(strcmp("Method",name) == 0)
      {
        validate_arg_char(i,nrhs,prhs,name);
        const char * method_name = mxArrayToString(prhs[++i]);
        if(strcmp("triangle",method_name)==0)
        {
          method = TRIANGLE;
        }else if(strcmp("epeck",method_name)==0)
        {
          method = CGAL_EPECK;
#ifndef WITH_CGAL
          mexErrMsgTxt(false,"Recompile with cgal to enabel.");
#endif
        }else if(strcmp("epick",method_name)==0)
        {
          method = CGAL_EPICK;
#ifndef WITH_CGAL
          mexErrMsgTxt(false,"Recompile with cgal to enabel.");
#endif
        }else if(strcmp("cgal",method_name)==0)
        {
          method = CGAL_EPECK;
#ifndef WITH_CGAL
          mexErrMsgTxt(false,"Recompile with cgal to enabel.");
#endif
        }else
        {
          mexErrMsgTxt(false,C_STR("Unknown Method: "<<method_name));
        }
      }else
      {
        mexErrMsgTxt(false,"Unknown parameter");
      }
      i++;
    }
  }

  Eigen::MatrixXd TV;
  Eigen::MatrixXi TF;
  Eigen::MatrixXi TE;
  Eigen::VectorXi TVM,TEM;
  // triangle will annoyingly mark all boundary edges (e.g., "-c" flag) with 1.
  // Shift all the input labels so that min is at 2. Then we'll shift back and
  // mark boundaries with EM.maxCoeff()+1
  int shift,max_EM;
  if(EM.size())
  {
    shift = EM.minCoeff()+2;
    max_EM = EM.maxCoeff();
    EM.array() += shift;
  }
  switch(method)
  {
    default:
    case TRIANGLE:
    try
    {
      igl::triangle::triangulate(V,E,H,VM,EM,flags,TV,TF,TVM,TE,TEM);
    }catch(std::runtime_error e)
    {
      ::mexErrMsgTxt((std::string("triangle failed: ")+e.what()).c_str());
    }
    break;
    case CGAL_EPECK:
      igl::copyleft::cgal::triangulate<CGAL::Epeck>(V,E,H,flags.find('c')!=std::string::npos,TV,TF);
    case CGAL_EPICK:
      igl::copyleft::cgal::triangulate<CGAL::Epick>(V,E,H,flags.find('c')!=std::string::npos,TV,TF);
    break;
  }
  for(int e = 0;e<TEM.size();e++)
  {
    if(TEM(e) == 1){ TEM(e) = max_EM+1;}
    else{ TEM(e) -= shift;}
  }

  switch(nlhs)
  {
    case 5:
      prepare_lhs_index(TEM,plhs+4);
    case 4:
      prepare_lhs_index(TE,plhs+3);
    case 3:
      prepare_lhs_index(TVM,plhs+2);
    case 2:
      prepare_lhs_index(TF,plhs+1);
    case 1:
      prepare_lhs_double(TV,plhs+0);
    default:break;
  }
}