///////////////////////////////////////////////////////////////////////////////
// pot_tail_mean.hpp
//
//  Copyright 2006 Daniel Egloff, Olivier Gygi. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_ACCUMULATORS_STATISTICS_POT_TAIL_MEAN_HPP_DE_01_01_2006
#define BOOST_ACCUMULATORS_STATISTICS_POT_TAIL_MEAN_HPP_DE_01_01_2006

#include <vector>
#include <limits>
#include <numeric>
#include <functional>
#include <boost/range.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/numeric/functional.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/statistics_fwd.hpp>
#include <boost/accumulators/statistics/peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/weighted_peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/pot_quantile.hpp>
#include <boost/accumulators/statistics/tail_mean.hpp>

namespace boost { namespace accumulators
{

namespace impl
{
    ///////////////////////////////////////////////////////////////////////////////
    // pot_tail_mean_impl
    //
    /**
        @brief Estimation of the (coherent) tail mean based on the peaks over threshold method (for both left and right tails)

        Computes an estimate for the (coherent) tail mean
        \f[
            \widehat{CTM}_{\alpha} = \hat{q}_{\alpha} - \frac{\bar{\beta}}{\xi-1}(1-\alpha)^{-\xi},
        \f]
        where \f$\bar[u]\f$, \f$\bar{\beta}\f$ and \f$\xi\f$ are the parameters of the
        generalized Pareto distribution that approximates the right tail of the distribution (or the
        mirrored left tail, in case the left tail is used). In the latter case, the result is mirrored
        back, yielding the correct result.
    */
    template<typename Sample, typename Impl, typename LeftRight>
    struct pot_tail_mean_impl
      : accumulator_base
    {
        typedef typename numeric::functional::fdiv<Sample, std::size_t>::result_type float_type;
        // for boost::result_of
        typedef float_type result_type;

        pot_tail_mean_impl(dont_care)
          : sign_((is_same<LeftRight, left>::value) ? -1 : 1)
        {
        }

        template<typename Args>
        result_type result(Args const &args) const
        {
            typedef
                typename mpl::if_<
                    is_same<Impl, weighted>
                  , tag::weighted_peaks_over_threshold<LeftRight>
                  , tag::peaks_over_threshold<LeftRight>
                >::type
            peaks_over_threshold_tag;

            typedef
                typename mpl::if_<
                    is_same<Impl, weighted>
                  , tag::weighted_pot_quantile<LeftRight>
                  , tag::pot_quantile<LeftRight>
                >::type
            pot_quantile_tag;

            extractor<peaks_over_threshold_tag> const some_peaks_over_threshold = {};
            extractor<pot_quantile_tag> const some_pot_quantile = {};

            float_type beta_bar = some_peaks_over_threshold(args).template get<1>();
            float_type xi_hat   = some_peaks_over_threshold(args).template get<2>();

            return some_pot_quantile(args) - this->sign_ * beta_bar/( xi_hat - 1. ) * std::pow(
                is_same<LeftRight, left>::value ? args[quantile_probability] : 1. - args[quantile_probability]
              , -xi_hat);
        }
    private:
        short sign_; // if the fit parameters from the mirrored left tail extreme values are used, mirror back the result
    };
} // namespace impl

///////////////////////////////////////////////////////////////////////////////
// tag::pot_tail_mean
// tag::pot_tail_mean_prob
//
namespace tag
{
    template<typename LeftRight>
    struct pot_tail_mean
      : depends_on<peaks_over_threshold<LeftRight>, pot_quantile<LeftRight> >
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::pot_tail_mean_impl<mpl::_1, unweighted, LeftRight> impl;
    };
    template<typename LeftRight>
    struct pot_tail_mean_prob
      : depends_on<peaks_over_threshold_prob<LeftRight>, pot_quantile_prob<LeftRight> >
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::pot_tail_mean_impl<mpl::_1, unweighted, LeftRight> impl;
    };
    template<typename LeftRight>
    struct weighted_pot_tail_mean
      : depends_on<weighted_peaks_over_threshold<LeftRight>, weighted_pot_quantile<LeftRight> >
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::pot_tail_mean_impl<mpl::_1, weighted, LeftRight> impl;
    };
    template<typename LeftRight>
    struct weighted_pot_tail_mean_prob
      : depends_on<weighted_peaks_over_threshold_prob<LeftRight>, weighted_pot_quantile_prob<LeftRight> >
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::pot_tail_mean_impl<mpl::_1, weighted, LeftRight> impl;
    };
}

// pot_tail_mean<LeftRight>(with_threshold_value) -> pot_tail_mean<LeftRight>
template<typename LeftRight>
struct as_feature<tag::pot_tail_mean<LeftRight>(with_threshold_value)>
{
    typedef tag::pot_tail_mean<LeftRight> type;
};

// pot_tail_mean<LeftRight>(with_threshold_probability) -> pot_tail_mean_prob<LeftRight>
template<typename LeftRight>
struct as_feature<tag::pot_tail_mean<LeftRight>(with_threshold_probability)>
{
    typedef tag::pot_tail_mean_prob<LeftRight> type;
};

// weighted_pot_tail_mean<LeftRight>(with_threshold_value) -> weighted_pot_tail_mean<LeftRight>
template<typename LeftRight>
struct as_feature<tag::weighted_pot_tail_mean<LeftRight>(with_threshold_value)>
{
    typedef tag::weighted_pot_tail_mean<LeftRight> type;
};

// weighted_pot_tail_mean<LeftRight>(with_threshold_probability) -> weighted_pot_tail_mean_prob<LeftRight>
template<typename LeftRight>
struct as_feature<tag::weighted_pot_tail_mean<LeftRight>(with_threshold_probability)>
{
    typedef tag::weighted_pot_tail_mean_prob<LeftRight> type;
};

// for the purposes of feature-based dependency resolution,
// pot_tail_mean<LeftRight> and pot_tail_mean_prob<LeftRight> provide
// the same feature as tail_mean
template<typename LeftRight>
struct feature_of<tag::pot_tail_mean<LeftRight> >
  : feature_of<tag::tail_mean>
{
};

template<typename LeftRight>
struct feature_of<tag::pot_tail_mean_prob<LeftRight> >
  : feature_of<tag::tail_mean>
{
};

// So that pot_tail_mean can be automatically substituted
// with weighted_pot_tail_mean when the weight parameter is non-void.
template<typename LeftRight>
struct as_weighted_feature<tag::pot_tail_mean<LeftRight> >
{
    typedef tag::weighted_pot_tail_mean<LeftRight> type;
};

template<typename LeftRight>
struct feature_of<tag::weighted_pot_tail_mean<LeftRight> >
  : feature_of<tag::pot_tail_mean<LeftRight> >
{
};

// So that pot_tail_mean_prob can be automatically substituted
// with weighted_pot_tail_mean_prob when the weight parameter is non-void.
template<typename LeftRight>
struct as_weighted_feature<tag::pot_tail_mean_prob<LeftRight> >
{
    typedef tag::weighted_pot_tail_mean_prob<LeftRight> type;
};

template<typename LeftRight>
struct feature_of<tag::weighted_pot_tail_mean_prob<LeftRight> >
  : feature_of<tag::pot_tail_mean_prob<LeftRight> >
{
};

}} // namespace boost::accumulators

#endif


#include <pybind11/stl.h>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/numpy.h>

#include <functional>

#include <Eigen/Dense>

#include "logval.h"

namespace py = pybind11;

typedef LogVal<double> LogValD;
namespace Eigen {
    typedef Eigen::Matrix<LogValD, Dynamic, Dynamic> MatrixXlogd;
    typedef Eigen::Matrix<float, Dynamic, Dynamic> MatrixXf;

}

Eigen::MatrixXlogd to_log(const Eigen::MatrixXf& X) {
    // the one-liner unaryExpr fails on windows. Maybe for good reason?
    //return X.unaryExpr([](float f) { return LogValD((double) f, false); });
    Eigen::MatrixXlogd res(X.rows(), X.cols());
    for (py::ssize_t i = 0; i < X.rows(); ++i) {
        for (py::ssize_t j = 0; j < X.cols(); ++j) {
            res(i, j) = LogValD((double) X(i, j), false);
        }
    }
    return res;
}

class log_domain_lu {
public:
    log_domain_lu(const Eigen::MatrixXf& X): lu(to_log(X)) { }

    float logdet() {
        return (float) lu.determinant().logabs();
    }

    auto inv() {
        Eigen::MatrixXlogd Xinv = lu.inverse();
        Eigen::MatrixXf Xinvf = Xinv.cast<double>().cast<float>();
        return Xinvf;
    }

private:
    Eigen::FullPivLU<Eigen::MatrixXlogd> lu;
};

class batch_log_domain_lu {
public:
    batch_log_domain_lu(
        const py::array_t<float>& X,
        const std::vector<int>& lengths,
        const py::array_t<bool>& sign)
    : lengths{ lengths }
    {
        auto X_acc = X.unchecked<3>();

        // there are three possibilities for the sign:
        // ndim=0 means sign is the same everywhere.
        // ndim=2 means sign at k,i,j is sign(i,j)
        // ndim=3 means sign at k,i,j is sign(k,i,j)
        // Dynamically design a sign extraction function for ecah case.

        std::function<bool (py::ssize_t, py::ssize_t, py::ssize_t)> _sign;
        auto sign_buf = sign.request();

        if (sign_buf.ndim == 0) {
            auto sign_val = static_cast<bool*>(sign_buf.ptr)[0];
            _sign = [sign_val](py::ssize_t k, py::ssize_t i, py::ssize_t j) { return sign_val; };
        } else if (sign_buf.ndim == 2) {
            auto sign_2d = sign.unchecked<2>();
            _sign = [sign_2d](py::ssize_t k, py::ssize_t i, py::ssize_t j) { return sign_2d(i, j); };
        } else if (sign_buf.ndim == 3) {
            auto sign_3d = sign.unchecked<3>();
            _sign = [sign_3d](py::ssize_t k, py::ssize_t i, py::ssize_t j) { return sign_3d(k, i, j); };
        } else {
            std::runtime_error("wrong dimension");
        }

        batch_size = X_acc.shape(0);
        dim1 = X_acc.shape(1);
        dim2 = X_acc.shape(2);

        for (py::ssize_t k = 0; k < batch_size; ++k) {

            auto dk = lengths[k];

            // pass 1. extract min
            float xkmax = X_acc(k, 0, 0);
            for (py::ssize_t i = 0; i < dk; ++i) {
                for (py::ssize_t j = 0; j < dk; ++j) {
                    xkmax = static_cast<double>(std::max(xkmax, X_acc(k, i, j)));
                }
            }

            xmax.push_back(xkmax);

            Eigen::MatrixXlogd Xk(dk, dk);
            for (py::ssize_t i = 0; i < dk; ++i) {
                for (py::ssize_t j = 0; j < dk; ++j) {
                    // upcast to avoid underflow
                    auto val = static_cast<double>(X_acc(k, i, j)) - xkmax;
                    Xk(i, j) = LogValD(val, _sign(k, i, j));
                }
            }
            lus.emplace_back(Xk);
        }
    }

    py::array_t<float> logdet() {
        auto res = py::array_t<float>({ batch_size });
        auto res_acc = res.mutable_unchecked<1>();

        for (py::ssize_t k = 0; k < batch_size; ++k) {
            double val = lus[k].determinant().logabs() + lengths[k] * xmax[k];
            res_acc(k) = static_cast<float>(val);
        }

        return res;
    }

    py::array_t<float> inv(bool zero_pad) {
        auto res = py::array_t<float>({ batch_size, dim1, dim2 });

        if (zero_pad) {
            std::fill(res.mutable_data(), res.mutable_data() + res.size(), float{});
        }

        auto res_acc = res.mutable_unchecked<3>();
        for (int k = 0; k < batch_size; ++k) {
            auto dk = lengths[k];
            LogValD exp_xkmax(xmax[k], false);
            Eigen::MatrixXlogd Xinv = lus[k].inverse();
            for (int i = 0; i < dk; ++i) {
                for (int j = 0; j < dk; ++j) {
                    LogValD exp_uij = Xinv(i, j);
                    exp_uij /= exp_xkmax;
                    res_acc(k, i, j) = static_cast<float>(exp_uij.as_float());
                }
            }
        }

        return res;
    }

private:
    std::vector<Eigen::FullPivLU<Eigen::MatrixXlogd>> lus;
    std::vector<int> lengths;
    std::vector<float> xmax;
    py::ssize_t batch_size;
    py::ssize_t dim1;
    py::ssize_t dim2;
};

PYBIND11_MODULE(lu, m) {

    py::class_<log_domain_lu>(m, "LogDomainLU")
        .def(py::init<const Eigen::MatrixXf&>(),
             py::arg().noconvert().none(false))
        .def("logdet",
             &log_domain_lu::logdet)
        .def("inv",
             &log_domain_lu::inv,
             py::return_value_policy::move);

    py::class_<batch_log_domain_lu>(m, "BatchLogDomainLU")
        .def(py::init<py::array_t<float>,
                      std::vector<int>,
                      py::array_t<bool>>(),
             py::arg().noconvert(), py::arg().noconvert(), py::arg().noconvert())
        .def("logdet",
             &batch_log_domain_lu::logdet,
             py::return_value_policy::move)
        .def("inv",
             &batch_log_domain_lu::inv,
             py::arg("zero_pad") = true,
             py::return_value_policy::move);

#ifdef VERSION_INFO
    m.attr("__version__") = VERSION_INFO;
#else
    m.attr("__version__") = "dev";
#endif
}


/* Copyright (c) 2012, Julian Straub <jstraub@csail.mit.edu>
 * Licensed under the MIT license. See LICENSE.txt or
 * http://www.opensource.org/licenses/mit-license.php */

#ifndef DP_HPP_
#define DP_HPP_

#include "baseMeasure.hpp"

#include <armadillo>

using namespace std;
using namespace arma;

double digamma(double x)
{
  // http://en.wikipedia.org/wiki/Digamma_function
  double x_sq = x*x;
  return ln(x) - 1.0/(2.0*x) - 1.0/(12.0*x_sq) + 1.0/(120.0*x_sq*x_sq) - 1.0/(252.0*x_sq*x_sq*x_sq);
}

template<class U>
class DP
{
public:
  DP(const BaseMeasure<U>& base, double alpha)
  : mH(base), mAlpha(alpha)
  {};

  ~DP()
  { };

  const BaseMeasure<U>& mH; // base measure
  double mAlpha;
private:
};

template<class U>
class DP_var
{
public:
  DP_var(const BaseMeasure<U>& base, double alpha)
   : mH(base), mAlpha(alpha)
  {};

  ~DP_var()
  { };

  Col<uint32_t> densityEst(const Mat<U>& x, uint32_t K0=10, uint32_t T0=10, uint32_t It=10)
  {

    Col<uint32_t> z(x.n_rows);

    uint32_t K=K0; // number of clusters

    // variables
    vector<double> gamma_1(K,0.0);
    vector<double> gamma_2(K,0.0);
    Row<U> tau_k1(x.n_cols);
    double tau_k2;
    Row<U> lambda_1(x.n_cols);
    double lambda_2;
    vector<double> S(K,0.0);

    // main loop
    for(uint32_t tt=0; tt<It; ++tt)
    {
      double S_sum=0.0;
      for(uint32_t k=0; k<K; ++k)
      {
        // TODO:  actually need to compute the exp of belows stuff => use different approximation!
        S[k] = digamma(gamma_1[k]) - digamma(gamma_1[k] + gamma_2[k]);
        for(uint32_t kt=0; kt<k-1; ++kt)
          S[k] += digamma(gamma_2[kt]) - digamma(gamma_1[kt] + gamma_2[kt]);
        //S[k] += ; // TODO: two remaingin expected values
        S_sum += S[k];
      }

      for(uint32_t k=0; k<K; ++k)
      {
        double
        gamma_k1=sum(z==k)+1.0;
        gamma_k2=alpha;
        for(uint32_t kj=k+1; kj<K; ++kj) gamma_k2 += sum(z==k);
        //Row<U> tau_k1=lambda_1; //TODO: what is lambda_1 and lambda_2
        tau_k2=lambda_2 + sum(z==k); // could reuse gamma_k1
      }
    }
  }

  const BaseMeasure<U>& mH; // base measure
  double mAlpha;
private:

};

#endif /* DP_HPP_ */


#include <armadillo>
#include "comfi.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/matrix.hpp"
#include "viennacl/compressed_matrix.hpp"
#include "viennacl/linalg/prod.hpp"
#include "viennacl/tools/timer.hpp"
#include "viennacl/linalg/maxmin.hpp"
#include "viennacl/forwards.h"

using namespace viennacl::linalg;

vcl_mat comfi::util::vec_to_mat(const vcl_vec &vec) {
  arma::vec cpu_vec(vec.size());
  viennacl::fast_copy(vec, cpu_vec);
  arma::mat cpu_mat(cpu_vec);
  vcl_mat return_mat(vec.size(), 1);
  viennacl::copy(cpu_mat, return_mat);
  return return_mat;
}

vcl_mat comfi::util::ot_vortex_ic(comfi::types::Context &ctx) {
  const double b = 1.0/std::sqrt(4.0*arma::datum::pi),
               n = 25.0/(36.0*arma::datum::pi),
               p = 5.0/(12.0*arma::datum::pi);
  arma::mat xn = arma::zeros<arma::mat>(ctx.num_of_grid(), ctx.num_of_eq);

  std::cout << "Building initial condition...";
  #pragma omp parallel for collapse(2)
  for (arma::uword i=0; i<ctx.nx; i++) { for (arma::uword j=0; j<ctx.nz; j++) {
    //indexing
    const arma::uword ij = inds(i, j, ctx);

    xn(ij, ctx.n_p) = n;
    xn(ij, ctx.n_n) = n;
    xn(ij, ctx.E_p) = p/(ctx.gammamono-1.0);
    xn(ij, ctx.E_n) = p/(ctx.gammamono-1.0);
    //xn(ij, ctx.Bp) = b;
    xn(ij, ctx.Bx) = -b*std::sin(2.0*arma::datum::pi*j*ctx.dz);
    xn(ij, ctx.Bz) = b*std::sin(4.0*arma::datum::pi*i*ctx.dx);
    xn(ij, ctx.Vx) = -1.0*n*std::sin(2.0*arma::datum::pi*j*ctx.dz);
    xn(ij, ctx.Vz) = n*std::sin(2.0*arma::datum::pi*i*ctx.dx);
  }}

  std::cout << "Orszang-Tang Vortex Initial Conditions: (" << ctx.nx << "," << ctx.nz <<
               ") Size: " << xn.size() << std::endl;

  vcl_mat xn_vcl(xn.n_rows, xn.n_cols);
  viennacl::copy(xn, xn_vcl);
  return xn_vcl;
}

vcl_mat comfi::util::shock_tube_ic(comfi::types::Context &ctx) {
  const double b = 0.75, b_l = 1.0, n_l = 1.0, p_l = 1.0, b_r = -1.0, n_r = 0.125, p_r = 0.1;
  arma::mat xn = arma::zeros<arma::mat>(ctx.num_of_grid(), ctx.num_of_eq);

  std::cout << "Building initial condition...";
  #pragma omp parallel for collapse(2)
  for (uint i=0; i<ctx.nx; i++) { for (uint j=0; j<ctx.nz; j++) {
    const arma::uword ij = inds(i, j, ctx);

    if (j > ctx.nz/2) {
      xn(ij, ctx.n_p) = n_l;
      xn(ij, ctx.n_n) = n_l;
      xn(ij, ctx.E_n) = p_l/(ctx.gammamono-1.0);
      xn(ij, ctx.E_p) = p_l/(ctx.gammamono-1.0);
      xn(ij, ctx.Bx) = b_l;
      xn(ij, ctx.Bz) = b;
    } else {
      xn(ij, ctx.E_n) = p_r/(ctx.gammamono-1.0);
      xn(ij, ctx.E_p) = p_r/(ctx.gammamono-1.0);
      xn(ij, ctx.n_p) = n_r;
      xn(ij, ctx.n_n) = n_r;
      xn(ij, ctx.Bx) = b_r;
      xn(ij, ctx.Bz) = b;
    }
  }}
  std::cout << "Shock Tube (" << ctx.nx << "," << ctx.nz <<
               ") Size: " << xn.size() << std::endl;

  vcl_mat xn_vcl(xn.n_rows, xn.n_cols);
  viennacl::copy(xn, xn_vcl);
  return xn_vcl;
}

std::string comfi::util::gettimestr()
{
      time_t rawtime;
      struct tm * timeinfo;
      char buffer[80];
      time (&rawtime);
      timeinfo = localtime(&rawtime);
      strftime(buffer,80,"%Y-%m-%d-%I-%M-%S",timeinfo);
      const std::string timestr(buffer);
      return timestr;
}

void comfi::util::sendtolog(const std::string message, const std::string filename)
{
    std::ofstream logfile(filename.c_str(), std::ios::app);
    if(logfile.is_open())
    {
        logfile << gettimestr() << ": " << message << std::endl;
    }
    else std::cout << "Could not open log file." << std::endl;

    logfile.close();
}

bool comfi::util::save_solution(const vcl_mat &x0,
                                comfi::types::Context &ctx,
                                const std::string &data_name) {
  bool success = true;
  int timestep = ctx.time_step();

  arma::mat savemat(x0.size1(), x0.size2());
  viennacl::copy(x0, savemat);
  static arma::mat gridmat(ctx.num_of_grid(), 2);
  static bool created = false;
  if (!created) {
    #pragma omp parallel for collapse(2)
    for (uint i=0; i<ctx.nx; i++) { for (uint j=0; j<ctx.nz; j++) {
      arma::uword ij = inds(i, j, ctx);
      gridmat(ij, 0) = i*ctx.dx*ctx.l_0;
      gridmat(ij, 1) = j*ctx.dz*ctx.l_0;
    }}
  }

  std::string folder = "output/";
  std::string filename = folder + "mhdsim.h5";
  std::string dataset = "/" + std::to_string(timestep) + "/" + data_name;
  std::string posset = "/" + std::to_string(timestep) + "/grid";
  savemat.save(arma::hdf5_name(filename, dataset, arma::hdf5_opts::append+arma::hdf5_opts::trans));
  gridmat.save(arma::hdf5_name(filename, posset, arma::hdf5_opts::append+arma::hdf5_opts::trans));

  return success;
}

void comfi::util::interpret_arguments(comfi::types::Settings &settings, int argc, char** argv) {
  if (argc != 1) // if arguments were passed
  {
    // parse arguments
    for(int i=1; i < argc; i++)
    {
      if(argv[i][0] == '-') // if starts with hyphen
      {
        if (i != argc-1) {
          std::string argument = argv[i];
          if (argument == "-max_time_steps") {
            std::string value = argv[i+1];
            settings.max_time_steps = stoi(value,nullptr,10);
          } else if (argument == "-max_time") {
            std::string value = argv[i+1];
            settings.max_time = std::stod(value);
          } else if (argument == "-save_dt") {
            std::string value = argv[i+1];
            settings.save_dt = std::atof(value.c_str());
          } else if (argument == "-save_dn") {
            std::string value = argv[i+1];
            settings.save_dn = stoi(value,nullptr,10);
          } else if (argument == "-tolerance")
          {
            std::string value = argv[i+1];
            settings.tolerance = std::stod(value);
          } else if (argument == "-leftbc") {
            // TODO
            std::string value = argv[i+1];
          } else if (argument == "-rightbc") {
            // TODO
            std::string value = argv[i+1];
          } else if (argument == "-upbc") {
            // TODO
            std::string value = argv[i+1];
          } else if (argument == "-downbc") {
            // TODO
            std::string value = argv[i+1];
          } else if (argument == "-restart") {
            settings.restart = true;
          } else {
            std::cerr << "The command line option \'" << argument << "\' is not recognized. Using defaults for unspecified parameters." << std::endl;
          }
        }
      }
    }
  }
}

double comfi::util::getmaxV(const vcl_mat &x0, comfi::types::Context &ctx) {
  using namespace viennacl::linalg;
  const vcl_vec Np = viennacl::column(x0, ctx.n_p);
  const vcl_vec Nn = viennacl::column(x0, ctx.n_n);
  const vcl_vec NVx = viennacl::column(x0, ctx.Vx);
  const vcl_vec NUx = viennacl::column(x0, ctx.Ux);
  const vcl_vec NUz = viennacl::column(x0, ctx.Uz);
  const vcl_vec NVz = viennacl::column(x0, ctx.Vz);
  const vcl_vec local_p_x_vec = element_fabs(element_div(NVx, Np));
  const double local_p_x = viennacl::linalg::max(local_p_x_vec);
  const vcl_vec local_p_z_vec = element_fabs(element_div(NVz, Np));
  const double local_p_z = viennacl::linalg::max(local_p_z_vec);
  const vcl_vec local_n_x_vec = element_fabs(element_div(NUx, Nn));
  const double local_n_x = viennacl::linalg::max(local_n_x_vec);
  const vcl_vec local_n_z_vec = element_fabs(element_div(NUz, Nn));
  const double local_n_z = viennacl::linalg::max(local_n_z_vec);
  std::cout << "Max local (p): (" << local_p_x << "," << local_p_z << ") | ";
  std::cout << "Max local (n): (" << local_n_x << "," << local_n_z << ")" << std::endl;
  const double fast_p_x = viennacl::linalg::max(viennacl::column(comfi::routines::fast_speed_x(x0, ctx), 0));
  const double fast_p_z = viennacl::linalg::max(viennacl::column(comfi::routines::fast_speed_z(x0, ctx), 0));
  std::cout << "Max fast (p): (" << fast_p_x << "," << fast_p_z << ") | ";
  const double sound_n_x = viennacl::linalg::max(viennacl::column(comfi::routines::sound_speed_n(x0, ctx), 0));
  const double sound_n_z = viennacl::linalg::max(viennacl::column(comfi::routines::sound_speed_n(x0, ctx), 0));
  std::cout << "Max sound speed (n): (" << sound_n_x << "," << sound_n_z << ")";
  std::cout << std::endl;

  /*
  return std::max(std::max(std::max(local_p_x, fast_p_x), std::max(local_p_z, fast_p_z)),
                  std::max(std::max(sound_n_x, local_n_x), std::max(sound_n_z, local_n_z)));
                  */
  return std::max(std::max(local_p_x+fast_p_x, local_p_z+fast_p_z),
                  std::max(sound_n_x+local_n_x, sound_n_z+local_n_z));
}

/*
vim: tabstop=2
vim: shiftwidth=2
vim: smarttab
vim: expandtab
*/


#ifdef HOPS_CLP_FOUND

#include <coin/ClpPackedMatrix.hpp>
#include <coin/ClpSimplex.hpp>

#include <Eigen/Sparse>

#include <hops/LinearProgram/LinearProgramClpImpl.hpp>
#include <hops/LinearProgram/LinearProgramStatus.hpp>

namespace {
    hops::LinearProgramStatus parseClpStatus(int returnCode) {
        switch (returnCode) {
            case 0:
                return hops::LinearProgramStatus::OPTIMAL;
            case 1:
                return hops::LinearProgramStatus::INFEASIBLE;
            case 2:
                return hops::LinearProgramStatus::UNBOUNDED;
            default:
                return hops::LinearProgramStatus::ERROR;
        }
    }

    bool isInequalityRedundant(const Eigen::MatrixXd &A,
                               const Eigen::VectorXd &b,
                               unsigned int index,
                               double tolerance) {
        Eigen::VectorXd bTemp = b;
        bTemp(index) += 1.0;

        auto result = hops::LinearProgramClpImpl(A, bTemp).solve(A.row(index));

        if (result.status != hops::LinearProgramStatus::OPTIMAL) {
            return false;
        }

        return result.objectiveValue <= b(index) + tolerance;
    }

}

hops::LinearProgramClpImpl::LinearProgramClpImpl(const Eigen::MatrixXd &A, const Eigen::VectorXd &b) :
        LinearProgram(A, b) {
    if (A.cols() > std::numeric_limits<int>::max() || A.cols() < std::numeric_limits<int>::min()) {
        throw std::runtime_error(
                "Objective has more columns, than can fit into an integer, making it incompatible to CLP");
    }
    if (A.rows() > std::numeric_limits<int>::max() || A.rows() < std::numeric_limits<int>::min()) {
        throw std::runtime_error(
                "Objective has more rows, than can fit into an integer, making it incompatible to CLP");
    }

    std::vector<int> rowIndices;
    std::vector<int> columnIndices;
    std::vector<double> values;
    Eigen::SparseMatrix<double> sparseA = A.sparseView();
    for (long i = 0; i < A.outerSize(); ++i) {
        for (Eigen::SparseMatrix<double>::InnerIterator it(sparseA, i); it; ++it) {
            if (it.value() != 0) {
                rowIndices.emplace_back(it.row());
                columnIndices.emplace_back(it.col());
                values.emplace_back(it.value());
            }
        }
    }

    CoinPackedMatrix matrix(false, rowIndices.data(), columnIndices.data(), values.data(), values.size());

    Eigen::VectorXd rowLower = Eigen::VectorXd::Constant(b.rows(), -DBL_MAX);
    Eigen::VectorXd colLower = Eigen::VectorXd::Constant(A.cols(), -DBL_MAX);
    Eigen::VectorXd colUpper = Eigen::VectorXd::Constant(A.cols(), DBL_MAX);

    model.loadProblem(matrix, colLower.data(), colUpper.data(), nullptr,
                      rowLower.data(), b.data());
    model.setOptimizationDirection(-1.);
    model.scaling(4);
    model.setLogLevel(0);
}

hops::LinearProgramClpImpl::LinearProgramClpImpl(const hops::LinearProgramClpImpl &other) :
        LinearProgram(other.A, other.b),
        model(other.model) {}

hops::LinearProgramClpImpl &hops::LinearProgramClpImpl::operator=(const hops::LinearProgramClpImpl &other) {
    this->A = other.A;
    this->b = other.b;
    this->model = other.model;
    return *this;
}

hops::LinearProgramSolution hops::LinearProgramClpImpl::solve(const Eigen::VectorXd &objective) const {
    for (int i = 0; i < static_cast<int>(objective.rows()); ++i) {
        model.setObjectiveCoefficient(i, objective(i));
    }
    model.primal();
    model.checkSolution();
    model.checkUnscaledSolution();

    return LinearProgramSolution(-model.rawObjectiveValue(), // - due to optimization direction internally
                                 Eigen::Map<Eigen::VectorXd>(model.primalColumnSolution(), objective.rows()),
                                 parseClpStatus(model.status()));
}

std::tuple<Eigen::MatrixXd, Eigen::VectorXd> hops::LinearProgramClpImpl::removeRedundantConstraints(double tolerance) {
    if (A.rows() <= 1) {
        return std::make_tuple(A, b);
    }

    for (int i = 0; i < A.rows(); ++i) {
        int numRows = A.rows();
        // Try to remove ith inequality
        if (isInequalityRedundant(A, b, i, tolerance)) {
            if (i != numRows - 1) {
                // Swap the row which is going to be removed with the
                // last row
                A.row(i).swap(A.row(numRows - 1));
                double temp = b(i);
                b(i) = b(numRows - 1);
                b(numRows - 1) = temp;
                i--; // resets index after swap
            }

            // Remove the last row
            A.conservativeResize(numRows - 1, Eigen::NoChange);
            b.conservativeResize(numRows - 1);
        }
    }
    *this = LinearProgramClpImpl(A, b);
    return std::make_tuple(A, b);
}

hops::LinearProgramSolution hops::LinearProgramClpImpl::computeChebyshevCenter() const {
    //Extend system by dimension for radius
    const long numberOfRows = A.rows();
    const long numberOfColumns = A.cols();
    Eigen::MatrixXd A_ext = A;
    Eigen::MatrixXd l_col(numberOfRows + 1, 1);
    l_col << A.rowwise().norm(), -1;
    A_ext.conservativeResize(numberOfRows + 1, numberOfColumns + 1);
    A_ext.row(numberOfRows) = Eigen::VectorXd::Zero(numberOfColumns + 1);
    A_ext.col(numberOfColumns) = l_col;
    Eigen::VectorXd b_ext = b;
    b_ext.conservativeResize(numberOfRows + 1);
    b_ext(numberOfRows) = 0;

    //make objective
    Eigen::VectorXd obj = Eigen::VectorXd::Zero(numberOfColumns + 1);
    obj(numberOfColumns) = 1;

    LinearProgramSolution chebyshevSolution = LinearProgramClpImpl(A_ext, b_ext).solve(obj);
    chebyshevSolution.optimalParameters.conservativeResize(A.cols());
    return chebyshevSolution;
}

std::vector<long> hops::LinearProgramClpImpl::computeUnconstrainedDimensions() const {
    std::vector<long> directions;
    for (long i = 0; i < A.cols(); ++i) {
        Eigen::VectorXd objective = Eigen::VectorXd::Zero(A.cols());
        objective(i) = 1.0;
        for (int j = 0; j < static_cast<int>(objective.rows()); ++j) {
            model.setObjectiveCoefficient(j, objective(j));
        }
        auto forwardSolution = solve(objective);
        if (forwardSolution.status != hops::LinearProgramStatus::OPTIMAL) {
            directions.push_back(i + 1);
        }

        auto backwardSolution = solve(-objective);
        if (backwardSolution.status != hops::LinearProgramStatus::OPTIMAL) {
            directions.push_back(-i - 1);
        }
    }
    return directions;
}

std::tuple<Eigen::MatrixXd, Eigen::VectorXd>
hops::LinearProgramClpImpl::addBoxConstraintsToUnconstrainedDimensions(double lb, double ub) {
    std::vector<long> unconstrainedDimensions = computeUnconstrainedDimensions();

    for (const auto &unconstrainedDimension : unconstrainedDimensions) {
        A.conservativeResize(A.rows() + 1, A.cols());
        A.row(A.rows() - 1) = Eigen::VectorXd::Zero(A.cols());
        b.conservativeResize(b.rows() + 1);
        if (unconstrainedDimension > 0) {
            A(A.rows() - 1, unconstrainedDimension - 1) = 1;
            b(b.rows() - 1) = ub;
        } else {
            A(A.rows() - 1, -unconstrainedDimension - 1) = -1;
            b(b.rows() - 1) = lb;
        }
    }
    *this = LinearProgramClpImpl(A, b);
    return std::make_tuple(A, b);
}

#endif //HOPS_CLP_FOUND


#ifndef CLASSIC_GRID_HPP
#define CLASSIC_GRID_HPP

#include <array>
#include <tuple>
#include <unordered_map>
#include <vector>

#include <boost/container_hash/hash.hpp>

#include "distributions/angular_gauss.hpp"
#include "types/types.hpp"

class ClassicGrid {
    int div;
    AngularGauss* distr;

    // points is a map of (i, j) pairs, that are indices of grid points, to
    // the indicies of the same grid's points in Cartesian coordinates'
    std::unordered_map<ClassicGridPoint, int, boost::hash<ClassicGridPoint>> points;

    // cartesian_points is a vector of grid's points in Cartesian coordinates
    std::vector<CoordsOfPoint> cartesian_points;

    // values is an evaluated function on grid, value in i'th position
    // corresponds to the function's value of i'th point in cartesian_points
    // vector
    std::vector<double> values;

    // trapeziums are the elementary objects of which the grid consists. They
    // are stored as vectors of columns of trapeziums on a sphere.
    std::vector<std::vector<Trapezium>> trapeziums;

    void GenerateGridAndEvaluateFunc(void);
    void EvaluateFuncRoutine(int lower_bound, int upper_bound);

    std::tuple<int, TrapeziumIndex> GetNextTrapeziumIndex(int previous_side, const TrapeziumIndex& previous) const;
    TrapeziumIndex GetUpperTrapeziumIndex(const TrapeziumIndex& previous) const;
    TrapeziumIndex GetLowerTrapeziumIndex(const TrapeziumIndex& previous) const;
    TrapeziumIndex GetLeftTrapeziumIndex(const TrapeziumIndex& previous) const;
    TrapeziumIndex GetRightTrapeziumIndex(const TrapeziumIndex& previous) const;

    // FindIntersection uses linear interpolation to find an intersection point
    // with value c between vertices with values a and b, where x1 = f^(-1)(a),
    // x2 = f^(-1)(b), sign((a - c) * (b - c)) must be -1.
    CoordsOfPoint FindIntersection(const CoordsOfPoint& x1, const CoordsOfPoint& x2, double a, double b, double c) const;

    // ProcessSegment processes trapezium using marching square algorithm and
    // returns last processed trapezium side and point.
    std::tuple<int, CoordsOfPoint> ProcessSegment(const Trapezium& trapezium, int index, int start_side, double iso_value);

    // GetMarchingSquareIndex returns the trapezium index needed for marching squares algorithm.
    // See Lookup Table here: https://en.wikipedia.org/wiki/Marching_squares#Basic_algorithm
    int GetMarchingSquareIndex(const Trapezium& trapezium, double value);

public:
    // ClassicGrid creates new classic grid with bandwidth grid_div and
    // function distr on it
    ClassicGrid(int grid_div, AngularGauss* distr);

    // Func returns function on grid
    const AngularGauss* Func(void) const;

    // GetCoordsOfPoint converts spherical coordinates of a point to Cartesian
    static CoordsOfPoint GetCoordsOfPoint(const AnglesOfPoint& point);
    // GetAnglesOfPoint converts Cartesian coordinates of a point to spherical
    static AnglesOfPoint GetAnglesOfPoint(const CoordsOfPoint& point);

    // GetIsolineCoords returns the coordinates of the points for the function
    // isoline with a value equal to iso_value.
    // It uses marching squares algorithm, see more info:
    // https://en.wikipedia.org/wiki/Marching_squares
    std::vector<CoordsOfPoint> GetIsolineCoords(double iso_value);
};

#endif // CLASSIC_GRID_HPP


/* Copyright (C) 2021 Intel Corporation
 * SPDX-License-Identifier: Apache-2.0
 * This program is 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. See accompanying LICENSE file.
 */

// This program performs basic operations n times and is used for profiling.

#include <iostream>

#include <helib/helib.h>
#include <helib/EncryptedArray.h>
#include <helib/ArgMap.h>
#include <NTL/BasicThreadPool.h>

void add(helib::Ctxt& ctxt, int iter)
{
  for (int i = 0; i < iter; ++i) {
    // Create tmp to avoid noise growth.
    helib::Ctxt tmp(ctxt);
    tmp += ctxt;
  }
}

void mult(helib::Ctxt& ctxt, int iter)
{
  for (int i = 0; i < iter; ++i) {
    // Create tmp to avoid noise growth.
    helib::Ctxt tmp(ctxt);
    tmp *= ctxt;
  }
}

void rotate(helib::Ctxt& ctxt, const helib::EncryptedArray& ea, int iter)
{
  for (int i = 0; i < iter; ++i) {
    // Create tmp to avoid noise growth.
    helib::Ctxt tmp(ctxt);
    ea.rotate(tmp, 1);
  }
}

void scalarAdd(helib::Ctxt ctxt, int iter)
{
  for (int i = 0; i < iter; ++i) {
    ctxt += 1l;
  }
}

void scalarMult(helib::Ctxt ctxt, int iter)
{
  for (int i = 0; i < iter; ++i) {
    ctxt *= 2l;
  }
}

int main(int argc, char* argv[])
{
  // Plaintext prime modulus
  unsigned long p = 131;
  // Cyclotomic polynomial - defines phi(m)
  unsigned long m = 130; // this will give 48 slots
  // Hensel lifting (default = 1)
  unsigned long r = 1;
  // Number of bits of the modulus chain
  unsigned long bits = 1000;
  // Number of columns of Key-Switching matrix (default = 2 or 3)
  unsigned long c = 2;
  // Size of NTL thread pool (default =1)
  unsigned long nthreads = 1;

  helib::ArgMap()
      .optional()
      .named()
      .arg("m", m, "Cyclotomic polynomial ring")
      .arg("p", p, "Plaintext prime modulus")
      .arg("r", r, "Hensel lifting")
      .arg("bits", bits, "# of bits in the modulus chain")
      .arg("c", c, "# fo columns of Key-Switching matrix")
      .arg("nthreads", nthreads, "Size of NTL thread pool")
      .parse(argc, argv);

  // set NTL Thread pool size
  if (nthreads > 1)
    NTL::SetNumThreads(nthreads);

  helib::Context context = helib::ContextBuilder<helib::BGV>()
                               .m(m)
                               .p(p)
                               .r(r)
                               .bits(bits)
                               .c(c)
                               .build();

  helib::SecKey secret_key = helib::SecKey(context);
  secret_key.GenSecKey();
  helib::addSome1DMatrices(secret_key);
  const helib::PubKey& public_key = secret_key;
  const helib::EncryptedArray& ea = context.getEA();

  std::cout << std::endl;
  context.printout();

  long nslots = ea.size();

  helib::PtxtArray pa(context);
  pa.random();
  helib::Ctxt ctxt(public_key);
  pa.encrypt(ctxt);

  int opt = 0;
  std::cout << "What operation(s) do you want to run? [0:all, 1:add, 2:mult, "
               "3:rotate, 4:scalarAdd, 5:scalarMult]: ";
  std::cin >> opt;

  int iter = 0;
  std::cout << "How many iterations?: ";
  std::cin >> iter;
  iter = (iter < 1) ? 10 : iter;

  std::cout << "Option: " << opt << ", " << iter << " times.\n";

  switch (opt) {
  case 0:
    std::cout << "Running option " << opt << ": all operations...\n";
    add(ctxt, iter);
    mult(ctxt, iter);
    rotate(ctxt, ea, iter);
    scalarAdd(ctxt, iter);
    scalarMult(ctxt, iter);
    std::cout << "Done!\n";
    break;
  case 1:
    std::cout << "Running option " << opt << ": additions...\n";
    add(ctxt, iter);
    std::cout << "Done!\n";
    break;
  case 2:
    std::cout << "Running option " << opt << ": multiplications...\n";
    mult(ctxt, iter);
    std::cout << "Done!\n";
    break;
  case 3:
    std::cout << "Running option " << opt << ": rotations...\n";
    rotate(ctxt, ea, iter);
    std::cout << "Done!\n";
    break;
  case 4:
    std::cout << "Running option " << opt << ": scalar additions...\n";
    scalarAdd(ctxt, iter);
    std::cout << "Done!\n";
    break;
  case 5:
    std::cout << "Running option " << opt << ": scalar multiplications...\n";
    scalarMult(ctxt, iter);
    std::cout << "Done!\n";
    break;
  default:
    std::cout << "Received invalid option: " << opt << std::endl;
  }

  return 0;
}


#include <frovedis.hpp>
#include <frovedis/matrix/crs_matrix.hpp>

#include <boost/program_options.hpp>

using namespace boost;
using namespace frovedis;
using namespace std;

template <class T>
crs_matrix_local<T> calc_tf(crs_matrix_local<T>& mat) {
  crs_matrix_local<T> ret;
  ret.val.resize(mat.val.size());
  ret.idx = mat.idx;
  ret.off = mat.off;
  ret.local_num_col = mat.local_num_col;
  ret.local_num_row = mat.local_num_row;
  for(size_t row = 0; row < mat.local_num_row; row++) {
    T sum = 0;
    for(size_t col = mat.off[row]; col < mat.off[row + 1]; col++) {
      sum += mat.val[col];
    }
    T norm = 1.0/sum;
    for(size_t col = mat.off[row]; col < mat.off[row + 1]; col++) {
      ret.val[col] = mat.val[col] * norm;
    }
  }
  return ret;
}

template <class T>
vector<T> local_df(crs_matrix_local<T>& mat) {
  vector<T> ret(mat.local_num_col);
  T* retp = &ret[0];
  size_t* idxp = &mat.idx[0];

  for(size_t row = 0; row < mat.local_num_row; row++) {
#pragma cdir nodep
    for(size_t col = mat.off[row]; col < mat.off[row + 1]; col++) {
      if(idxp[col] >= mat.local_num_col) {
        std::cerr << "local_num_col = " << mat.local_num_col
                  << ", idxp[col] = " << idxp[col] << std::endl;
        throw std::runtime_error("error");
      }
      retp[idxp[col]]++;
    }
  }
  return ret;
}

template <class T>
vector<T> reduce_df(const vector<T>& l, const vector<T>& r) {
  vector<T> ret(l.size());
  for(size_t i = 0; i < l.size(); i++) {
    ret[i] = l[i] + r[i];
  }
  return ret;
}

template <class T>
vector<T> calc_idf(crs_matrix<T>& mat) {
  double total_doc = static_cast<double>(mat.num_row);
  auto local_dfs = mat.data.map(local_df<T>);
  auto df = local_dfs.reduce(reduce_df<T>);
  vector<T> idf(df.size());
  for(size_t i = 0; i < df.size(); i++) {
    idf[i] = log(total_doc / static_cast<double>(df[i]));
  }
  return idf;
}

template <class T>
void mul_idf(crs_matrix_local<T>& mat, vector<T>& idf) {
  T* matvalp = &mat.val[0];
  T* idfp = &idf[0];
  size_t* idxp = &mat.idx[0];
  for(size_t row = 0; row < mat.local_num_row; row++) {
#pragma cdir nodep
    for(size_t col = mat.off[row]; col < mat.off[row + 1]; col++) {
      matvalp[col] *= idfp[idxp[col]];
    }
  }
}

/*
  assume that document x term matrix
  (i.e. document is row, term is column)

  Though TF-IDF has vaious definitions, we adopted definition in
  Japanese wikipedia (2015/8/4)
  tf: number of term / all number of terms in the doc
  idf: log (number of all docs / number of docs that includes the term)
 */
template <class T>
void tfidf(const string& input, const string& output) {
  auto mat = make_crs_matrix_load<T>(input);
  crs_matrix<T> tf(mat.data.map(calc_tf<T>));
  auto idf = calc_idf<T>(mat);
  auto bcast_idf = make_node_local_broadcast(idf);
  tf.data.mapv(mul_idf<T>, bcast_idf);
  tf.save(output);
}

int main(int argc, char* argv[]){
  use_frovedis use(argc, argv);

  using namespace boost::program_options;

  options_description opt("option");
  opt.add_options()
    ("input,i", value<string>(), "input matrix file")
    ("output,o", value<string>(), "output matrix file");

  variables_map argmap;
  store(command_line_parser(argc,argv).options(opt).allow_unregistered().
        run(), argmap);
  notify(argmap);

  string input, output;

  if(argmap.count("input")){
    input = argmap["input"].as<string>();
  } else {
    cerr << "input matrix file is not specified" << endl;
    exit(1);
  }

  if(argmap.count("output")){
    output = argmap["output"].as<string>();
  } else {
    cerr << "output matrix file is not specified" << endl;
    exit(1);
  }

  tfidf<double>(input, output);
}


#include "compi.hpp"

#include <complex>
#include <unordered_map>
#include <algorithm>
#include <utility>

#include <boost/math/quadrature/gauss_kronrod.hpp>
#include <boost/math/tools/precision.hpp>

extern "C" {
    #include "integration_routines.h"
}

#include "integration_routines_template.hpp"
#include "IntegrandFunctionWrapper.hpp"
#include "utils.hpp"

struct GaussKronrodParameters: public RoutineParametersBase{
    Real x_min;
    Real x_max;
    unsigned points = 31;

    GaussKronrodParameters(PyObject* routine_args, PyObject* routine_kwargs){
        constexpr std::array<const char*,0> dumby_arg = {};
        constexpr std::array<const char*,1> keyword_only_args = {"points"};
        constexpr auto keywords = generate_keyword_list<IntegralRange::finite>(dumby_arg,dumby_arg,keyword_only_args);

        if(!PyArg_ParseTupleAndKeywords(routine_args,routine_kwargs,"Odd|OO$pIdI",const_cast<char**>(keywords.data()),
                &integrand,&x_min,&x_max,
                &args,&kw,
                &full_output, &max_levels,&tolerance,&points)){
            throw could_not_parse_arguments("Unable to parse python arguments to C variables");
        }
    }

};

GaussKronrodParameters::result_type run_integration_routine(const compi_internal::IntegrandFunctionWrapper& f, const GaussKronrodParameters& parameters){
    using std::complex;
    using namespace compi_internal;
    using boost::math::quadrature::gauss_kronrod;
    using IntegrationRoutine = complex<Real>(*)(IntegrandFunctionWrapper, Real, Real, unsigned, Real, Real*, Real*);

    // The possible tempates for the different allowed numbers of divisions are instasiated,
    // so that the Python runtime can select which one to use
    static const std::unordered_map<unsigned,IntegrationRoutine> integration_routines{{15,gauss_kronrod<Real,15>::integrate},
                                                                                {31,gauss_kronrod<Real,31>::integrate},
                                                                                {41,gauss_kronrod<Real,41>::integrate},
                                                                                {51,gauss_kronrod<Real,51>::integrate},
                                                                                {61,gauss_kronrod<Real,61>::integrate}
                                                                                };

    GaussKronrodParameters::result_type result;

    try{
        result.result = integration_routines.at(parameters.points)(f,parameters.x_min,parameters.x_max,parameters.max_levels,parameters.tolerance,&(result.err),&(result.l1));
    } catch (const std::out_of_range& e){
        PyErr_SetString(PyExc_ValueError,"Invalid number of points for gauss_kronrod");
        throw unable_to_call_integration_routine("Invalid number of points for gauss_kronrod");
    }

    return result;
}

template<unsigned points>
std::pair<PyObject*, PyObject*> get_abscissa_and_weights() noexcept{
    auto abscissa = compi_internal::py_list_from_real_container(boost::math::quadrature::gauss_kronrod<Real,points>::abscissa());
    if(abscissa == NULL){
        return std::make_pair<PyObject*,PyObject*>(NULL,NULL);
    }
    auto weights = compi_internal::py_list_from_real_container(boost::math::quadrature::gauss_kronrod<Real,points>::weights());
    if(weights == NULL){
        Py_DECREF(abscissa);
        return std::make_pair<PyObject*,PyObject*>(NULL,NULL);
    }
    return std::make_pair(abscissa,weights);
}

template<>
PyObject* generate_full_output_dict(const GaussKronrodParameters::result_type& result, const GaussKronrodParameters& parameters) noexcept{

    // The boost API returning the abscissa and weights simply spesifies that
    // they are returned as a (reference to a) random access container.
    // Since this could (reasonably) depend on points (e.g. array<Real,points>)
    // it is awkward to use the map based appraoch in run_integation_routine
    // and instead we use more verbose a template/switch based approach

    std::pair<PyObject*,PyObject*> abscissa_and_weights;
    switch(parameters.points){
        case 15:
            abscissa_and_weights = get_abscissa_and_weights<15>();
            break;
        case 31:
            abscissa_and_weights = get_abscissa_and_weights<31>();
            break;
        case 41:
            abscissa_and_weights = get_abscissa_and_weights<41>();
            break;
        case 51:
            abscissa_and_weights = get_abscissa_and_weights<51>();
            break;
        case 61:
            abscissa_and_weights = get_abscissa_and_weights<61>();
            break;
        default:
            // Should never get here as have already checked the value of points is valid
            PyErr_SetString(PyExc_NotImplementedError,"Unable to generate abscissa and weights for the given number of points.\nPlease report this bug");
            return NULL;
        }

        if(abscissa_and_weights.first == NULL || abscissa_and_weights.second == NULL){
            return NULL;
        }

        return Py_BuildValue("{sfsOsO}","L1 norm",result.l1,"abscissa",abscissa_and_weights.first,"weights",abscissa_and_weights.second);
}

// Integrates a Python function returning a complex over a finite interval using
// Gauss-Kronrod adaptive quadrature
extern "C" PyObject* gauss_kronrod(PyObject* self, PyObject* args, PyObject* kwargs){
    return integration_routine<GaussKronrodParameters>(args,kwargs);
}


/*=============================================================================
    Copyright (c) 2002-2015 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  A complex number micro parser.
//
//  [ JDG May 10, 2002 ]    spirit1
//  [ JDG May 9, 2007 ]     spirit2
//  [ JDG May 12, 2015 ]    spirit X3
//
///////////////////////////////////////////////////////////////////////////////

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>

#include <iostream>
#include <string>
#include <complex>

///////////////////////////////////////////////////////////////////////////////
//  Our complex number parser/compiler
///////////////////////////////////////////////////////////////////////////////
namespace client
{
    template <typename Iterator>
    bool parse_complex(Iterator first, Iterator last, std::complex<double>& c)
    {
        using boost::spirit::x3::double_;
        using boost::spirit::x3::_attr;
        using boost::spirit::x3::phrase_parse;
        using boost::spirit::x3::ascii::space;

        double rN = 0.0;
        double iN = 0.0;
        auto fr = [&](auto& ctx){ rN = _attr(ctx); };
        auto fi = [&](auto& ctx){ iN = _attr(ctx); };

        bool r = phrase_parse(first, last,

            //  Begin grammar
            (
                    '(' >> double_[fr]
                        >> -(',' >> double_[fi]) >> ')'
                |   double_[fr]
            ),
            //  End grammar

            space);

        if (!r || first != last) // fail if we did not get a full match
            return false;
        c = std::complex<double>(rN, iN);
        return r;
    }
}

////////////////////////////////////////////////////////////////////////////
//  Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
    std::cout << "/////////////////////////////////////////////////////////\n\n";
    std::cout << "\t\tA complex number micro parser for Spirit...\n\n";
    std::cout << "/////////////////////////////////////////////////////////\n\n";

    std::cout << "Give me a complex number of the form r or (r) or (r,i) \n";
    std::cout << "Type [q or Q] to quit\n\n";

    std::string str;
    while (getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        std::complex<double> c;
        if (client::parse_complex(str.begin(), str.end(), c))
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << "got: " << c << std::endl;
            std::cout << "\n-------------------------\n";
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }

    std::cout << "Bye... :-) \n\n";
    return 0;
}


/**
 * @file Range.hpp
 *
 * The file defines a template class to represent ranges.
 *
 * @author Thomas Röfer
 */

#pragma once

#include <Eigen/Dense>
#include <algorithm>
#include <type_traits>

/**
 * A template class to represent ranges. It also defines the 13 Allen relations
 */
template<typename T>
struct Range
{
  /**
   * Constructor.
   * Defines an empty range.
   */
  constexpr Range() : min(T()), max(T()) {};

  /**
   * Constructor.
   * Defines an empty range.
   * @param minmax A conjoined starting and ending point of the empty range.
   */
  constexpr Range(T minmax) : min(minmax), max(minmax) {};

  /**
   * Constructor.
   * @param min The minimum of the range.
   * @param max The maximum of the range.
   */
  constexpr Range(T min, T max) : min(min), max(max) {};

  /** A range between 0 and 1. */
  static constexpr Range<T> ZeroOneRange();

  /** A range between -1 and 1. */
  static constexpr Range<T> OneRange();

  /**
   * The function enlarges the range so that a certain value will be part of it.
   * @param t The value that will be part of the range.
   * @return A reference to the range.
   */
  Range<T>& add(T t)
  {
    if(min > t)
      min = t;
    if(max < t)
      max = t;
    return *this;
  }

  /**
   * The function enlarges the range so that the resulting range also contains another one.
   * @param r The range that also will be part of the range.
   * @return A reference to the range.
   */
  Range<T>& add(const Range<T>& r)
  {
    add(r.min);
    add(r.max);
    return *this;
  }

  /**
   * The function checks whether a certain value is in the range.
   * Note that the function is able to handle circular range, i.e. max < min.
   * @param t The value.
   * @return Is the value inside the range?
   */
  constexpr bool isInside(T t) const {return min <= max ? t >= min && t <= max : t >= min || t <= max;}

  /**
   * The function limits a certain value to the range.
   * Note that the function is not able to handle circular range, i.e. max < min.
   * @param t The value that will be "clipped" to the range.
   * @return The limited value.
   */
  constexpr T limit(T t) const {return t < min ? min : t > max ? max : t;} //sets a limit for a Range

  constexpr T clamped(T t) const { return limit(t); }
  T& clamp(T& t) const { t = clamped(t); return t; }

  template<typename Derived>
  Derived& clamp(Eigen::DenseBase<Derived>& mat) const
  {
    static_assert(std::is_same<typename Eigen::MatrixBase<Derived>::Scalar, T>::value, "Matrix must have the same scalar type as the Range.");
    return mat = mat.derived().unaryExpr([this](T val) { return clamped(val); });
  }

  template<typename Derived>
  Derived clamped(const Eigen::DenseBase<Derived>& mat) const
  {
    static_assert(std::is_same<typename Eigen::MatrixBase<Derived>::Scalar, T>::value, "Matrix must have the same scalar type as the Range.");
    return mat.derived().unaryExpr([this](T val) { return clamped(val); });
  }

  /**
   * The function limits another range to this range.
   * Note that the function is able to handle circular range, i.e. max < min.
   * @param r The range that will be "clipped" to this range.
   * @return The limited value.
   */
  constexpr Range<T> limit(const Range<T>& r) const { return Range<T>(limit(r.min), limit(r.max)); } //sets the limit of a Range

  /**
   * Scales a value t with a range of tRange to this range.
   */
  T scale(T t, const Range<T>& tRange) const;

  /**
   * The function returns the size of the range.
   * @return The difference between the lower limit and the higher limit.
   */
  constexpr T getSize() const {return max - min;}

  /**
   * The function returns the center of the range.
   * @return The center.
   */
  constexpr T getCenter() const {return (max + min) / 2;}

  //!@name The 13 Allen relations
  //!@{
  constexpr bool operator==(const Range<T>& r) const {return min == r.min && max == r.max;}
  constexpr bool operator<(const Range<T>& r) const {return max < r.min;}
  constexpr bool operator>(const Range<T>& r) const {return min > r.max;}
  constexpr bool meets(const Range<T>& r) const {return max == r.min;}
  constexpr bool metBy(const Range<T>& r) const {return min == r.max;}
  constexpr bool overlaps(const Range<T>& r) const {return min < r.min && max < r.max && max > r.min;}
  constexpr bool overlappedBy(const Range<T>& r) const {return min > r.min && max > r.max && min < r.max;}
  constexpr bool starts(const Range<T>& r) const {return min == r.min && max < r.max;}
  constexpr bool startedBy(const Range<T>& r) const {return min == r.min && max > r.max;}
  constexpr bool finishes(const Range<T>& r) const {return max == r.max && min > r.min;}
  constexpr bool finishedBy(const Range<T>& r) const {return max == r.max && min < r.min;}
  constexpr bool during(const Range<T>& r) const {return min > r.min && max < r.max;}
  constexpr bool contains(const Range<T>& r) const {return min < r.min && max > r.max;}
  //!@}

  constexpr bool operator!=(const Range<T>& r) const {return min != r.min || max != r.max;}

  // The size of the intersection of to ranges or 0 if there is no intersection
  constexpr T intersectionSizeWith(const Range<T>& r) const {return std::max(0.f, std::min(max, r.max) - std::max(min, r.min));};

  T min;
  T max; /**< The limits of the range. */
};

class Angle;

using Rangea = Range<Angle>;
using Rangei = Range<int>;
using Rangef = Range<float>;
using Rangeuc = Range<unsigned char>;

template<typename T>
constexpr Range<T> Range<T>::ZeroOneRange()
{
  return Range<T>(T(0), T(1));
}

template<typename T>
constexpr Range<T> Range<T>::OneRange()
{
  return Range<T>(T(-1), T(1));
}

template<typename T>
T Range<T>::scale(T t, const Range<T>& tRange) const
{
  return limit(((t - tRange.min) / (tRange.max - tRange.min)) * (max - min) + min);
}


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

#pragma once

#include "craam/ImMDP.hpp"
#include "craam/Simulation.hpp"
#include "craam/Samples.hpp"
#include "craam/modeltools.hpp"
#include "craam/algorithms/values.hpp"

#include <rm/range.hpp>

#include <boost/functional/hash.hpp>
#include <iostream>
#include <iterator>
#include <cmath>

using namespace std;
using namespace craam;
using namespace craam::algorithms;
using namespace craam::impl;
using namespace util::lang;

/**
Creates a simple chain problem.
Actions:   0 - left
           1 - right
Optimal solution: Action 1, with value function:
    [1.1 gamma^2/(1-gamma), 1.1 gamma/(1-gamma), 1.1/(1-gamma)]

*/
MDP make_chain1(){
    MDP rmdp(3);

    add_transition(rmdp,0,1,1,1,0);
    add_transition(rmdp,1,1,2,1,0);
    add_transition(rmdp,2,1,2,1,1.1);

    add_transition(rmdp,0,0,0,1,0);
    add_transition(rmdp,1,0,0,1,1);
    add_transition(rmdp,2,0,1,1,1);

    return rmdp;
}

BOOST_AUTO_TEST_CASE( simple_construct_mdpi ) {

    auto mdp = make_shared<MDP>();
    vector<long> observations({0,0});
    Transition initial(vector<long>{0,1},vector<prec_t>{0.5,0.5},vector<prec_t>{0,0});

    add_transition(*mdp,0,0,1,1.0,1.0);
    add_transition(*mdp,1,0,0,1.0,1.0);
    BOOST_CHECK_EQUAL(mdp->state_count(), 2);

    MDPI im(const_pointer_cast<const MDP>(mdp), observations, initial);

    MDPI im2(*mdp,observations,initial);

    // check that we really have a copy
    add_transition(*mdp,1,0,2,1.0,1.0);

    BOOST_CHECK_EQUAL(mdp->state_count(), 3);
    BOOST_CHECK_EQUAL(im.get_mdp()->state_count(), 3);
    BOOST_CHECK_EQUAL(im2.get_mdp()->state_count(), 2);
}

BOOST_AUTO_TEST_CASE( simple_construct_mdpi_r ) {

    auto mdp = make_shared<MDP>();
    vector<long> observations({0,0});
    Transition initial(vector<long>{0,1},vector<prec_t>{0.5,0.5},vector<prec_t>{0,0});

    add_transition(*mdp,0,0,1,1.0,1.0);
    add_transition(*mdp,1,0,0,1.0,2.0);

    MDPI_R imr(const_pointer_cast<const MDP>(mdp), observations, initial);

    // COPY ! so we can change the threshold
    auto rmdp = imr.get_robust_mdp();

    BOOST_CHECK_EQUAL(rmdp.state_count(), 1);
    BOOST_CHECK_EQUAL(rmdp.get_state(0).action_count(), 1);
    BOOST_CHECK_EQUAL(rmdp.get_state(0).get_action(0).outcome_count(), 2);

    vector<prec_t> iv(rmdp.state_count(),0.0);

    auto&& so = mpi_jac(rmdp, 0.9, iv, uniform_nature(rmdp, optimistic_unbounded, 0.0), 100, 0.0, 10, 0.0);
    BOOST_CHECK_CLOSE(so.valuefunction[0], 20, 1e-3);

    auto&& sr = mpi_jac(rmdp,0.9,iv,uniform_nature(rmdp,robust_unbounded, 0.0), 100,0.0,10,0.0);
    BOOST_CHECK_CLOSE(sr.valuefunction[0], 10, 1e-3);
}

BOOST_AUTO_TEST_CASE( small_construct_mdpi_r ) {

    auto mdp = make_shared<MDP>();
    vector<long> observations{0,0,1};

    Transition initial(vector<long>{0,1,2},vector<prec_t>{1.0/3.0,1.0/3.0,1.0/3.0},
                        vector<prec_t>{0,0,0});

    // action 0
    add_transition(*mdp,0,0,0,0.5,1.0);
    add_transition(*mdp,0,0,1,0.5,1.0);

    add_transition(*mdp,1,0,0,0.5,2.0);
    add_transition(*mdp,1,0,1,0.5,2.0);

    add_transition(*mdp,2,0,2,1.0,1.2);

    // action 1
    add_transition(*mdp,0,1,2,1.0,1.2);
    add_transition(*mdp,1,1,2,1.0,1.2);

    BOOST_TEST_CHECKPOINT("Constructing MDPI_R.");
    MDPI_R imr(const_pointer_cast<const MDP>(mdp), observations, initial);

    // Copy to change threshold
    auto rmdp = imr.get_robust_mdp();

    BOOST_TEST_CHECKPOINT("Checking MDP properties.");
    BOOST_CHECK_EQUAL(rmdp.state_count(), 2);
    BOOST_CHECK_EQUAL(rmdp.get_state(0).action_count(), 2);
    BOOST_CHECK_EQUAL(rmdp.get_state(1).action_count(), 1);
    BOOST_CHECK_EQUAL(rmdp.get_state(0).get_action(0).outcome_count(), 2);
    BOOST_CHECK_EQUAL(rmdp.get_state(0).get_action(1).outcome_count(), 2);
    BOOST_CHECK_EQUAL(rmdp.get_state(1).get_action(0).outcome_count(), 1);

    vector<prec_t> iv(rmdp.state_count(),0.0);

    vector<prec_t> target_v_opt{20.0,12.0};
    vector<prec_t> target_v_rob{12.0,12.0};

    BOOST_TEST_CHECKPOINT("Solving RMDP");
    auto&& so = mpi_jac(rmdp,0.9,iv,uniform_nature(rmdp,optimistic_unbounded, 0.0),100,0.0,10,0.0);
    CHECK_CLOSE_COLLECTION(so.valuefunction, target_v_opt, 1e-3);

    auto&& sr = mpi_jac(rmdp,0.9,iv,uniform_nature(rmdp,robust_unbounded, 0.0),100,0.0,10,0.0);
    CHECK_CLOSE_COLLECTION(sr.valuefunction, target_v_rob, 1e-3);
}

BOOST_AUTO_TEST_CASE( small_reweighted_solution ) {

    auto mdp = make_shared<MDP>();
    vector<long> observations({0,0,1});
    Transition initial(vector<long>{0,1,2},vector<prec_t>{1.0/3.0,1.0/3.0,1.0/3.0},
                        vector<prec_t>{0,0,0});

    // action 0
    add_transition(*mdp,0,0,0,0.5,1.0);
    add_transition(*mdp,0,0,1,0.5,1.0);

    add_transition(*mdp,1,0,0,0.5,2.0);
    add_transition(*mdp,1,0,1,0.5,2.0);

    add_transition(*mdp,2,0,2,1.0,1.2);

    // action 1
    add_transition(*mdp,0,1,2,1.0,1.2);
    add_transition(*mdp,1,1,2,1.0,1.2);

    BOOST_TEST_CHECKPOINT("Constructing MDPI_R.");
    MDPI_R imr(const_pointer_cast<const MDP>(mdp), observations, initial);

    BOOST_TEST_CHECKPOINT("Solving MDPI_R.");
    auto&& pol = imr.solve_reweighted(10, 0.9);

    indvec polvec{0,0};
    BOOST_CHECK_EQUAL_COLLECTIONS(pol.begin(), pol.end(),polvec.begin(),polvec.end());

    auto&& pol2 = imr.solve_robust(10, 0.0, 0.9);
    BOOST_CHECK_EQUAL_COLLECTIONS(pol2.begin(), pol2.end(),polvec.begin(),polvec.end());

    //auto retval = imr.total_return(pol, 0.99);
    //cout << "Return: " << retval << endl;

    //ostream_iterator<prec_t> output(cout, ", ");
    //copy(pol.begin(), pol.end(), output);
}

BOOST_AUTO_TEST_CASE(simple_mdpo_save_load_save_load) {
    MDP&& rmdp1 = make_chain1();

    Transition initial(indvec{0,1,2},numvec{1.0/3.0,1.0/3.0,1/3.0});
    indvec state2obs{0,0,1};

    MDPI mdpi1(rmdp1, state2obs, initial);

    stringstream store1, store2, store3;

    mdpi1.to_csv(store1,store2,store3);
    store1.seekg(0); store2.seekg(0); store3.seekg(0);

    auto&& string11 = store1.str();
    auto&& string12 = store2.str();
    auto&& string13 = store3.str();

    auto mdpi2 = MDPI::from_csv(store1,store2,store3);

    stringstream store21, store22, store23;

    mdpi2->to_csv(store21, store22, store23);

    auto&& string21 = store21.str();
    auto&& string22 = store22.str();
    auto&& string23 = store23.str();

    BOOST_CHECK_EQUAL(string11, string21);
    BOOST_CHECK_EQUAL(string12, string22);
    BOOST_CHECK_EQUAL(string13, string23);
}

BOOST_AUTO_TEST_CASE(simple_mdpor_save_load_save_load) {
    MDP&& rmdp1 = make_chain1();

    Transition initial(indvec{0,1,2},numvec{1.0/3.0,1.0/3.0,1/3.0});
    indvec state2obs{0,0,1};

    MDPI_R mdpi1(rmdp1, state2obs, initial);

    stringstream store1, store2, store3;

    mdpi1.to_csv(store1,store2,store3);
    store1.seekg(0); store2.seekg(0); store3.seekg(0);

    auto&& string11 = store1.str();
    auto&& string12 = store2.str();
    auto&& string13 = store3.str();

    auto mdpi2 = MDPI_R::from_csv(store1,store2,store3);

    stringstream store21, store22, store23;

    mdpi2->to_csv(store21, store22, store23);

    auto&& string21 = store21.str();
    auto&& string22 = store22.str();
    auto&& string23 = store23.str();

    BOOST_CHECK_EQUAL(string11, string21);
    BOOST_CHECK_EQUAL(string12, string22);
    BOOST_CHECK_EQUAL(string13, string23);
}

using namespace craam::msen;

/*
template<class T>
void print_vector(vector<T> vec){
    for(auto&& p : vec){
        cout << p << " ";
    }
}*/

BOOST_AUTO_TEST_CASE(implementable_from_samples){
    const int terminal_state = 10;

    CounterTerminal sim(0.9,0,terminal_state,1);
    RandomPolicy<CounterTerminal> random_pol(sim,1);

    auto samples = make_samples<CounterTerminal>();
    simulate(sim,samples,random_pol,50,50);
    simulate(sim,samples,[](int){return 1;},10,20);
    simulate(sim,samples,[](int){return -1;},10,20);

    SampleDiscretizerSI<typename CounterTerminal::State,
                        typename CounterTerminal::Action> sd;
    // initialize action values
    sd.add_action(-1); sd.add_action(+1);
    //initialize state values
    for(auto i : range(-terminal_state,terminal_state)) sd.add_state(i);

    sd.add_samples(samples);

    BOOST_CHECK_EQUAL(samples.get_initial().size(), sd.get_discrete()->get_initial().size());
    BOOST_CHECK_EQUAL(samples.size(), sd.get_discrete()->size());

    SampledMDP smdp;
    smdp.add_samples(*sd.get_discrete());
    auto mdp = smdp.get_mdp();
    auto&& initial = smdp.get_initial();

    auto&& sol = mpi_jac(*mdp,0.9);

    //cout << "Optimal policy: " << endl; print_vector(sol.policy); cout << endl;

    BOOST_CHECK_CLOSE(sol.total_return(initial), 51.313973553, 1e-3);

    // define observations
    indvec observations(mdp->state_count(), -1);
    size_t last_obs(0), inobs(0);
    for(auto i : range(size_t(0), mdp->state_count())){
        // check if this is a terminal state
        if(mdp->get_state(i).action_count() == 0 || inobs >= 2){
            if(inobs > 0){
                inobs = 0;
                last_obs++;
            }
            observations[i] = last_obs++;
        }else {
            observations[i] = last_obs;
            inobs++;
        }
        //cout << " " << observations[i] ;
    }
    //cout << endl;

    MDPI_R mdpi(mdp, observations, initial);
    auto&& randompolicy = mdpi.random_policy(25);

    auto isol = mdpi.solve_reweighted(0, 0.9, randompolicy);
    BOOST_CHECK_EQUAL_COLLECTIONS(randompolicy.begin(), randompolicy.end(), isol.begin(), isol.end());
    isol = mdpi.solve_robust(0, 0.0, 0.9, randompolicy);
    BOOST_CHECK_EQUAL_COLLECTIONS(randompolicy.begin(), randompolicy.end(), isol.begin(), isol.end());

    isol = mdpi.solve_reweighted(1, 0.9, randompolicy);

    auto sol_impl = mpi_jac(*mdp, 0.9, numvec(0), PlainBellman(mdpi.obspol2statepol(isol)));

    BOOST_CHECK_CLOSE(sol_impl.total_return(initial), 51.3135, 1e-3);
    BOOST_CHECK_CLOSE(mdpi.total_return(0.9), 51.3135, 1e-3);

    isol = mdpi.solve_robust(1, 0.0, 0.9, randompolicy);
    sol_impl = mpi_jac(*mdp, 0.9, numvec(0), PlainBellman(mdpi.obspol2statepol(isol)));

    BOOST_CHECK_CLOSE(sol_impl.total_return(initial), 51.3135, 1e-3);
    BOOST_CHECK_CLOSE(mdpi.total_return(0.9), 51.3135, 1e-3);
}

BOOST_AUTO_TEST_CASE(test_return_of_implementable){
    // test return with different initial states

    const prec_t gamma = 0.99;

    MDP&& mdp = make_chain1();
    indvec observations = {0,0,0};
    Transition  initial1(numvec({1.0, 0.0, 0.0})),
                initial2(numvec({0.0, 1.0, 0.0})),
                initial3(numvec({0.0, 0.0, 1.0}));

    MDPI mdpi1(mdp, observations, initial1);
    BOOST_CHECK_CLOSE(mdpi1.total_return(gamma, 1e-5), 1.1*pow(gamma,2)/(1-gamma), 1e-3);
    MDPI mdpi2(mdp, observations, initial2);
    BOOST_CHECK_CLOSE(mdpi2.total_return(gamma, 1e-5), 1.1*pow(gamma,1)/(1-gamma), 1e-3);
    MDPI mdpi3(mdp, observations, initial3);
    BOOST_CHECK_CLOSE(mdpi3.total_return(gamma, 1e-5), 1.1*pow(gamma,0)/(1-gamma), 1e-3);
}

// TODO: make sure there is a test that checks that the return of the implementable policy with
// the true weights has the same return as the true MDP.


#include <Eigen/Dense>
#include <iostream>

using namespace Eigen;
using namespace std;

int main(int, char**)
{
  cout.precision(3);
  Array44i a = Array44i::Random();
cout << "Here is the array a:" << endl << a << endl;
cout << "Here is a.rightCols(2):" << endl;
cout << a.rightCols(2) << endl;
a.rightCols(2).setZero();
cout << "Now the array a is:" << endl << a << endl;

  return 0;
}


/*
 * matrix_equiv.cc
 * Copyright 2015 John Lawson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "matrix_equiv.h"

#include <boost/functional/hash.hpp>

namespace ptope {
std::size_t
MEquivHash::operator()(const arma::mat & m) const {
	static auto rnd = [](double const& val)->std::size_t{return std::lround(val * 1e5);};
	std::size_t hash = 113;
	m_sums.resize(m.n_rows);
	for(arma::uword i = 0, max = m.n_cols; i < max; ++i) {
		m_sums[i].first = rnd(std::accumulate(m.begin_col(i), m.end_col(i),
				static_cast<double>(0),
				[](double const& init, double const& val){return init + rnd(val);}));
		m_sums[i].second = rnd(std::accumulate(m.begin_col(i), m.end_col(i),
				static_cast<double>(0),
				[](double const& init, double const& val){return init + (rnd(val) * rnd(val));}));
	}
	std::sort(m_sums.begin(), m_sums.end());
	boost::hash_combine(hash, m_sums);
	return hash;
}
bool
MEquivEqual::operator()(const arma::mat & lhs, const arma::mat & rhs) const {
	bool result = false;
	if(lhs.n_cols == rhs.n_cols) {
		__perm.reserve(lhs.n_cols);
		compatible_vectors(lhs, rhs, __comp);
		result = check_permutation(lhs, rhs, __perm, 0, __comp);
		__perm.clear();
	}
	return result;
}
void
MEquivEqual::sums(const arma::mat & m, std::vector<double> & out) const {
	out.reserve(m.n_cols);
	for(arma::uword i = 0, max = m.n_cols; i < max; ++i) {
		out[i] = std::accumulate(m.begin_col(i), m.end_col(i), static_cast<double>(0));
	}
}
void
MEquivEqual::compatible_vectors(const arma::mat & lhs, const arma::mat & rhs,
		std::vector<std::vector<arma::uword>> & result) const {
	for(auto & vec : result) {
		vec.clear();
	}
	for(std::size_t i = result.size(), max = lhs.n_cols; i < max; ++i) {
		result.push_back(std::vector<arma::uword>());
	}
	sums(lhs, __l_sum);
	sums(rhs, __r_sum);
	for(arma::uword j = 0, max = lhs.n_cols; j < max; ++j) {
		for(arma::uword i = 0; i < max; ++i) {
			if(_d_eq(__l_sum[j], __r_sum[i])) result[j].push_back(i);
		}
	}
}
bool
MEquivEqual::check_permutation(const arma::mat & lhs, const arma::mat & rhs,
		std::vector<arma::uword> & perm, std::size_t index,
		const std::vector<std::vector<arma::uword>> & comp) const {
	bool result = false;
	if(index == lhs.n_cols) {
		// Have complete permutation
		result = true;
	} else {
		// Add another entry to the permutation
		for(const arma::uword & map : comp[index]) {
			if(result) break;
			// Check that the new map value is not already in perm
			if(std::find(perm.begin(), perm.begin() + index, map)
					== perm.begin() + index) {
				// Check that the value gives correct permutation so far
				bool skip = false;
				double const * lhs_col_data = lhs.colptr(index);
				double const * rhs_col_data = rhs.colptr(map);
				for(size_t k = 0; !skip && k < index; ++k) {
					skip = !_d_eq(lhs_col_data[k], rhs_col_data[perm[k]]);
				}
				if(!skip) {
					perm[index] = map;
					result = check_permutation(lhs, rhs, perm, index + 1, comp);
				}
			}
		}
	}
	return result;
}
bool
MColPermEquiv::operator()(const arma::mat & lhs, const arma::mat & rhs) const {
	bool result = false;
	if(lhs.n_cols == rhs.n_cols) {
		__perm.reserve(lhs.n_cols);
		compatible_vectors(lhs, rhs, __comp);
		result = check_permutation(lhs, rhs, __perm, 0, __comp);
		__perm.clear();
	}
	return result;
}
void
MColPermEquiv::sums(const arma::mat & m, std::vector<double> & out) const {
	out.reserve(m.n_cols);
	for(arma::uword i = 0, max = m.n_cols; i < max; ++i) {
		out[i] = std::accumulate(m.begin_col(i), m.end_col(i), static_cast<double>(0));
	}
}
void
MColPermEquiv::compatible_vectors(const arma::mat & lhs, const arma::mat & rhs,
		std::vector<std::vector<arma::uword>> & result) const {
	for(auto & vec : result) {
		vec.clear();
	}
	for(std::size_t i = result.size(), max = lhs.n_cols; i < max; ++i) {
		result.push_back(std::vector<arma::uword>());
	}
	sums(lhs, __l_sum);
	sums(rhs, __r_sum);
	for(arma::uword j = 0, max = lhs.n_cols; j < max; ++j) {
		for(arma::uword i = 0; i < max; ++i) {
			if(_d_eq(__l_sum[j], __r_sum[i])) result[j].push_back(i);
		}
	}
}
bool
MColPermEquiv::check_permutation(const arma::mat & lhs, const arma::mat & rhs,
		std::vector<arma::uword> & perm, std::size_t index,
		const std::vector<std::vector<arma::uword>> & comp) const {
	bool result = false;
	if(index == lhs.n_cols) {
		result = true;
	} else {
		// Add another entry to the permutation
		for(const arma::uword & map : comp[index]) {
			if(result) break;
			if(std::find(perm.begin(), perm.begin() + index, map)
					== perm.begin() + index) {
				bool skip = false;
				double const * lhs_col_data = lhs.colptr(index);
				double const * rhs_col_data = rhs.colptr(map);
				for(size_t k = 0; !skip && k < index; ++k) {
					skip = !_d_eq(lhs_col_data[k], rhs_col_data[k]);
				}
				if(!skip) {
					perm[index] = map;
					result = check_permutation(lhs, rhs, perm, index + 1, comp);
				}
			}
		}
	}
	return result;
}
}


#include <algorithm>
#include <boost/optional.hpp>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define REP(i, n) for(int i = 0, i##_MACRO = (n); i < i##_MACRO; i++)
#define RANGE(i, a, b) for(int i = (a), i##_MACRO = (b); i < i##_MACRO; i++)
#define EACH(e, a) for(auto&& e : a)
#define ALL(a) std::begin(a), std::end(a)
#define RALL(a) std::rbegin(a), std::rend(a)
#define FILL(a, n) memset((a), n, sizeof(a))
#define FILLZ(a) FILL(a, 0)
#define INT(x) (static_cast<int>(x))

using namespace std;

using ll = long long;
using VI = vector<int>;
using VI2D = vector<vector<int>>;

constexpr int INF = 2e9;
constexpr double EPS = 1e-10;
constexpr double PI = acos(-1.0);

constexpr int dx[] = {-1, 0, 1, 0};
constexpr int dy[] = {0, -1, 0, 1};

template <typename T>
constexpr int sign(T x) {
	return x < 0 ? -1 : x > 0 ? 1 : 0;
}

template <>
constexpr int sign(double x) {
	return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

template <typename T, typename U>
constexpr void chmax(T& m, U x) {
	m = max(m, x);
}

template <typename T, typename U>
constexpr void chmin(T& m, U x) {
	m = min(m, x);
}

template <typename T>
constexpr T square(T x) {
	return x * x;
}

int roundup_pow2(int n) {
	if(!(n & (n - 1))) {
		return n;
	}

	int i = 1;
	while((n >> i) != 0) {
		i++;
	}
	return 1 << i;
}

template <typename T>
class SegmentTree {
	using F = function<T(T, T)>;

	// 演算
	F merge;
	// 単位元
	T identity;
	vector<T> tree;
	size_t size;

	public:
	SegmentTree(const vector<T>& a, const F f, const T id)
		: tree(roundup_pow2(a.size()) * 2 - 1, id),
		  size(roundup_pow2(a.size())), merge(f), identity(id) {
		int offset = this->size - 1;
		for(int i = 0; i < a.size(); i++) {
			this->tree[i + offset] = a[i];
		}
		for(int i = offset - 1; i >= 0; i--) {
			this->tree[i] = this->apply(i);
		}
	}
	// モノイド(Z,+)
	SegmentTree(const vector<T> a)
		: SegmentTree(a, [](T a, T b) { return a + b; }, 0) {}

	// 更新
	// 関数の指定がなければ置き換え
	void update(const size_t index, const T value, const F f = [](T a, T b) {
		return b;
	}) {
		size_t i = index + size - 1;
		this->tree[i] = f(this->tree[i], value);
		while(i > 0) {
			i = (i - 1) / 2;
			this->tree[i] = this->apply(i);
		}
	}

	// 一点取得
	T find(const size_t index) { return this->tree[index + size - 1]; }

	// 区間取得
	T find(const size_t query_left, const size_t query_right) const {
		return this->find_impl(query_left, query_right, 0, 0, this->size);
	}

	private:
	T apply(size_t index) const {
		return this->merge(this->tree[index * 2 + 1],
						   this->tree[index * 2 + 2]);
	}

	T find_impl(size_t query_left,
				size_t query_right,
				size_t node,
				size_t node_left,
				size_t node_right) const {
		if(node_right <= query_left || query_right <= node_left) {
			return this->identity;
		}
		if(query_left <= node_left && node_right <= query_right) {
			return this->tree[node];
		}

		return this->merge(find_impl(query_left,
									 query_right,
									 node * 2 + 1,
									 node_left,
									 node_left + (node_right - node_left) / 2),
						   find_impl(query_left,
									 query_right,
									 node * 2 + 2,
									 node_left + (node_right - node_left) / 2,
									 node_right));
	}
};

int to_bitset(char c) {
	return 1 << (c - 'a');
}

int main() {
	int n;
	string s;
	cin >> n >> s;
	vector<int> v(n);
	REP(i, n) { v[i] = to_bitset(s[i]); }
	SegmentTree<int> st(v, [](int a, int b) { return a | b; }, 0);
	int q;
	cin >> q;
	VI results;
	REP(i, q) {
		int type;
		cin >> type;
		if(type == 1) {
			int iq;
			char c;
			cin >> iq >> c;
			st.update(iq - 1, to_bitset(c));
		} else {
			int l, r;
			cin >> l >> r;
			results.push_back(__builtin_popcount(st.find(l - 1, r)));
		}
	}
	EACH(e, results) { cout << e << endl; }
	return 0;
}