#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <math.h>
#include <omp.h>
#include <iomanip>

#include "mvnormal.h"
#include "macau.h"
#include "chol.h"
#include "linop.h"
#include "noisemodels.h"
#include "truncnorm.h"
extern "C" {
  #include <sparse.h>
}

using namespace std;
using namespace Eigen;

void ILatentPrior::sample_latents(FixedGaussianNoise & noise, Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat,
                    double mean_value, const Eigen::MatrixXd &samples, const int num_latent) {
  this->sample_latents(U, mat, mean_value, samples, noise.alpha, num_latent);
}

void ILatentPrior::sample_latents(AdaptiveGaussianNoise & noise, Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat,
                    double mean_value, const Eigen::MatrixXd &samples, const int num_latent) {
  this->sample_latents(U, mat, mean_value, samples, noise.alpha, num_latent);
}

void ILatentPrior::sample_latents(FixedGaussianNoise & noiseModel, MatrixData & matrixData,
                                std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  if (mode == 0) {
    this->sample_latents(noiseModel, *samples[0], matrixData.Yt, matrixData.mean_value, *samples[1], num_latent);
  } else {
    this->sample_latents(noiseModel, *samples[1], matrixData.Y,  matrixData.mean_value, *samples[0], num_latent);
  }
}

void ILatentPrior::sample_latents(AdaptiveGaussianNoise & noiseModel, MatrixData & matrixData,
                                std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  if (mode == 0) {
    this->sample_latents(noiseModel, *samples[0], matrixData.Yt, matrixData.mean_value, *samples[1], num_latent);
  } else {
    this->sample_latents(noiseModel, *samples[1], matrixData.Y,  matrixData.mean_value, *samples[0], num_latent);
  }
}

void ILatentPrior::sample_latents(ProbitNoise & noiseModel, MatrixData & matrixData,
                                std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  if (mode == 0) {
    this->sample_latents(noiseModel, *samples[0], matrixData.Yt, matrixData.mean_value, *samples[1], num_latent);
  } else {
    this->sample_latents(noiseModel, *samples[1], matrixData.Y,  matrixData.mean_value, *samples[0], num_latent);
  }
}

void ILatentPrior::sample_latents(FixedGaussianNoise& noiseModel, TensorData & data,
                                std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  sample_latents(noiseModel.alpha, data, samples, mode, num_latent);
}

void ILatentPrior::sample_latents(AdaptiveGaussianNoise& noiseModel, TensorData & data,
                            std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  sample_latents(noiseModel.alpha, data, samples, mode, num_latent);
}

/** BPMFPrior */
void BPMFPrior::sample_latents(Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat, double mean_value,
                    const Eigen::MatrixXd &samples, double alpha, const int num_latent) {
  const int N = U.cols();

#pragma omp parallel for schedule(dynamic, 2)
  for(int n = 0; n < N; n++) {
    sample_latent_blas(U, n, mat, mean_value, samples, alpha, mu, Lambda, num_latent);
  }
}

void BPMFPrior::update_prior(const Eigen::MatrixXd &U) {
  tie(mu, Lambda) = CondNormalWishart(U, mu0, b0, WI, df);
}

void BPMFPrior::init(const int num_latent) {
  mu.resize(num_latent);
  mu.setZero();

  Lambda.resize(num_latent, num_latent);
  Lambda.setIdentity();
  Lambda *= 10;

  // parameters of Inv-Whishart distribution
  WI.resize(num_latent, num_latent);
  WI.setIdentity();
  mu0.resize(num_latent);
  mu0.setZero();
  b0 = 2;
  df = num_latent;
}

void BPMFPrior::sample_latents(ProbitNoise & noise, Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat,
                               double mean_value, const Eigen::MatrixXd &samples, const int num_latent) {
  const int N = U.cols();

#pragma omp parallel for schedule(dynamic, 2)
  for(int n = 0; n < N; n++) {
    sample_latent_blas_probit(U, n, mat, mean_value, samples, mu, Lambda, num_latent);
  }

}

void BPMFPrior::sample_latents(ProbitNoise& noiseModel, TensorData & data,
                               std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  // TODO
  throw std::runtime_error("Unimplemented: sample_latents");
}

void sample_latent_tensor(std::unique_ptr<Eigen::MatrixXd> &U,
                          int n,
                          std::unique_ptr<SparseMode> & sparseMode,
                          VectorView<Eigen::MatrixXd> & view,
                          double mean_value,
                          double alpha,
                          Eigen::VectorXd & mu,
                          Eigen::MatrixXd & Lambda) {
  const int nmodes1 = view.size();
  const int num_latent = U->rows();

  MatrixXd MM(num_latent, num_latent);
  MM = Lambda;
  VectorXd rr = VectorXd::Zero(mu.size());

  Eigen::VectorXi & row_ptr = sparseMode->row_ptr;
  Eigen::MatrixXi & indices = sparseMode->indices;
  Eigen::VectorXd & values  = sparseMode->values;

  Eigen::MatrixXd* S0 = view.get(0);

  for (int j = row_ptr(n); j < row_ptr(n + 1); j++) {
    VectorXd col = S0->col(indices(j, 0));
    for (int m = 1; m < nmodes1; m++) {
      col.noalias() = col.cwiseProduct(view.get(m)->col(indices(j, m)));
    }

    MM.triangularView<Eigen::Lower>() += alpha * col * col.transpose();
    rr.noalias() += col * ((values(j) - mean_value) * alpha);
  }

  Eigen::LLT<MatrixXd> chol = MM.llt();
  if(chol.info() != Eigen::Success) {
    throw std::runtime_error("Cholesky Decomposition failed!");
  }

  rr.noalias() += Lambda * mu;
  chol.matrixL().solveInPlace(rr);
  for (int i = 0; i < num_latent; i++) {
    rr[i] += randn0();
  }
  chol.matrixU().solveInPlace(rr);
  U->col(n).noalias() = rr;
}

void BPMFPrior::sample_latents(double noisePrecision,
                               TensorData & data,
                               std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples,
                               const int mode,
                               const int num_latent) {
  auto& sparseMode = (*data.Y)[mode];
  auto& U = samples[mode];
  const int N = U->cols();
  VectorView<Eigen::MatrixXd> view(samples, mode);

#pragma omp parallel for schedule(dynamic, 2)
  for (int n = 0; n < N; n++) {
    sample_latent_tensor(U, n, sparseMode, view, data.mean_value, noisePrecision, mu, Lambda);
  }
}

/** MacauPrior */
template<class FType>
void MacauPrior<FType>::init(const int num_latent, std::unique_ptr<FType> &Fmat, bool comp_FtF) {
  mu.resize(num_latent);
  mu.setZero();

  Lambda.resize(num_latent, num_latent);
  Lambda.setIdentity();
  Lambda *= 10;

  // parameters of Inv-Whishart distribution
  WI.resize(num_latent, num_latent);
  WI.setIdentity();
  mu0.resize(num_latent);
  mu0.setZero();
  b0 = 2;
  df = num_latent;

  // side information
  F = std::move(Fmat);
  use_FtF = comp_FtF;
  if (use_FtF) {
    FtF.resize(F->cols(), F->cols());
    At_mul_A(FtF, *F);
  }

  Uhat.resize(num_latent, F->rows());
  Uhat.setZero();

  beta.resize(num_latent, F->cols());
  beta.setZero();

  // initial value (should be determined automatically)
  lambda_beta = 5.0;
  // Hyper-prior for lambda_beta (mean 1.0, var of 1e+3):
  lambda_beta_mu0 = 1.0;
  lambda_beta_nu0 = 1e-3;
}

template<class FType>
void MacauPrior<FType>::sample_latents(Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat, double mean_value,
                    const Eigen::MatrixXd &samples, double alpha, const int num_latent) {
  const int N = U.cols();
#pragma omp parallel for schedule(dynamic, 2)
  for(int n = 0; n < N; n++) {
    // TODO: try moving mu + Uhat.col(n) inside sample_latent for speed
    sample_latent_blas(U, n, mat, mean_value, samples, alpha, mu + Uhat.col(n), Lambda, num_latent);
  }
}

template<class FType>
void MacauPrior<FType>::sample_latents(ProbitNoise& noiseModel, TensorData & data,
                               std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples, int mode, const int num_latent) {
  // TODO:
}

template<class FType>
void MacauPrior<FType>::sample_latents(double noisePrecision,
                                       TensorData & data,
                                       std::vector< std::unique_ptr<Eigen::MatrixXd> > & samples,
                                       int mode,
                                       const int num_latent) {
  auto& sparseMode = (*data.Y)[mode];
  auto& U = samples[mode];
  const int N = U->cols();
  VectorView<Eigen::MatrixXd> view(samples, mode);

#pragma omp parallel for schedule(dynamic, 2)
  for (int n = 0; n < N; n++) {
    Eigen::VectorXd mu2 = mu + Uhat.col(n);
    sample_latent_tensor(U, n, sparseMode, view, data.mean_value, noisePrecision, mu2, Lambda);
  }
}

template<class FType>
void MacauPrior<FType>::update_prior(const Eigen::MatrixXd &U) {
  // residual (Uhat is later overwritten):
  Uhat.noalias() = U - Uhat;
  MatrixXd BBt = A_mul_At_combo(beta);
  // sampling Gaussian
  tie(mu, Lambda) = CondNormalWishart(Uhat, mu0, b0, WI + lambda_beta * BBt, df + beta.cols());
  sample_beta(U);
  compute_uhat(Uhat, *F, beta);
  lambda_beta = sample_lambda_beta(beta, Lambda, lambda_beta_nu0, lambda_beta_mu0);
}

template<class FType>
double MacauPrior<FType>::getLinkNorm() {
  return beta.norm();
}

/** Update beta and Uhat */
template<class FType>
void MacauPrior<FType>::sample_beta(const Eigen::MatrixXd &U) {
  const int num_feat = beta.cols();
  // Ft_y = (U .- mu + Normal(0, Lambda^-1)) * F + sqrt(lambda_beta) * Normal(0, Lambda^-1)
  // Ft_y is [ D x F ] matrix
  MatrixXd tmp = (U + MvNormal_prec_omp(Lambda, U.cols())).colwise() - mu;
  MatrixXd Ft_y = A_mul_B(tmp, *F) + sqrt(lambda_beta) * MvNormal_prec_omp(Lambda, num_feat);

  if (use_FtF) {
    MatrixXd K(FtF.rows(), FtF.cols());
    K.triangularView<Eigen::Lower>() = FtF;
    for (int i = 0; i < K.cols(); i++) {
      K(i,i) += lambda_beta;
    }
    chol_decomp(K);
    chol_solve_t(K, Ft_y);
    beta = Ft_y;
  } else {
    // BlockCG
    solve_blockcg(beta, *F, lambda_beta, Ft_y, tol, 32, 8);
  }
}

template<class FType>
void MacauPrior<FType>::sample_latents(ProbitNoise & noise, Eigen::MatrixXd &U, const Eigen::SparseMatrix<double> &mat,
                                       double mean_value, const Eigen::MatrixXd &samples, const int num_latent) {
    const int N = U.cols();
#pragma omp parallel for schedule(dynamic, 2)
  for(int n = 0; n < N; n++) {
    // TODO: try moving mu + Uhat.col(n) inside sample_latent for speed
    sample_latent_blas_probit(U, n, mat, mean_value, samples, mu + Uhat.col(n), Lambda, num_latent);
  }

}

void BPMFPrior::saveModel(std::string prefix) {
  writeToCSVfile(prefix + "-latentmean.csv", mu);
}

template<class FType>
void MacauPrior<FType>::saveModel(std::string prefix) {
  writeToCSVfile(prefix + "-latentmean.csv", mu);
  writeToCSVfile(prefix + "-link.csv", beta);
}

std::pair<double,double> posterior_lambda_beta(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu) {
  const int D = beta.rows();
  MatrixXd BB(D, D);
  A_mul_At_combo(BB, beta);
  double nux = nu + beta.rows() * beta.cols();
  double mux = mu * nux / (nu + mu * (BB.selfadjointView<Eigen::Lower>() * Lambda_u).trace() );
  double b   = nux / 2;
  double c   = 2 * mux / nux;
  return std::make_pair(b, c);
}

double sample_lambda_beta(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu) {
  auto gamma_post = posterior_lambda_beta(beta, Lambda_u, nu, mu);
  return rgamma(gamma_post.first, gamma_post.second);
}

/** global function */
void sample_latent(MatrixXd &s, int mm, const SparseMatrix<double> &mat, double mean_rating,
    const MatrixXd &samples, double alpha, const VectorXd &mu_u, const MatrixXd &Lambda_u,
    const int num_latent)
{
  // TODO: add cholesky update version
  MatrixXd MM = MatrixXd::Zero(num_latent, num_latent);
  VectorXd rr = VectorXd::Zero(num_latent);
  for (SparseMatrix<double>::InnerIterator it(mat, mm); it; ++it) {
    auto col = samples.col(it.row());
    MM.noalias() += col * col.transpose();
    rr.noalias() += col * ((it.value() - mean_rating) * alpha);
  }

  Eigen::LLT<MatrixXd> chol = (Lambda_u + alpha * MM).llt();
  if(chol.info() != Eigen::Success) {
    throw std::runtime_error("Cholesky Decomposition failed!");
  }

  rr.noalias() += Lambda_u * mu_u;
  chol.matrixL().solveInPlace(rr);
  for (int i = 0; i < num_latent; i++) {
    rr[i] += randn0();
  }
  chol.matrixU().solveInPlace(rr);
  s.col(mm).noalias() = rr;
}

void sample_latent_blas(MatrixXd &s, int mm, const SparseMatrix<double> &mat, double mean_rating,
    const MatrixXd &samples, double alpha, const VectorXd &mu_u, const MatrixXd &Lambda_u,
    const int num_latent)
{
  MatrixXd MM = Lambda_u;
  VectorXd rr = VectorXd::Zero(num_latent);
  for (SparseMatrix<double>::InnerIterator it(mat, mm); it; ++it) {
    auto col = samples.col(it.row());
    MM.triangularView<Eigen::Lower>() += alpha * col * col.transpose();
    rr.noalias() += col * ((it.value() - mean_rating) * alpha);
  }

  Eigen::LLT<MatrixXd> chol = MM.llt();
  if(chol.info() != Eigen::Success) {
    throw std::runtime_error("Cholesky Decomposition failed!");
  }

  rr.noalias() += Lambda_u * mu_u;
  chol.matrixL().solveInPlace(rr);
  for (int i = 0; i < num_latent; i++) {
    rr[i] += randn0();
  }
  chol.matrixU().solveInPlace(rr);
  s.col(mm).noalias() = rr;
}

void sample_latent_blas_probit(MatrixXd &s, int mm, const SparseMatrix<double> &mat, double mean_rating,
    const MatrixXd &samples, const VectorXd &mu_u, const MatrixXd &Lambda_u,
    const int num_latent)
{
    MatrixXd MM = Lambda_u;
    VectorXd rr = VectorXd::Zero(num_latent);
    double z;
    auto u = s.col(mm);
    for (SparseMatrix<double>::InnerIterator it(mat, mm); it; ++it) {
      auto col = samples.col(it.row());
      MM.triangularView<Eigen::Lower>() += col * col.transpose();
			double y = 2 * it.value() - 1;
      z = y * rand_truncnorm(y * col.dot(u), 1.0, 0.0);
      rr.noalias() += col * z;
    }
  Eigen::LLT<MatrixXd> chol = MM.llt();
  if(chol.info() != Eigen::Success) {
    throw std::runtime_error("Cholesky Decomposition failed!");
  }

  rr.noalias() += Lambda_u * mu_u;
  chol.matrixL().solveInPlace(rr);
  for (int i = 0; i < num_latent; i++) {
    rr[i] += randn0();
  }
  chol.matrixU().solveInPlace(rr);
  s.col(mm).noalias() = rr;
}

/**
 * X = A * B
 */
Eigen::MatrixXd A_mul_B(Eigen::MatrixXd & A, Eigen::MatrixXd & B) {
  MatrixXd out(A.rows(), B.cols());
  A_mul_B_blas(out, A, B);
  return out;
}

Eigen::MatrixXd A_mul_B(Eigen::MatrixXd & A, SparseFeat & B) {
  MatrixXd out(A.rows(), B.cols());
  A_mul_Bt(out, B.Mt, A);
  return out;
}

Eigen::MatrixXd A_mul_B(Eigen::MatrixXd & A, SparseDoubleFeat & B) {
  MatrixXd out(A.rows(), B.cols());
  A_mul_Bt(out, B.Mt, A);
  return out;
}

MacauPrior<Eigen::MatrixXd>* make_dense_prior(int nlatent, double* ptr, int nrows, int ncols, bool colMajor, bool comp_FtF) {
	MatrixXd* Fmat = new MatrixXd(0, 0);
	if (colMajor) {
		*Fmat = Map<Matrix<double, Dynamic, Dynamic, ColMajor> >(ptr, nrows, ncols);
	} else {
		*Fmat = Map<Matrix<double, Dynamic, Dynamic, RowMajor> >(ptr, nrows, ncols);
	}
	unique_ptr<MatrixXd> Fmat_ptr = unique_ptr<MatrixXd>(Fmat);
	return new MacauPrior<MatrixXd>(nlatent, Fmat_ptr, comp_FtF);
}

template class MacauPrior<SparseFeat>;
template class MacauPrior<SparseDoubleFeat>;
template class MacauPrior<Eigen::MatrixXd>;


#include <memory>
#include <vector>

#include <boost/shared_ptr.hpp>

#include <CGAL/Straight_skeleton_2.h>
#include <CGAL/create_offset_polygons_2.h>
#include <CGAL/create_straight_skeleton_2.h>
#include <CGAL/create_straight_skeleton_from_polygon_with_holes_2.h>

#include <jlcxx/module.hpp>
#include <jlcxx/smart_pointers.hpp>

#include <julia.h>

#include "polygon_2.hpp"
#include "utils.hpp"

namespace jlcgal {

template<typename T>
std::shared_ptr<T>
to_std(boost::shared_ptr<T> ptr) {
  return std::shared_ptr<T>(ptr.get(), [ptr](T*) mutable { ptr.reset(); });
}

template<typename Polygon>
jlcxx::Array<Polygon>
to_poly_jlarr(std::vector<boost::shared_ptr<Polygon>> ps) {
  jlcxx::Array<Polygon> jlarr;
  for (const auto& poly : ps) jlarr.push_back(Polygon(*poly));
  return jlarr;
}

void wrap_straight_skeleton_2(jlcxx::Module& cgal) {
  typedef CGAL::Straight_skeleton_2<Kernel> Skeleton_2;

  typedef CGAL::HalfedgeDS_in_place_list_face<Skeleton_2::Face>         Face;
  typedef CGAL::HalfedgeDS_in_place_list_halfedge<Skeleton_2::Halfedge> Halfedge;
  typedef CGAL::HalfedgeDS_in_place_list_vertex<Skeleton_2::Vertex>     Vertex;

  const std::string skel_2_name = "StraightSkeleton2";

  auto ssf = cgal.add_type<Face>    (skel_2_name + "Face");
  auto ssh = cgal.add_type<Halfedge>(skel_2_name + "Halfedge");
  auto ssv = cgal.add_type<Vertex>  (skel_2_name + "Vertex");

  ssf
    // Access Functions
    .method("id", &Face::id)
    .method("halfedge", [](const Face& f) { return *f.halfedge(); })
    ;

  ssh
    // Access Functions
    .method("id",       &Halfedge::id)
    .method("opposite", [](const Halfedge& h) { return *h.opposite(); })
    .method("next",     [](const Halfedge& h) { return *h.next(); })
    .method("prev",     [](const Halfedge& h) { return *h.prev(); })
    .method("vertex",   [](const Halfedge& h) { return *h.vertex(); })
    .method("face",     [](const Halfedge& h) { return *h.face(); })
    .method("defining_contour_edge", [](const Halfedge& h) {
      return *h.defining_contour_edge();
    })
    // Predicates
    .method("has_null_segment",  &Halfedge::has_null_segment)
    .method("has_infinite_time", &Halfedge::has_infinite_time)
    .method("is_border",         &Halfedge::is_border)
    .method("is_bisector",       &Halfedge::is_bisector)
    .method("is_inner_bisector", &Halfedge::is_inner_bisector)
    .method("slope",             &Halfedge::slope)
    ;

  ssv
    // Access Functions
    .method("id",       &Vertex::id)
    .method("degree",   &Vertex::degree)
    .method("halfedge", [](const Vertex& v) { return *v.halfedge(); })
    .method("point",    &Vertex::point)
    ;
  cgal.set_override_module(jl_base_module);
  ssv
    .method("time", &Vertex::time)
    ;
  cgal.unset_override_module();
  ssv
    .method("primary_bisector", [](const Vertex& v) {
      return *v.primary_bisector();
    })
    // Queries
    .method("has_infinite_time", &Vertex::has_infinite_time)
    .method("has_null_point",    &Vertex::has_null_point)
    .method("is_contour",        &Vertex::is_contour)
    .method("is_skeleton",       &Vertex::is_skeleton)
    .method("is_split",          &Vertex::is_split)
    ;

  cgal.add_type<Skeleton_2>(skel_2_name)
    // Access Member Functions
    .method("size_of_faces",     &Skeleton_2::size_of_faces)
    .method("size_of_halfedges", &Skeleton_2::size_of_halfedges)
    .method("size_of_vertices",  &Skeleton_2::size_of_vertices)
    .method("faces", [](const Skeleton_2& s) {
      return collect(s.faces_begin(), s.faces_end());
    })
    .method("halfedges", [](const Skeleton_2& s) {
      return collect(s.halfedges_begin(), s.halfedges_end());
    })
    .method("vertices", [](const Skeleton_2& s) {
      return collect(s.vertices_begin(), s.vertices_end());
    })
    // Predicates
    .method("is_valid", &Skeleton_2::is_valid)
    ;

  cgal.method("create_exterior_straight_skeleton_2",
              [](const FT& max_offset, const Polygon_2& poly) {
    return to_std(CGAL::create_exterior_straight_skeleton_2(max_offset,
                                                            poly,
                                                            Kernel()));
  });
  cgal.method("create_exterior_straight_skeleton_2",
              [](const FT& max_offset, jlcxx::ArrayRef<Point_2> ps) {
    // because `bbox_2` uses operator-> from the input iterator... which
    // ArrayRef::iterator is missing, so we copy it into a vector.
    std::vector<Point_2> vps(ps.begin(), ps.end());
    return to_std(CGAL::create_exterior_straight_skeleton_2(max_offset,
                                                            vps.begin(),
                                                            vps.end(),
                                                            Kernel()));
  });
  cgal.method("create_interior_straight_skeleton_2", [](const Polygon_2& poly) {
    return to_std(CGAL::create_interior_straight_skeleton_2(poly, Kernel()));
  });
  cgal.method("create_interior_straight_skeleton_2",
              [](const Polygon_with_holes_2& poly) {
    return to_std(CGAL::create_interior_straight_skeleton_2(poly));
  });
  cgal.method("create_interior_straight_skeleton_2",
              [](jlcxx::ArrayRef<Point_2> contour) {
    return to_std(CGAL::create_interior_straight_skeleton_2(contour.begin(),
                                                            contour.end(),
                                                            Kernel()));
  });
  cgal.method("create_interior_straight_skeleton_2",
              [](jlcxx::ArrayRef<Point_2> contour,
                 jlcxx::ArrayRef<Polygon_2> holes) {
    return to_std(CGAL::create_interior_straight_skeleton_2(contour.begin(),
                                                            contour.end(),
                                                            holes.begin(),
                                                            holes.end(),
                                                            Kernel()));
  });

  cgal.method("create_offset_polygons_2", [](const FT& offset,
                                             const Skeleton_2& ss) {
    return to_poly_jlarr(CGAL::create_offset_polygons_2<Polygon_2>(offset,
                                                                   ss,
                                                                   Kernel()));
  });
}

} // jlcgal


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//  Quantum Monte Carlo Simulation for Kitaev Models
//  written by: Tim Eschmann, June 2016
//  Modified version: February 2019
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//  The code skeleton of this file was derived from "ising_skeleton.cpp"
//  which is part of the ALPS libraries:
//
/*****************************************************************************
 *
 * ALPS Project: Algorithms and Libraries for Physics Simulations
 *
 * ALPS Libraries
 *
 * Copyright (C) 2003 by Brigitte Surer
 *                       and Jan Gukelberger
 *
 * This software is part of the ALPS libraries, published under the ALPS
 * Library License; you can use, redistribute it and/or modify it under
 * the terms of the license, either version 1 or (at your option) any later
 * version.
 *
 * You should have received a copy of the ALPS Library License along with
 * the ALPS Libraries; see the file LICENSE.txt. If not, the license is also
 * available from http://alps.comp-phys.org/.
 *
 * 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
/*****************************************************************************/
//  This software incorporates the Armadillo C++ Library
//  Armadillo C++ Linear Algebra Library
//  Copyright 2008-2020 Conrad Sanderson (http://conradsanderson.id.au)
//  Copyright 2008-2016 National ICT Australia (NICTA)
//  Copyright 2017-2020 Arroyo Consortium
//  Copyright 2017-2020 Data61, CSIRO

//  This product includes software developed by Conrad Sanderson (http://conradsanderson.id.au)
//  This product includes software developed at National ICT Australia (NICTA)
//  This product includes software developed at Arroyo Consortium
//  This product includes software developed at Data61, CSIRO

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

#include <iostream>
#include <sstream>
#include <vector>
#include <complex>
#include <cmath>
#include <armadillo>
#include <mpi.h>

#include <alps/scheduler/montecarlo.h>
#include <alps/alea.h>
#include <boost/random.hpp>
#include <boost/multi_array.hpp>
#include <boost/filesystem.hpp>

// Include functions package
#include "functions.hpp"

// Include the desired lattice (read in header file from .hpp folder)
#include "Lattices/honeycomb_8_sites.hpp"

#ifndef _temp_hpp_
#define _temp_hpp_
double calc_temp(double T_min, double T_max, int me, int np, std::string dist);
#endif

using namespace arma;

class Simulation
{
public:
    Simulation(int np, int me, double T_min, double T_max, double T, std::string dist, std::string output_file)
    // Define interaction matrix A, parameters and measurable observables:
    :   eng_(3*me) // Random generator engine (different seed for each replica)
    ,   rng_(eng_, dist_) // Random generator
    ,   np_(np) // # of processes
    ,   me_(me) // process number / parallelization rank
    ,   T_min(T_min) // minimal temperature
    ,   T_max(T_max) // maximal temperature
    ,   dist(dist) // Temperature distribution
    ,   temp_(T)  // Replica Temperature
    ,   beta_(1/T) // Inverse replica Temperature
    ,   ham() // Interaction matrix / Z2 configuration
    ,   N_() // # system sites (IMPORTANT: THIS HAS CHANGED W.R.T. FORMER VERSIONS !!!)
    ,   v_() // Vector with coordinates of nonzero matrix entries
    ,   length_() // Length of this vector
    ,   eigval() // Eigenvalues of interaction matrix
    ,   F() // Free energy
    ,   plaquettes() // matrix with elementary plaquettes
    ,   energy_("E") // Measurement data: energy
    ,   e2_("E2") // Measurement data: squared energy
    //,   e4_("E4") // Measurement data: energy^4
    ,   dE_db("dE_db") // Measurement data: dE / d(beta) = fermionic part of specific heat
    //,   p_("p") // Disorder
    ,   flux_real("Flreal") // Measurement data: average plaquet flux (real part)
    ,   flux_imag("Flimag") // "" (imaginary part)
    ,   flux_real_squared("Flreal2") // Measurement data: average plaquet flux (real part)
    ,   flux_imag_squared("Flimag2") // "" (imaginary part)
    ,   spin_corr("Spin_corr")
    ,   flip_rate() // Single flip acceptance rate
    ,   filename_(output_file) // Filename for data saving

    {

    // Locate nonzero entries of interaction matrix (.hpp file):
    v_ = non_zeros();
    length_ = v_.size();

    // Fill interaction matrix A with coefficients due to lattice symmetry (.hpp file)
    ham = randomize(def_matrix());

    N_ = size(ham)[0];

    // Vector of eigenvalues of A
    eigval = eig_sym(ham);

    F = free_en(eigval, beta_);

    plaquettes = create_plaquettes();

    // Initialize single flip acceptance to 0:
    flip_rate = 0;

    }

    // Replica Monte Carlo iteration
    void run(int n, int ntherm, int sweeps_per_swap, int sweeps_per_save)
    {
        engine_type loaded, saved; // needed to load and save random engine status
        sweeps_ = n;
        thermalization_ = ntherm; // thermalization steps

        double fr;
        int n_tot = n + ntherm;
        double tau_en, tau_fl; // autocorrelation times

        std::stringstream matrix_output; // needed for saving configurations
        matrix_output << "matrix_temp_" << 1/beta_ << ".saved";

        std::stringstream rng_save; // needed for saving random generator status
        rng_save << "rng_" << me_ << ".saved";

        //Load so-far-obtained measurement data (ALPS)
        if (boost::filesystem::exists(filename_))
        {
            load(filename_);
        }

        // Load random generator status:
        std::ifstream rngfile(rng_save.str().c_str(), std::ifstream::in);
        if(rngfile.good())
        {
            rngfile >> loaded;
            rngfile.close();
            eng_ = loaded;
        }

        // Load last Z2 configuration from file (-> skip thermalization)
        std::ifstream matfile(matrix_output.str().c_str(), std::ifstream::in);
        if(matfile.good())
            ham.load(matrix_output.str().c_str());

        // Thermalize for ntherm steps
        if (me_ == 1)
            std::cout << "Thermalizing ..." << std::endl;

        while(ntherm--)
        {
            step();

            if (ntherm % sweeps_per_swap == 0)
            {
                swap();

                if (((ntherm + n) / sweeps_per_swap) % sweeps_per_save == 0)
                {
                    // Calculate single flip acceptance rate and send it to Master process:
                    fr = double(flip_rate)/(double(n_tot - n - ntherm)*N_);
                    MPI_Send(&fr, 1, MPI_DOUBLE, 0, 5, MPI_COMM_WORLD);
                }
            }

            if (ntherm % sweeps_per_save == 0)
            {
                // Save Z2 configuration:
                ham.save(matrix_output.str().c_str());

                // Save random generator status:
                saved = eng_;
                std::ofstream file(rng_save.str().c_str(), std::ofstream::trunc);
                file << saved;

                // Tell that everything is saved ...
                if (me_ == 1)
                    std::cout << "SAVE " << ntherm << std::endl;

            }
        }

        if (me_ == 1)
        {
            std::cout << "###############################" << std::endl;
            std::cout << "Sweeping ..." << std::endl;
        }

        // Run n steps
        while(n--)
        {
            step();

            // Output eigenvalue and flux configurations:
            //outputZ2gauge();
            //output_eigenvalues();
            //output_flux_confs();

            // Measure observables:
            measure();

            // Swap:
            if (n % sweeps_per_swap == 0)
            {
                swap();

                if (((n) / sweeps_per_swap) % sweeps_per_save == 0)
                {
                    // Calculate single flip acceptance rate and send it to Master process:
                    fr = double(flip_rate)/(double(n_tot - n - ntherm)*N_);
                    MPI_Send(&fr, 1, MPI_DOUBLE, 0, 5, MPI_COMM_WORLD);
                }
            }

            // Save all simulation data:
            if (n % sweeps_per_save == 0)
            {
                // Save results:
                save(filename_);

                // Save Z2 configuration:
                ham.save(matrix_output.str().c_str());

                // Save random generator status:
                saved = eng_;
                std::ofstream file(rng_save.str().c_str(), std::ofstream::trunc);
                file << saved;

                // Tell that everything is saved ...
                if (me_ == 1)
                    std::cout << "SAVE " << n << std::endl;
            }
        }

        //Save the observables to file
        save(filename_);

        tau_en = energy_.tau();
        tau_fl = flux_real.tau();
        MPI_Send(&tau_en, 1, MPI_DOUBLE, 0, 6, MPI_COMM_WORLD);
        MPI_Send(&tau_fl, 1, MPI_DOUBLE, 0, 7, MPI_COMM_WORLD);

        // Print observables
	    /*
        std::cout << temp_ << std::endl;
	    std::cout.precision(17);
       	std::cout << energy_.name() << ":\t" << energy_.mean()
            << " +- " << energy_.error() << ";\ttau = " << energy_.tau()
            << ";\tconverged: " << alps::convergence_to_text(energy_.converged_errors())
            << std::endl;

	    std::cout << flux_real.name() << ":\t" << flux_real.mean()
            << " +- " << flux_real.error() << ";\ttau = " << flux_real.tau()
            << ";\tconverged: " << alps::convergence_to_te        //std::cout << double(counter)/double(tries) << std::endl;xt(flux_real.converged_errors())
            << std::endl;
        std::cout << flux_imag.name() << ":\t" << flux_imag.mean()
            << " +- " << flux_imag.error() << ";\ttau = " << flux_imag.tau()
            << ";\tconverged: " << alps::convergence_to_text(flux_imag.converged_errors())
            << std::endl;*/

    }

    // Iteration step (= "Metropolis sweep"):
    void step()
    {
        int kk, i, j; // Running indices
        cx_mat ham_new(N_,N_);
        vec eigval_new(N_); // Vector for eigenvalues of new matrix (= update proposal)

        double F_new; // New free energy

        int coord1, coord2; // Random bond coordinate
        double alpha, gamma; // Monte Carlo variables

        int count = 0;

        // One sweep = N tries (# lattice sites)
        for (kk = 0; kk < N_; kk++)
        {
            // Alternative matrix for sampling:
            ham_new = ham;

            // Switch sign of random matrix entry:
            int die = roll_die(length_);
            coord1 = v_[die]/(N_);
            coord2 = v_[die]%(N_);
            ham_new(coord1, coord2) *= -1;
            ham_new(coord2, coord1) *= -1;

            // Measure free energy of changed Hamiltonian:
            eigval_new = eig_sym(ham_new);
            F_new = free_en(eigval_new, beta_);

            // Accept change with probability according to Boltzmann distribution:
            //alpha = exp(- beta_ * (F_new - F));
            // Use Gibbs weights instead of Metropolis weights:
            alpha = 1./ (1. + exp(beta_ * (F_new - F)));
            gamma = rng_();

            // Accepted?
            if (gamma <= alpha) // accept
            {
                ham = ham_new;
                eigval = eigval_new;
                F = F_new;

                // Single flip acceptance rate + 1
                flip_rate += 1;
            }

            count += 1;
        }
    }

    // Does what it says ...
    void measure()
    {
        std::complex <double> fl;
        double E_, dE_, s;
        //double p;
        double fl_real;
        double fl_imag;
        double corr;

        // Measure energy etc.:
        E_ = en(eigval, beta_);
        dE_ = diffE(eigval, beta_);

        // Measure average flux per plaquet / disorder:
        fl = flux(ham, plaquettes);
        fl_real = std::real(fl);
        fl_imag = std::imag(fl);
        //p = get_p(ham, plaquettes);

        // Measure spin-spin correlation:
        corr = correlation(ham, v_, beta_);

        // Add sample to observables:
        energy_ << E_/double(N_); // Energy per site
        e2_ << E_/double(N_)*E_/double(N_); // Squared energy per site
        //e4_ << E_/double(N_)*E_/double(N_)*E_/double(N_)*E_/double(N_);
        dE_db << dE_ / double(N_); // dE/d(beta)
        //p_ << p;
        flux_real << fl_real;
        flux_imag << fl_imag;
        flux_real_squared << fl_real*fl_real;
        flux_imag_squared << fl_imag*fl_imag;
        spin_corr << corr;
    }

    ///////////////////////////////////////////////////////////////
    // Output Z2 gauge field configuration:
    ///////////////////////////////////////////////////////////////

    void outputZ2gauge()
    {
        int coord1, coord2;
        std::stringstream gauge_output; // needed for saving configurations
        gauge_output << "gauge_configuration_temp_" << 1/beta_ << ".saved";

        std::ofstream gauge(gauge_output.str().c_str(), std::ofstream::app);
        for (int j = 0; j < v_.size(); j++)
        {
            coord1 = v_[j]/N_;
            coord2 = v_[j]%N_;

            gauge << std::setprecision(17) << std::imag(ham(coord1, coord2) / std::abs(ham(coord1, coord2))) << "   ";
        }
        gauge << std::endl;
    }

    // Output flux configurations:
    void output_flux_confs()
    {
        cx_vec fl_confs = flux_confs(ham, plaquettes);

        std::stringstream flux_output; // needed for saving configurations
        flux_output << "flux_configuration_temp_" << 1/beta_ << ".saved";

        std::ofstream flux(flux_output.str().c_str(), std::ofstream::app);
        for(int iii = 0; iii < size(plaquettes)[0]; iii++)
        {
            // Switch between real and imaginary fluxes:
            flux << std::setprecision(17) << std::real(fl_confs[iii]) << "   ";
            //flux << std::setprecision(17) << std::imag(fl_confs[iii]) << std::endl;
        }
        flux << std::endl;
    }

    // Output eigenvalue configurations:
    void output_eigenvalues()
    {
        std::stringstream output; // needed for saving energies
        output << "eigenvalues_temp_" << 1/beta_ << ".saved";

        std::ofstream eig(output.str().c_str(), std::ofstream::app);
        for(int iii = 0; iii < size(eigval)[0]/2; iii++)
        {
            eig << std::setprecision(17) << eigval[iii] << "   ";
        }
        eig << std::endl;
    }

    // Swap replica with left neighbour ...
    void swapleft()
    {
        MPI_Status status;
        int control = 0;
        int jj,kk;
        double beta_alt = 1/calc_temp(T_min, T_max, me_ - 1, np_, dist);
        cx_mat H_a(N_,N_); // receive
        cx_mat H_b = ham; // send

        double f2 = -beta_alt * free_en(eigval, beta_alt);
        double f3 = beta_ * F;

        MPI_Send(&f2, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
        MPI_Send(&f3, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);

        MPI_Recv(&control, 1, MPI_INT, 0, 2, MPI_COMM_WORLD, &status);

        if (control == 1)
        {
            // Receive replica from left neighbour
            for (jj = 0; jj < N_; jj++)
            {
                for (kk = 0; kk < N_; kk++)
                {
                    MPI_Recv(&H_a(jj,kk), 1, MPI_DOUBLE_COMPLEX, me_- 1, 3, MPI_COMM_WORLD, &status);
                }
            }

            // Send own replica to left neighbour
            for (jj = 0; jj < N_; jj++)
            {
                for (kk = 0; kk < N_; kk++)
                {
                    MPI_Send(&H_b(jj,kk), 1, MPI_DOUBLE_COMPLEX, me_- 1, 4, MPI_COMM_WORLD);
                }
            }

            ham = H_a;
            eigval = eig_sym(ham);
            F = free_en(eigval, beta_);
        }
    }

    // Swap replica with right neighbour ...
    void swapright()
    {
        MPI_Status status;
        int control = 0;
        int jj, kk;
        double beta_alt = 1/calc_temp(T_min, T_max, me_ + 1, np_, dist);
        cx_mat H_b(N_,N_); // receive (here it's the other way round!!!)
        cx_mat H_a = ham; // send

        double f1 = -beta_alt * free_en(eigval, beta_alt);
        double f4 = beta_ * F;

        MPI_Send(&f1, 1, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);
        MPI_Send(&f4, 1, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);

        MPI_Recv(&control, 1, MPI_INT, 0, 1, MPI_COMM_WORLD, &status);

        if (control == 1)
        {
            // Send own replica to right neighbour
            for (jj = 0; jj < N_; jj++)
            {
                for (kk = 0; kk < N_; kk++)
                {
                    MPI_Send(&H_a(jj,kk), 1, MPI_DOUBLE_COMPLEX, me_+ 1, 3, MPI_COMM_WORLD);
                }
            }

            // Receive replica from right neighbour
            for (jj = 0; jj < N_; jj++)
            {
                for (kk = 0; kk < N_; kk++)
                {
                    MPI_Recv(&H_b(jj,kk), 1, MPI_DOUBLE_COMPLEX, me_+ 1, 4, MPI_COMM_WORLD, &status);
                }
            }

            ham = H_b;
            eigval = eig_sym(ham);
            F = free_en(eigval, beta_);
        }

    }

    // Parallel Tempering for each temperature point (= "Swap"")
    void swap()
    {
        if (me_ != 1)
            swapleft();
        if (me_ != np_ - 1)
            swapright();
    }

    // Master process for managing swaps:
    void master(int therm, int sweeps, int sweeps_per_save, int sweeps_per_swap)
    {
        MPI_Status status;
        engine_type loaded, saved; // needed to load and save random engine status
        int counts = (therm + sweeps)/sweeps_per_swap; // How many swaps in total?
        int control = 0;  // Signal for accepting / rejecting swap
        int sign_[np_];   // needed for ensemble optimization
        int nplus_[np_];  // n+ histogram
        int nminus_[np_]; // n- histogram
        double counter[np_]; // How many accepted swaps?
        double den = 0;
        int s_i, s_i_plus_1; // sign for each replica (was it at T_min or T_max latest?)
        double f1, f2, f3, f4; // free energy variables
        double alpha_pt, gamma_pt; // Monte Carlo variables

        double fr; // Single flip acceptance rate

        // Autocorrelation times:
        double tau_en;
        double tau_fl;

        std::stringstream rng_save; // needed for saving random generator status
        rng_save << "rng_" << me_ << ".saved";

        std::stringstream sfar; // needed for saving single flip acceptance rates
        sfar << "single_flip_rate.saved";

        std::stringstream nplus_ratio; // needed for saving ratio function f = n_plus / n_tot
        nplus_ratio << "n_plus_ratio.saved";

        std::stringstream swap_ratio; // needed for saving replica exchange ratio
        swap_ratio << "swap_ratio.saved";

        std::stringstream tau_energy; // needed for saving energy autocorrelation time
        tau_energy << "tau_energy.saved";

        std::stringstream tau_flux; // needed for saving flux autocorrelation time
        tau_flux << "tau_flux.saved";

        // Load random generator status:
        std::ifstream rngfile(rng_save.str().c_str(), std::ifstream::in);
        if(rngfile.good())
        {
            rngfile >> loaded;
            rngfile.close();
            eng_ = loaded;
        }

        // Initialize sign array and histograms
        for (int k = 0; k < np_; k++)
        {
            sign_[k] = 0;
            nplus_[k] = 0;
            nminus_[k] = 0;
            counter[k] = 0;
        }

        sign_[1] = 1;
        sign_[np_ - 1] = -1;

        // PT iteration
        while(counts--)
        {
            den += 2;

            // Regard temperature points from T_min to T_max
            for (int i = 1; i < np_ - 1; i++)
            {
                // Receive free energies from replicas
                MPI_Recv(&f1, 1, MPI_DOUBLE, i, 1, MPI_COMM_WORLD, &status);
                MPI_Recv(&f4, 1, MPI_DOUBLE, i, 1, MPI_COMM_WORLD, &status);
                MPI_Recv(&f2, 1, MPI_DOUBLE, i+1, 2, MPI_COMM_WORLD, &status);
                MPI_Recv(&f3, 1, MPI_DOUBLE, i+1, 2, MPI_COMM_WORLD, &status);

                // Decide if replicas are swapped
                alpha_pt = exp(f1 + f2 + f3 + f4);
		        gamma_pt = rng_();

                if (gamma_pt <= alpha_pt) // accept
                {
                    control = 1;

                    MPI_Send(&control, 1, MPI_INT, i, 1, MPI_COMM_WORLD);
                    MPI_Send(&control, 1, MPI_INT, i+1, 2, MPI_COMM_WORLD);

                    // Record histogram for ensemble optimization:
                    s_i = check_sign_i(i, np_,  sign_[i], sign_[i+1]);
                    s_i_plus_1 = check_sign_i_plus_1(i, np_,  sign_[i], sign_[i+1]);
                    sign_[i] = s_i;
                    sign_[i+1] = s_i_plus_1;
                    if (counts < sweeps + therm - 100) // Start recording after a couple of steps ...
                    {
                        if (s_i == 1)
                            nplus_[i] += 1;
                        else if (s_i = -1)
                            nminus_[i] += 1;
                        if (s_i_plus_1 == 1)
                            nplus_[i+1] += 1;
                        else if (s_i_plus_1 == -1)
                            nminus_[i+1] += 1;
                    }

                    // Record replica exchange rate:
                    counter[i] += 1;
                    counter[i+1] += 1;

                }
                else // refuse
                {
                    //std::cout << "NO SWAP " << i << " " << i + 1 << std::endl;
                    control = 0;
                    MPI_Send(&control, 1, MPI_INT, i, 1, MPI_COMM_WORLD);
                    MPI_Send(&control, 1, MPI_INT, i+1, 2, MPI_COMM_WORLD);
                }
            }

            if (counts % sweeps_per_save == 0)
            {
                // Give single flip acceptance rates as output ...
                std::ofstream sf__(sfar.str().c_str(), std::ofstream::trunc);
                for (int jj = 1; jj < np_; jj++)
                {
                    MPI_Recv(&fr, 1, MPI_DOUBLE, jj, 5, MPI_COMM_WORLD, &status);
                    sf__ << std::setprecision(17) << calc_temp(T_min, T_max, jj, np_, dist) << " " << fr << std::endl;
                }

                // Give histogram as output ...
                std::ofstream nplus__(nplus_ratio.str().c_str(), std::ofstream::trunc);
                for (int kk = 1; kk < np_ ; kk++)
                {
                    nplus__ << std::setprecision(17) << calc_temp(T_min, T_max, kk, np_, dist) << " " << nplus_[kk] / double(nplus_[kk] + nminus_[kk]) << std::endl;
                }

                // Give swap ratio as output ...
                std::ofstream swap_ratio__(swap_ratio.str().c_str(), std::ofstream::trunc);
                for (int ll = 1; ll < np_ ; ll++)
                {
                    swap_ratio__ << std::setprecision(17) << calc_temp(T_min, T_max, ll, np_, dist) << " " << counter[ll] / den << std::endl;
                }

                // Save random generator status:
                saved = eng_;
                std::ofstream file(rng_save.str().c_str(), std::ofstream::trunc);
                file << saved;
            }
        }

        // Give autocorrelation times as output ...
        std::ofstream tau1(tau_energy.str().c_str(), std::ofstream::trunc);
        std::ofstream tau2(tau_flux.str().c_str(), std::ofstream::trunc);
        for (int ll = 1; ll < np_; ll++)
        {
            MPI_Recv(&tau_en, 1, MPI_DOUBLE, ll, 6, MPI_COMM_WORLD, &status);
            tau1 << std::setprecision(17) << calc_temp(T_min, T_max, ll, np_, dist) << " " << tau_en << std::endl;

            MPI_Recv(&tau_fl, 1, MPI_DOUBLE, ll, 7, MPI_COMM_WORLD, &status);
            tau2 << std::setprecision(17) << calc_temp(T_min, T_max, ll, np_, dist) << " " << tau_fl << std::endl;
        }
    }

    void load(std::string const & filename)
    {
        alps::hdf5::archive ar(filename, "a");
        ar["/simulation/results/"+energy_.representation()] >> energy_;
        ar["/simulation/results/"+e2_.representation()] >> e2_;
        //ar["/simulation/results/"+e4_.representation()] >> e4_;
        ar["/simulation/results/"+dE_db.representation()] >> dE_db;
        //ar["/simulation/results/"+p_.representation()] >> p_;
        ar["/simulation/results/"+flux_real.representation()] >> flux_real;
        ar["/simulation/results/"+flux_imag.representation()] >> flux_imag;
        ar["/simulation/results/"+flux_real_squared.representation()] >> flux_real_squared;
        ar["/simulation/results/"+flux_imag_squared.representation()] >> flux_imag_squared;
        ar["/simulation/results/"+spin_corr.representation()] >> spin_corr;
        ar["/parameters/T"] >> temp_;
        ar["/parameters/BETA"] >> beta_;
        ar["/parameters/SWEEPS"] >> sweeps_;
        ar["/parameters/THERMALIZATION"] >> thermalization_;
    }

    void save(std::string const & filename)
    {
        alps::hdf5::archive ar(filename, "a");
        ar["/simulation/results/"+energy_.representation()] << energy_;
        ar["/simulation/results/"+e2_.representation()] << e2_;
        //ar["/simulation/results/"+e4_.representation()] << e4_;
        ar["/simulation/results/"+dE_db.representation()] << dE_db;
        //ar["/simulation/results/"+p_.representation()] << p_;
        ar["/simulation/results/"+flux_real.representation()] << flux_real;
        ar["/simulation/results/"+flux_imag.representation()] << flux_imag;
        ar["/simulation/results/"+flux_real_squared.representation()] << flux_real_squared;
        ar["/simulation/results/"+flux_imag_squared.representation()] << flux_imag_squared;
        ar["/simulation/results/"+spin_corr.representation()] << spin_corr;
        ar["/parameters/T"] << temp_;
        ar["/parameters/BETA"] << beta_;
        ar["/parameters/SWEEPS"] << sweeps_;
        ar["/parameters/THERMALIZATION"] << thermalization_;
    }

    /////////////////////////////////////////////////////////////////////
    // Randomize zz-entries of matrix (likewise: all nonzero entries): //
    /////////////////////////////////////////////////////////////////////

    cx_mat randomize(cx_mat ham)
    {
        int N_ = size(ham)[0];
        int die, coord1, coord2;

        for (int j = 0 ; j < 3*N_/2; j++)
        {
            die = roll_die(length_);
            coord1 = v_[die]/N_;
            coord2 = v_[die]%N_;

            // Flip a coin:
            if (rng_() < 0.5)
            {
                ham(coord1, coord2) *= -1;
                ham(coord2, coord1) *= -1;
            }
        }

        return ham;
    }

    /*cx_mat bond_randomize(cx_mat ham)
    {
        int N_ = size(ham)[0];
        int die, coord1, coord2;
        double dJ = 0.8;

        for (int j = 0 ; j < N_/4; j++)
        {
            die = roll_die(length_);
            coord1 = v_[die]/N_;
            coord2 = v_[die]%N_;

            // Flip a coin:
            if (rng_() < 0.5)
            {
                ham(coord1, coord2) -= dJ;
                ham(coord2, coord1) = -ham(coord1, coord2);
            }
            else
            {
                ham(coord1, coord2) += dJ;
                ham(coord2, coord1) = -ham(coord1, coord2);
            }
        }

        return ham;
    }*/

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

    protected:

    // Random int from the interval [0,max)
    int roll_die(int max) const
    {
        return static_cast<int>(max * rng_());
    }

private:
    typedef boost::mt19937 engine_type; // Mersenne twister
    typedef boost::uniform_real<> distribution_type;
    typedef boost::variate_generator<engine_type&, distribution_type> rng_type;
    engine_type eng_;
    distribution_type dist_;
    mutable rng_type rng_;

    size_t sweeps_;
    size_t thermalization_;

    // Everything here is described above:
    int np_;
    int me_;
    double T_min;
    double T_max;
    double temp_;
    double beta_;

    cx_mat ham;
    size_t N_;

    std::vector<int> v_;
    int length_;

    vec eigval;

    double F;

    Mat<int> plaquettes;

    // ALPS Observables:

    alps::RealObservable energy_;
    alps::RealObservable e2_;
    //alps::RealObservable e4_;
    alps::RealObservable dE_db;
    //alps::RealObservable p_;
    alps::RealObservable flux_real;
    alps::RealObservable flux_imag;
    alps::RealObservable flux_real_squared;
    alps::RealObservable flux_imag_squared;
    alps::RealObservable spin_corr;

    int flip_rate;

    signed int sign;

    std::string dist;
    std::string filename_;
};


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

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

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_RSQRT_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_RSQRT_HPP_INCLUDED
#include <boost/simd/function/fast.hpp>
#include <boost/simd/function/fast.hpp>
#include <boost/simd/function/fast.hpp>

#include <boost/simd/function/bitwise_cast.hpp>
#include <boost/dispatch/function/overload.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_OVERLOAD ( rsqrt_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::single_<A0> >
                          , boost::simd::fast_tag
                         )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0, fast_tag const& ) const BOOST_NOEXCEPT
    {
      typedef bd::as_integer_t<A0> i_t;

      // Quake III Arena RSQRT approximation
      i_t x = bitwise_cast<i_t>(a0);
      i_t y = 0x5f3759df - (x >> 1);

      // make negative values be NaN
      y |= x >> (sizeof(i_t)*CHAR_BIT-1);

      A0 x2 = a0 * 0.5f;
      A0 y2 = bitwise_cast<A0>(y);

      // Newton-Rhapson refinement steps: 2 NR steps for precision purpose
      y2    = y2 * ( 1.5f - ( x2 * y2 * y2 ) );
      return  y2 * ( 1.5f - ( x2 * y2 * y2 ) );
    }
  };

  BOOST_DISPATCH_OVERLOAD ( rsqrt_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::double_<A0> >
                          , boost::simd::fast_tag
                         )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0, fast_tag const& ) const BOOST_NOEXCEPT
    {
      return simd::rsqrt(a0);
    }
  };

} } }

#endif


#include <boost/test/unit_test.hpp>

#include <scmd.hh>

#include "value_factory.hh"
#include "sparse_matrix_value_generator.hh"
#include "../test_utils.hh"
#include "../fixture.hh"

BOOST_FIXTURE_TEST_SUITE(multiply_tests, matrix_fixture)

BOOST_AUTO_TEST_CASE(float_mm)
{
    using namespace scylla_blas;

    // print_matrix(*float_6x5);
    // print_matrix(*float_5x6);

    std::cerr << "Multiply" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(NoTrans, NoTrans, 1, *float_BxA, *float_AxB, 1, *float_BxB);
    // print_matrix(*float_6x6);

    std::cerr << "Multiply with coeff (2.5)" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(NoTrans, NoTrans, 2.5, *float_BxA, *float_AxB, 1, *float_BxB);
    // print_matrix(*float_6x6);

    std::cerr << "Multiply and add 3 times result" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(NoTrans, NoTrans, 1, *float_BxA, *float_AxB, 1, *float_BxB);
    scheduler->sgemm(NoTrans, NoTrans, 1, *float_BxA, *float_AxB, 3, *float_BxB);
    // print_matrix(*float_6x6);

    std::cerr << "Multiply transA" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(Trans, NoTrans, 1, *float_AxB, *float_AxB, 1, *float_BxB);
    // print_matrix(*float_6x6);

    std::cerr << "Multiply transB" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(NoTrans, Trans, 1, *float_BxA, *float_BxA, 1, *float_BxB);
    // print_matrix(*float_6x6);

    std::cerr << "Multiply transAB" << std::endl;
    float_BxB->clear_all();
    scheduler->sgemm(Trans, Trans, 1, *float_AxB, *float_BxA, 1, *float_BxB);
    // print_matrix(*float_6x6);
}

BOOST_AUTO_TEST_SUITE_END();


#include <dlib/opencv.h>
#include <opencv2/highgui/highgui.hpp>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>

#include <time.h>

#include <stdio.h>
#include <iostream>
using namespace dlib;
using namespace std;
using namespace cv;

image_window win;
shape_predictor sp;
std::vector<cv::Point> righteye;
std::vector<cv::Point> lefteye;
char c;
cv::Point p;
float times,timed;
float tmp_close = 0, tmp_open = 0, time_close = 0;
float end_close = 0, end_open = 0;
float time_open = 0;
int i = 0, j = 0 , bl = 0, err = 0;

double compute_EAR(std::vector<cv::Point> vec)
{
    double a = cv::norm(cv::Mat(vec[1]), cv::Mat(vec[5]));
    double b = cv::norm(cv::Mat(vec[2]), cv::Mat(vec[4]));
    double c = cv::norm(cv::Mat(vec[0]), cv::Mat(vec[3]));

    double ear = (a + b) / (2.0 * c);
    return ear;
}

int main()
{
    try {
        cv::VideoCapture cap("../nam-thang-sang.mp4");

        if (!cap.isOpened()) {
            cerr << "Unable to connect to camera" << endl;
            return 1;
        }
        cap.set(CAP_PROP_FRAME_WIDTH, 640); //use small resolution for fast processing
        cap.set(CAP_PROP_FRAME_HEIGHT, 480);

        // Load face detection and deserialize  face landmarks model.
        frontal_face_detector detector = get_frontal_face_detector();

        deserialize("../../shape_predictor_68_face_landmarks.dat") >> sp;

        // Grab and process frames until the main window is closed by the user.
        const clock_t start = clock();
        while (!win.is_closed()) {
            // Grab a frame
            cv::Mat temp;
            if (!cap.read(temp)) {
                break;
            }

            cv_image<bgr_pixel> cimg(temp);
            full_object_detection shape;

            // Detect faces

            std::vector<rectangle> faces = detector(cimg);
            win.clear_overlay();
            win.set_image(cimg);

            // Find the pose of each face.
            if (faces.size() > 0) {
                shape = sp(cimg, faces[0]); //work only with 1 face

                for (int b = 36; b < 42; ++b) {
                    p.x = shape.part(b).x();
                    p.y = shape.part(b).y();
                    lefteye.push_back(p);
                }
                for (int b = 42; b < 48; ++b) {
                    p.x = shape.part(b).x();
                    p.y = shape.part(b).y();
                    righteye.push_back(p);
                }

                // cout << "left eye:" << lefteye << endl;
                //Compute Eye aspect ration for eyes
                double right_ear = compute_EAR(righteye);
                double left_ear = compute_EAR(lefteye);

                //if the avarage eye aspect ratio of lef and right eye less than 0.2, the status is sleeping.
                // if ((right_ear + left_ear) / 2 < 0.2) {
                //     const clock_t tmp_close = clock();

                //     if (end_open == 0) {
                //         time_close = float(tmp_close - start) / 1000000;
                //     } else {
                //         time_close = float(tmp_close - end_open) / 1000000;
                //     }

                //     cout << "Close time :" << time_close << endl;

                //     if ( time_close > 2) {
                //         // cout << "Close time :" << time_close << endl;
                //         printf("\a");
                //         // win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 0, 0), "Warning"));
                //     }
                //     const clock_t close = clock();
                //     end_close = close;
                // } else {
                //     const clock_t tmp_open = clock();
                //     if ( end_close != 0) {
                //         float time_open = float(tmp_open - end_close) / 1000000;
                //          i++;
                //         cout << "count "<< i <<endl;
                //         cout << "Open time :" << time_open << endl;
                //         if ( time_open > 0.4) { // Real open eye
                //             end_open = tmp_open;
                //             // cout << "Open" << endl;
                //             // win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "Not sleeping"));
                //         } else { //
                //             cout << "Blink" << endl;
                //         }
                //     } else {
                //         end_open = tmp_open;
                //     }
                // }
                // if ((right_ear + left_ear) / 2 < 0.2) {
                //     if ( j > 4 && err > 1) {
                //         i = 0;
                //     }
                //     i++;
                //     bl++;
                //     const clock_t tmp_close = clock();
                //     float time = float(tmp_close - tmp_open) /  1000000;
                //     cout << "time" << time << endl;
                //     cout << "Open   " << j << endl;
                //     cout << "close   " << i << endl;
                //     // cout << "close        " << i << endl;
                //     if ( i >= 40) {
                //         cout << "Sleep" << endl;
                //         win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 0, 0), "Warning"));
                //     } else
                //     {
                //         win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(0, 0, 255), "CLose"));
                //     }
                //     err = 0;
                // } else {
                //     const clock_t tmp_open = clock();
                //     if( bl > 2 ) {
                //         j = 0;
                //     }
                //     err++;
                //     cout << "error   " << err << "j "<< j<< endl;
                //     j++;

                //     if (bl <= 8 && bl > 0) {
                //         cout << "blink" << endl;
                //     }
                //     bl = 0;
                //     win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "Not bleeping"));
                // }

                if ((right_ear + left_ear) / 2 < 0.2) {
                    const clock_t tmp_close = clock();
                    if ( end_close == 0) time_close = 0;
                    else time_close = time_close + float(tmp_close - end_close) / 1000000;
                    cout << "Sleep " <<  time_close << endl;
                    if (time_close > 2) {
                        time_open = 0;
                        printf("\a");
                        cout << "Sleep" << endl;
                        win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 0, 0), "Warning"));
                    } else {
                        win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(0, 0, 255), "CLose"));
                    }
                    end_close = tmp_close;
                } else {
                    cout << "Sleep ---- open" <<  time_close << endl;
                    const clock_t tmp_open = clock();
                    if ( end_open == 0) time_open = 0;
                    else time_open = time_open + float(tmp_open - end_open) / 1000000;
                    cout << "Open" <<  time_open << endl;
                    if ( time_open > 0.1 || time_close <= 0.4) {
                        time_close = 0;
                        end_close = 0;
                    }

                    win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "Not bleeping"));
                    end_open = tmp_open;
                }

                righteye.clear();
                lefteye.clear();

                win.add_overlay(render_face_detections(shape));

                c = (char)waitKey(30);
                if (c == 27)
                    break;
            } else {
                if (time_close != 0) {
                    printf("\a");
                    cout << "warning " << endl;
                }
            }
        }
    }
    catch (serialization_error& e) {
        cout << "Check the path to dlib's default face landmarking model file to run this example." << endl;
        cout << "You can get it from the following URL: " << endl;
        cout << "   http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2" << endl;
        cout << endl
             << e.what() << endl;
    }
    catch (exception& e) {
        cout << e.what() << endl;
    }
}


#ifndef STAN_MATH_PRIM_SCAL_PROB_POISSON_LOG_RNG_HPP
#define STAN_MATH_PRIM_SCAL_PROB_POISSON_LOG_RNG_HPP

#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_less.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/multiply_log.hpp>
#include <stan/math/prim/scal/fun/gamma_q.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <boost/random/poisson_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <limits>

namespace stan {
namespace math {

template <class RNG>
inline int poisson_log_rng(double alpha, RNG& rng) {
  using boost::random::poisson_distribution;
  using boost::variate_generator;

  static const char* function = "poisson_log_rng";
  static const double POISSON_MAX_LOG_RATE = 30 * std::log(2);

  using std::exp;

  check_not_nan(function, "Log rate parameter", alpha);
  check_less(function, "Log rate parameter", alpha, POISSON_MAX_LOG_RATE);

  variate_generator<RNG&, poisson_distribution<> > poisson_rng(
      rng, poisson_distribution<>(exp(alpha)));
  return poisson_rng();
}

}  // namespace math
}  // namespace stan
#endif


/*
This file is a part of NNTL project (https://github.com/Arech/nntl)

Copyright (c) 2015-2021, Arech (aradvert@gmail.com; https://github.com/Arech)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of NNTL nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "stdafx.h"

#include "../nntl/math.h"
#include "../nntl/common.h"

#include "../nntl/interface/rng/cstd.h"
#include "../nntl/interface/rng/afrand.h"
#include "../nntl/interface/rng/afrand_mt.h"

#include "../nntl/interfaces.h"

#include "../nntl/utils/chrono.h"
#include "../nntl/utils/tictoc.h"

#include "../nntl/interface/rng/distr_normal_naive.h"

#pragma warning(push,3)
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#pragma warning(pop)

using namespace nntl;
using namespace nntl::utils;

typedef d_interfaces::real_t real_t;
typedef math::smatrix<real_t> realmtx_t;

#ifdef NNTL_DEBUG
constexpr unsigned TEST_PERF_REPEATS_COUNT = 10;
#else
constexpr unsigned TEST_PERF_REPEATS_COUNT = 400;
#endif // NNTL_DEBUG

void test_rng_perf(vec_len_t rowsCnt, vec_len_t colsCnt = 10) {
	//typedef realmtx_t::numel_cnt_t numel_cnt_t;

	using namespace ::std::chrono;
	const auto dataSize = realmtx_t::sNumel(rowsCnt, colsCnt);
	STDCOUTL("******* testing rng performance over " << rowsCnt << "x" << colsCnt << " matrix (" << dataSize << " elements) **************");

	//double tstStd;
	double tstAFMersenne, tstAFSFMT0, tstAFSFMT1;
	steady_clock::time_point bt;
	nanoseconds diff;
	constexpr unsigned maxReps = TEST_PERF_REPEATS_COUNT;

	realmtx_t m(rowsCnt, colsCnt);
	ASSERT_TRUE(!m.isAllocationFailed());

	/*
	 *turning it off because it works more than 100 slower
	{
		rng::CStd rg;
		bt = steady_clock::now();
		for (unsigned r = 0; r < maxReps; ++r) {
			rg.gen_matrix_norm(m);
		}
		diff = steady_clock::now() - bt;
	}
	STDCOUTL("CStd:\t" << utils::duration_readable(diff, maxReps, &tstStd));*/

	{
		rng::AFRand<real_t, AFog::CRandomMersenne> rg;
		bt = steady_clock::now();
		for (unsigned r = 0; r < maxReps; ++r) {
			rg.gen_matrix_norm(m);
		}
		diff = steady_clock::now() - bt;
	}
	STDCOUTL("AFMersenne:\t" << utils::duration_readable(diff, maxReps, &tstAFMersenne));

	{
		rng::AFRand<real_t, AFog::CRandomSFMT0> rg;
		bt = steady_clock::now();
		for (unsigned r = 0; r < maxReps; ++r) {
			rg.gen_matrix_norm(m);
		}
		diff = steady_clock::now() - bt;
	}
	STDCOUTL("AFSFMT0:\t" << utils::duration_readable(diff, maxReps, &tstAFSFMT0));

	{
		rng::AFRand<real_t, AFog::CRandomSFMT1> rg;
		bt = steady_clock::now();
		for (unsigned r = 0; r < maxReps; ++r) {
			rg.gen_matrix_norm(m);
		}
		diff = steady_clock::now() - bt;
	}
	STDCOUTL("AFSFMT1:\t" << utils::duration_readable(diff, maxReps, &tstAFSFMT1));
}

TEST(TestRNG, RngPerf) {
	//for (unsigned i = 100; i <= 140; i+=1) test_rng_perf(i,100);

	test_rng_perf(1000);
#ifndef TESTS_SKIP_LONGRUNNING
	test_rng_perf(10000);
	//test_rng_perf(100000);
	//test_rng_perf(1000000);
#endif
}

//////////////////////////////////////////////////////////////////////////
template<typename AFRng, typename iThreadsT>
void test_rngmt(iThreadsT&iT, realmtx_t& m) {
	constexpr unsigned maxReps = 5*TEST_PERF_REPEATS_COUNT;

	real_t g = real_t(0);
	auto ptr = m.data();
	auto dataCnt = m.numel();
	utils::tictoc tS, tM, tB;
	rng::AFRand_mt<real_t, AFRng, iThreadsT> rg(iT);

	for (unsigned r = 0; r < maxReps; ++r) {
		tS.tic();
		rg.gen_vector_norm_st(ptr, dataCnt);
		tS.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(::std::abs(g));

		tM.tic();
		rg.gen_vector_norm_mt(ptr, dataCnt);
		tM.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(::std::abs(g));

		tB.tic();
		rg.gen_vector_norm(ptr, dataCnt);
		tB.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(::std::abs(g));
	}
	tS.say("st");
	tM.say("mt");
	tB.say("()");
	STDCOUTL(g);
}

template<typename iRng, typename iThreadsT>
void test_rng_mt_perf(iThreadsT& iT, char* pName, vec_len_t rowsCnt, vec_len_t colsCnt = 10) {
	const auto dataSize = realmtx_t::sNumel(rowsCnt, colsCnt);
	STDCOUTL("******* testing multithreaded "<< pName<<	" performance over " << rowsCnt << "x" << colsCnt << " matrix (" << dataSize << " elements) **************");
	realmtx_t m(rowsCnt, colsCnt);
	ASSERT_TRUE(!m.isAllocationFailed());
	threads::prioritize_workers<threads::PriorityClass::PerfTesting, iThreadsT> pw(iT);
	test_rngmt<iRng, iThreadsT>(iT, m);
}

TEST(TestRNG, RngMtPerf) {
	typedef nntl::d_interfaces::iThreads_t def_threads_t;
	def_threads_t Thr;

	NNTL_RUN_TEST2( (rng::_impl::AFRAND_MT_THR<AFog::CRandomMersenne, real_t>::bnd_gen_vector_norm), 10)
		test_rng_mt_perf<AFog::CRandomMersenne>(Thr, "AFMersenne", i, 10);
	NNTL_RUN_TEST2( (rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT0, real_t>::bnd_gen_vector_norm), 10)
		test_rng_mt_perf<AFog::CRandomSFMT0>(Thr, "AFSFMT0", i, 10);
	NNTL_RUN_TEST2( (rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT1, real_t>::bnd_gen_vector_norm), 10)
		test_rng_mt_perf<AFog::CRandomSFMT1>(Thr, "AFSFMT1", i, 10);
}

//////////////////////////////////////////////////////////////////////////
template<typename base_t> struct NormDistrCompat_EPS {};
template<> struct NormDistrCompat_EPS<double> { static constexpr double eps = 1e-4; };
template<> struct NormDistrCompat_EPS<float> { static constexpr float eps = 5e-2f; };
TEST(TestRNG, NormDistrCompat) {
	typedef d_interfaces::iRng_t iRng_t;
	typedef nntl::d_interfaces::iThreads_t def_threads_t;
	typedef ::std::vector<real_t> vec_t;

	static constexpr real_t targMean(real_t(.5)), targStddev(real_t(2.));
	static constexpr int maxReps = 5, totalElms = 1000000;

	vec_t dest(totalElms);

	def_threads_t Thr;
	iRng_t iR(Thr);
	rng::distr_normal_naive<iRng_t> d(iR, targMean, targStddev);

	for (int i = 0; i < maxReps; ++i) {
		::std::fill(dest.begin(), dest.end(), real_t(0.));
		d.gen_vector(&dest.front(), totalElms);

		//see http://www.boost.org/doc/libs/1_63_0/doc/html/accumulators/user_s_guide.html

		::boost::accumulators::accumulator_set<real_t, ::boost::accumulators::stats<
			::boost::accumulators::tag::mean
			, ::boost::accumulators::tag::lazy_variance >
		> acc;
		for (const auto& v : dest) {
			acc(v);
		}
		const real_t _mean = ::boost::accumulators::extract_result< ::boost::accumulators::tag::mean >(acc)
			, _std = ::std::sqrt(::boost::accumulators::extract_result< ::boost::accumulators::tag::lazy_variance >(acc));
		STDCOUTL("Mean = " << _mean << ", std = " << _std);
		ASSERT_NEAR(_mean, targMean, NormDistrCompat_EPS<real_t>::eps) << "Wrong mean!!!";
		ASSERT_NEAR(_std, targStddev, NormDistrCompat_EPS<real_t>::eps) << "Wrong StdDev!!!";
	}
}

TEST(TestRNG, normal_vector_Compat) {
	typedef d_interfaces::iRng_t iRng_t;
	typedef nntl::d_interfaces::iThreads_t def_threads_t;
	typedef ::std::vector<real_t> vec_t;

	static constexpr real_t targMean(real_t(.5)), targStddev(real_t(2.));
	static constexpr unsigned maxReps = 5, totalElms = 1000000;

	vec_t dest(totalElms);

	def_threads_t Thr;
	iRng_t iR(Thr);
	//rng::distr_normal_naive<iRng_t> d(iR, targMean, targStddev);

	for (unsigned i = 0; i < maxReps; ++i) {
		::std::fill(dest.begin(), dest.end(), real_t(0.));
		iR.normal_vector(&dest.front(), totalElms, targMean, targStddev);

		::boost::accumulators::accumulator_set<real_t, ::boost::accumulators::stats<
			::boost::accumulators::tag::mean
			, ::boost::accumulators::tag::lazy_variance >
		> acc;
		for (const auto& v : dest) {
			acc(v);
		}
		const real_t _mean = ::boost::accumulators::extract_result< ::boost::accumulators::tag::mean >(acc)
			, _std = ::std::sqrt(::boost::accumulators::extract_result< ::boost::accumulators::tag::lazy_variance >(acc));
		STDCOUTL("Mean = " << _mean << ", std = " << _std);
		ASSERT_NEAR(_mean, targMean, NormDistrCompat_EPS<real_t>::eps) << "Wrong mean!!!";
		ASSERT_NEAR(_std, targStddev, NormDistrCompat_EPS<real_t>::eps) << "Wrong StdDev!!!";
	}
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
template<typename iRng, typename iThreadsT>
void test_bernoulli_perf(iThreadsT& iT, char* pName, vec_len_t rowsCnt, vec_len_t colsCnt = 10) {
	const auto dataSize = realmtx_t::sNumel(rowsCnt, colsCnt);
	STDCOUTL("******* testing " << pName << ".bernoulli_vector performance over " << rowsCnt << "x" << colsCnt << " matrix (" << dataSize << " elements) **************");
	realmtx_t m(rowsCnt, colsCnt);
	ASSERT_TRUE(!m.isAllocationFailed());
	constexpr unsigned maxReps = 5 * TEST_PERF_REPEATS_COUNT;

	const auto p = real_t(.6), pv = real_t(1.), nv = real_t(0);
	real_t g = real_t(0);
	auto ptr = m.data();
	auto dataCnt = m.numel();
	utils::tictoc tS, tM, tB;
	rng::AFRand_mt<real_t, iRng, iThreadsT> rg(iT);

	threads::prioritize_workers<threads::PriorityClass::PerfTesting, iThreadsT> pw(iT);
	for (unsigned r = 0; r < maxReps; ++r) {
		tS.tic();
		rg.bernoulli_vector_st(ptr, dataCnt, p, pv, nv);
		tS.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(g);

		tM.tic();
		rg.bernoulli_vector_mt(ptr, dataCnt, p, pv, nv);
		tM.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(g);

		tB.tic();
		rg.bernoulli_vector(ptr, dataCnt, p, pv, nv);
		tB.toc();
		for (const auto& e : m) g += e;
		g = ::std::log(g);
	}
	tS.say("st");
	tM.say("mt");
	tB.say("()");
	STDCOUTL(g);
}

TEST(TestRNG, BernoulliVectorPerf) {
	typedef nntl::d_interfaces::iThreads_t def_threads_t;
	def_threads_t Thr;

	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomMersenne, real_t>::bnd_bernoulli_vector), 10)
		test_bernoulli_perf<AFog::CRandomMersenne>(Thr, "AFMersenne", i, 10);
	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT0, real_t>::bnd_bernoulli_vector), 10)
		test_bernoulli_perf<AFog::CRandomSFMT0>(Thr, "AFSFMT0", i, 10);
	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT1, real_t>::bnd_bernoulli_vector), 10)
		test_bernoulli_perf<AFog::CRandomSFMT1>(Thr, "AFSFMT1", i, 10);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
template<typename iRng, typename iThreadsT>
void test_normal_perf(iThreadsT& iT, char* pName, vec_len_t rowsCnt, vec_len_t colsCnt = 10) {
	const auto dataSize = realmtx_t::sNumel(rowsCnt, colsCnt);
	STDCOUTL("******* testing " << pName << ".normal_vector performance over " << rowsCnt << "x" << colsCnt << " matrix (" << dataSize << " elements) **************");
	realmtx_t M(rowsCnt, colsCnt);
	ASSERT_TRUE(!M.isAllocationFailed());
	constexpr unsigned maxReps = 5 * TEST_PERF_REPEATS_COUNT;

	const auto m = real_t(.0), st = real_t(1.);
	real_t g = real_t(0);
	auto ptr = M.data();
	auto dataCnt = M.numel();
	utils::tictoc tS, tM, tB;
	rng::AFRand_mt<real_t, iRng, iThreadsT> rg(iT);

	threads::prioritize_workers<threads::PriorityClass::PerfTesting, iThreadsT> pw(iT);
	for (unsigned r = 0; r < maxReps; ++r) {
		tS.tic();
		rg.normal_vector_st(ptr, dataCnt, m, st);
		tS.toc();
		for (const auto& e : M) g += e;
		g = ::std::log(::std::abs(g));

		tM.tic();
		rg.normal_vector_mt(ptr, dataCnt, m, st);
		tM.toc();
		for (const auto& e : M) g += e;
		g = ::std::log(::std::abs(g));

		tB.tic();
		rg.normal_vector(ptr, dataCnt, m, st);
		tB.toc();
		for (const auto& e : M) g += e;
		g = ::std::log(::std::abs(g));
	}
	tS.say("st");
	tM.say("mt");
	tB.say("()");
	STDCOUTL(g);
}

TEST(TestRNG, normal_vector_perf) {
	typedef nntl::d_interfaces::iThreads_t def_threads_t;
	def_threads_t Thr;

	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomMersenne, real_t>::bnd_normal_vector), 10)
		test_normal_perf<AFog::CRandomMersenne>(Thr, "AFMersenne", i, 10);
	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT0, real_t>::bnd_normal_vector), 10)
		test_normal_perf<AFog::CRandomSFMT0>(Thr, "AFSFMT0", i, 10);
	NNTL_RUN_TEST2((rng::_impl::AFRAND_MT_THR<AFog::CRandomSFMT1, real_t>::bnd_normal_vector), 10)
		test_normal_perf<AFog::CRandomSFMT1>(Thr, "AFSFMT1", i, 10);
}


#include <iostream>
#include <thread>
#include <cmath>
#include <cstdlib>
#include <boost/timer/timer.hpp>

using namespace std;
using namespace boost::timer;

int main()
{
	srand((unsigned int)time(NULL));

	auto_cpu_timer program_timer(3);

	auto procUser = [](long work)
	{
		for (long i = 0; i < work; ++i)
			sqrt(123.456L);
	};

	auto procSystem = [](long work)
	{
		for (long i = 0; i < work; ++i)
			thread([](){}).detach();
	};

	auto procTimer = [](long work)
	{
		cpu_timer timer;
		timer.start();

		for(long i = 0; i < work; ++i)
			rand();

		timer.stop();
		cout << "Thread timer:" << timer.format(3);
	};

	thread t1(procUser, 1000000000);
	thread t2(procSystem, 100000);
	thread t3(procTimer, 100000000);

	t1.join();
	t2.join();
	t3.join();

	cout << "Program timer:";

	return 1;
}


// This file is part of DM-HEOM (https://github.com/noma/dm-heom)
//
// Copyright (c) 2015-2019 Matthias Noack, Zuse Institute Berlin
//
// Licensed under the 3-clause BSD License, see accompanying LICENSE,
// CONTRIBUTORS.md, and README.md for further information.

#include <algorithm>
#include <exception>
#include <iostream>

#include <boost/format.hpp>
#include <noma/num/meta_stepper.hpp>

#include "heom/command_line.hpp"
#include "heom/common.hpp"
#include "heom/handle_main_exception.hpp"
#include "heom/hierarchy_graph.hpp"
#include "heom/hierarchy_mask.hpp"
#include "heom/hierarchy_norm.hpp"
#include "heom/instance.hpp"
#include "heom/make_file_observer_list.hpp"
#include "heom/ocl_config.hpp"
#include "heom/ode.hpp"
#include "heom/population_dynamics_solver.hpp"
#include "heom/rate_kernel_config.hpp"

namespace bmt = ::heom::bmt;
namespace num = ::heom::num;
namespace ocl = ::heom::ocl;
using num::int_t;
using num::real_t;
using num::complex_t;
using heom::complex_matrix_t;

int main(int argc, char* argv[])
{
	// start runtime measurement
	bmt::timer app_timer;

	// output compile-time configuration
	std::cout << "-------------------- Compile-Time Configuration ------------" << std::endl;
	heom::write_compile_config(std::cout);
	std::cout << "------------------------------------------------------------" << std::endl;

	std::exception_ptr eptr;
	try {
		// command line parsing
		heom::command_line command_line { argc, argv };

		// parse config files
		std::cout << "Parsing OpenCL configuration from file: " << command_line.ocl_config_filename() << std::endl;
		heom::ocl_config ocl_config(command_line.ocl_config_filename());
		std::cout << "Parsing HEOM configuration from file: " << command_line.heom_config_filename() << std::endl;
		heom::rate_kernel_config heom_config(command_line.heom_config_filename());

		heom::hierarchy_graph complete_graph(heom_config.baths_number(), heom_config.baths_matsubaras(), heom_config.system_ado_depth());

		heom::instance heom_instance(heom_config, heom::sites_to_states_mode_t::identity, complete_graph);
		heom_instance.set_hierarchy_top(); // NOTE: default hierarchy_top, i.d. upper left = (1.0, 0.0)

		using stepper_t = num::meta_stepper; // uses solver_stepper_type from config
		using solver_t = heom::population_dynamics_solver<heom::ode, stepper_t, heom::hierarchy_norm>;

		// create a solver from configuration and instance
		const ocl::nd_range ocl_nd_range {
				{}, // offset
		        {1, static_cast<std::uint64_t>(heom_instance.matrices())}, // global size
		        {1, 1} // local size
			};

		solver_t solver(ocl_config, ocl_nd_range, heom_config, heom_instance);
		std::cout << "-------------------- OpenCL Runtime Configuration ----------" << std::endl;
		solver.write_ocl_runtime_config(std::cout);
		std::cout << "------------------------------------------------------------" << std::endl;

		// with create_hierarchy_mask we create mask corresponding to configured filtering strategy, then update solver
		// NOTE: .data() returns pointer to mask_t array
		heom::hierarchy_mask hierarchy_mask = heom::create_hierarchy_mask(heom_config.filtering_strategy(), complete_graph, heom_config.baths_matsubaras(), heom_config.filtering_first_layer());
		solver.update_hierarchy_mask(hierarchy_mask.data());

		std::cout << "-------------------- Hierarchy Mask Counter ----------------" << std::endl;
		heom::write_hierarchy_mask_stats(hierarchy_mask, std::cout);
		std::cout << "------------------------------------------------------------" << std::endl;

		// hierarchy top buffer, needed throughout the computation
		std::unique_ptr<complex_t[]> hierarchy_top_buffer(new complex_t[heom_instance.size_hierarchy_top()]);

		// lambda to evaluate the HEOM ODE outside of the solver's propagation, i.e. non-intrusively (only back_buffer will be overwritten)
		// TODO: think: heom_kernel needs limited depth, probably at most 2 layers
		auto evaluate_heom_ode = [&]() {
			solver.ode().solve(solver.time(), 1.0, solver.front_buffer(), solver.back_buffer()); // NOTE: back_buffer() can always be safely overwritten
		};

		// lambda to scale the whole hierarchy in back_buffer with i, store result to front_buffer
		auto scale_hierarchy_with_i = [&]() {
			solver.hierarchy_scale(solver.back_buffer(), complex_t(0.0,1.0), solver.front_buffer());
		};

		// set hierarchy_top diagonal to zero in front_buffer
		auto zero_hierarchy_top_diag = [&]() {
			// read hierarchy_top from back_buffer
			solver.hierarchy_top(hierarchy_top_buffer.get());
			// modify diagonal
			for (int_t i = 0; i < heom_config.system_sites(); ++i)
				hierarchy_top_buffer[i * heom_config.system_sites() + i] = complex_t { 0.0, 0.0 };
			// update_hierarchy_top
			solver.update_hierarchy_top(hierarchy_top_buffer.get());
		};

		// lambda to read the diagonal of the top level hierarchy
		auto read_top_level_diag = [&](int_t site, complex_matrix_t& matrix_buffer) {
			// read top matrix
			solver.read_buffer(solver.back_buffer(), hierarchy_top_buffer.get(), heom_instance.size_hierarchy_top());
			// copy diagonal to site-th row
			for (int_t i = 0; i < heom_config.system_sites(); ++i)
				matrix_buffer.at(site, i) = hierarchy_top_buffer[i * heom_config.system_sites() + i];
		};

		int_t iterations = heom_config.solver_steps() / heom_config.program_observe_steps();
		int_t steps_per_iteration = heom_config.program_observe_steps();
		int_t total_steps = iterations * heom_config.program_observe_steps();

		// set up output file
		auto& observation_filename = heom_config.observations().get().front().get().second; // filename of first specified observation, guaranteed to exist by config checks
		std::ofstream observation_file(observation_filename);
		if (observation_file.fail())
			throw std::runtime_error("Error: could not open output file: " + observation_filename);

		// set up observation data structures
		std::vector<real_t> observation_times;
		std::vector<complex_matrix_t> observations; // for collecting all observations first
		for (int_t i = 0; i <= iterations; ++i) // add iterations + 1 matrices
			observations.emplace_back(heom_config.system_sites(), heom_config.system_sites()); // NOTE: null-initialised

		// for progress estimate
		const size_t solver_runs = heom_config.system_sites();
		size_t solver_run = 1;

		// iterate over the number of sites
		for (int_t site = 0; site < heom_config.system_sites(); ++site) {
			std::cout << "Processing site " << (site + 1) << "/" << heom_config.system_sites() << std::endl;

			// re-use solver and instance instead of creating a new one for every iteration
			if (site > 0) {
				// re-init instance
				heom_instance.null_hierarchy();

				// create and hierarchy_top
				std::fill(hierarchy_top_buffer.get(), hierarchy_top_buffer.get() + heom_instance.size_hierarchy_top(), complex_t {0.0, 0.0});
				hierarchy_top_buffer[site * heom_config.system_sites() + site] = complex_t { 1.0, 0.0 };
				heom_instance.set_hierarchy_top(hierarchy_top_buffer.get());

				// re-init solver
				solver.reset();
				++solver_run; // for progress estimate
			}

			// run heom_kernel (front_buffer => back_buffer)
			evaluate_heom_ode();
			// multiply with i (imag) (back_buffer => front_buffer)
			scale_hierarchy_with_i();
			// zero diagonal (front_buffer => front_buffer)
			zero_hierarchy_top_diag();
			// run heom_kernel (front_buffer => back_buffer)
			evaluate_heom_ode();
			// observe back_buffer
			read_top_level_diag(site, observations[0]);
			observation_times.push_back(0.0);

			// run the solver, print output every n steps
			for (int_t i = 0; i < iterations; ++i) {
				// propagate
				solver.step_forward(steps_per_iteration); // NOTE: switches front_buffer and back_buffer, result is new front_buffer
				// zero diagonal (front_buffer => front_buffer)
				zero_hierarchy_top_diag();
				// run heom_kernel (front_buffer => back_buffer)
				evaluate_heom_ode();
				// observe back_buffer
				read_top_level_diag(site, observations[i + 1]);

				// after propagation, we are at:
				const auto current_step = (i + 1) * steps_per_iteration;
				const real_t current_time = current_step * heom_config.solver_step_size();

				observation_times.push_back(current_time);

				// update status
				heom::write_progress(current_step - 1, total_steps, solver_run, solver_runs, "Calculation rate kernel: ", std::cout);
			}

		} // for system_sites

		// write observations header
		observation_file << "time";
		for (int_t i = 0; i < heom_config.system_sites(); ++i)
			for (int_t j = 0; j < heom_config.system_sites(); ++j)
				for (int_t k = 0; k < 2; ++k) // complex
				{
					observation_file << heom::default_delimiter;
					observation_file << "elem_" << i << "_" << j << (k % 2 == 0 ? "_real" : "_imag");
				}
		observation_file << std::endl;

		// write observations data
		auto format_0 = heom::real_format;
		auto format_1 = heom::real_format;
		for (int_t it = 0; it <= iterations; ++it) {
			observation_file << (format_0 % observation_times[it]) << heom::default_delimiter;
			for (int_t i = 0; i < heom_config.system_sites(); ++i)
				for (int_t j = 0; j < heom_config.system_sites(); ++j) {
					observation_file << (format_0 % observations[it].at(i, j).real()) << heom::default_delimiter << (format_1 % observations[it].at(i, j).imag());
					if ((i * heom_config.system_sites() + j) < (heom_config.system_sites() * heom_config.system_sites()) - 1)
						observation_file << heom::default_delimiter;
				}
			observation_file << '\n';
		}
		observation_file << std::flush; // tell the OS to write to disk

		// output benchmark results
		std::cout << "-------------------- Solver Runtime Summary ----------------" << std::endl;
		solver.write_runtime_summary(std::cout);
		std::cout << "------------------------------------------------------------" << std::endl;
		std::cout << "-------------------- Memory Summary ------------------------" << std::endl;
		std::cout << "local hierarchy buffer size:            "
		          << boost::format("%11.2f MiB\n") % (heom_instance.size_hierarchy_byte() / 1024.0 / 1024.0);
		std::cout << "local instance size:                    "
		          << boost::format("%11.2f MiB\n") % (heom_instance.allocated_byte() / 1024.0 / 1024.0);
		std::cout << "max. heap via aligned::allocate(..):    "
		          << boost::format("%11.2f MiB") %
		             (heom::memory::aligned::instance().allocated_byte_max() / 1024.0 / 1024.0)
		          << " ("
		          << boost::format("count: %6i") % heom::memory::aligned::instance().allocations_max()
		          << ')' << std::endl;
		std::cout << "max. OpenCL buffer allocations:         "
		          << boost::format("%11.2f MiB") % (solver.ocl_helper().allocated_byte_max() / 1024.0 / 1024.0)
		          << " ("
		          << boost::format("count: %6i") % solver.ocl_helper().allocations_max()
		          << ')' << std::endl;
		std::cout << "------------------------------------------------------------" << std::endl;

	} catch (...) {
		eptr = std::current_exception();
	}
	int ret = heom::handle_main_exception(eptr);

	// print application runtime
	std::cout << "main(): application runtime: " << boost::format("%11.2f") % std::chrono::duration_cast<bmt::seconds>(bmt::duration(app_timer.elapsed())).count() << " s" << std::endl;

	return ret;
}


#include <dai/alldai.h>
#include <iostream>
#include <fstream>
#include <string>
#include "dai/emrun.h"
#include <dai/util.h>
#include <string>
#include <sys/stat.h>
#include <time.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <boost/lexical_cast.hpp>
#include <iomanip>      // std::setprecision
using namespace std;
using namespace dai;
//
// Generate a random number between 0 and 1
// return a uniform number in [0,1].
double unifRand()
{
    return rand()/double(RAND_MAX);
}
// Reset the random number generator with the system clock.
void seed()
{
	/*Declare variable to hold seconds on clock.*/
//	time_t seconds;
	/*Get value from system clock and place in seconds variable.*/
//	time(&seconds);
	/*Convert seconds to a unsigned integer.*/
     struct timeval time;
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     srand((time.tv_sec * 1000) + (time.tv_usec));
   //  rnd_seed((time.tv_sec * 1000) + (time.tv_usec / 10) );
}

std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
	s.erase(0, s.find_first_not_of(t));
	s.erase(s.find_last_not_of(t) + 1);
	return s;
}
string alarmlearnedfgloc = "/Applications/Utilities/libDAI-0.3.1/alarm1_factor_graphs/";
int main(int argc, char *argv[]) {
string line1;

int t=100;
int p_c = 0; // counter to skip the first four config lines in the cpt of the fg file
for( size_t n = 0; n < t; n++ )
{
	string number2;
	stringstream num2;
	num2 << n;
	number2 = num2.str();
	string parent2 = alarmlearnedfgloc +"alarm_"+number2+".fg";
	const char* parentfgfile = &parent2[0];
	ofstream c1 (parentfgfile);
	ifstream p1 (argv[1]);  // input file
	getline(p1,line1);
	c1<<line1<<endl;
	int factor =0;
	int no_factors = 0;
	int* factor_names;
	int factor_pos=0;
	int* states_arr;
	int no_states = 1;
	int arr_size =1;
	int factor_states =0;
	int tot_arrays =0;
	int arr_set =0;
	int arr_set_states =0;
	int no_arr_arrset =0;
	int stat =0;
	while(getline(p1,line1))
	{
		line1 = trim(line1);
		stringstream ss(line1);
		if(line1.empty())
		{
		  p_c = 0;
		  stat =0;
		  cout<<"factor variable "<<factor<<endl;
		  c1<<endl;
		  no_states = 1;
		  arr_size =1;
		  factor++;
		}
		if(p_c == 1)
		{
		 ss>>no_factors;
		 cout<<"no. of factors "<<no_factors<<endl;
		c1<<no_factors<<endl;
		}
		if(p_c == 2)
		{
		 factor_names = new int[no_factors];
		 std::vector<std::string> fields;
	 	 fields = tokenizeString( line1, true,"  ");
		 for( size_t i = 0; i < fields.size(); ++i )
		 {
		  stringstream n;
		  n << fields[i];
		  string s = n.str();
		  if( s.find_first_not_of(" ")!= std::string::npos)
		  {
			c1<<s<<" ";
			n >> factor_names[i];
			if(factor_names[i] == (factor-1))
			 factor_pos =i;
		  }
	 	 }
		 c1<<endl;
		 //cout<<"factor position "<<factor_pos<<endl;
		}
		if(p_c == 3)
		{
		 states_arr = new int[no_factors];
		 std::vector<std::string> fields1;
	 	 fields1 = tokenizeString( line1, true,"  ");
		 for( size_t j = 0; j < fields1.size(); ++j )
		 {
		  stringstream n1;
		  n1 << fields1[j];
		  string state = n1.str();
		  if( state.find_first_not_of(" ")!= std::string::npos)
		  {
			c1<<state<<" ";
			n1 >> states_arr[j];
			no_states = no_states * states_arr[j];
			if(j < factor_pos)
			 arr_size = arr_size * states_arr[j];
			if(j == factor_pos)
			 {
			  factor_states = states_arr[j];
			  arr_set_states = arr_size*states_arr[j];
			 }
		  }
	 	 }
		 c1<<endl;
		 tot_arrays = no_states/arr_size;
		 arr_set = no_states/arr_set_states;
		 no_arr_arrset = factor_states;
		 //cout<<"array size "<<arr_size<<endl;
		 //cout<<no_states<<"\t"<<"no. of states "<<no_states<<"\t"<<"total arrays "<<tot_arrays<<"\t"<<"array set "<<arr_set<<"\t"<<"no. arrays "<<no_arr_arrset<<"\t"<<"factor states "<<factor_states<<endl;
		c1<<no_states<<endl;
		}
		if(p_c == 5)
	        {
		 for(int k=0; k<arr_set; k++)
		  {
			std::vector< double * > Arrays;
		       	for(int l=0; l<no_arr_arrset; l++)
			{
			 Arrays.push_back( new double[arr_size]);
			}
			vector<double*>::iterator it;
			for(int m=0; m<arr_size; m++)
			{
				it=Arrays.begin();
                std::vector<double> vec;
			    for(int i=0; i<no_arr_arrset; i++)
			    {
                    vec.push_back(unifRand());
			    }
			    const double total = std::accumulate(vec.begin(), vec.end(), 0.0);
			    for (double& value: vec)
			    {
                    double* arr = *it;
                    value /= total;
                    arr[m] = value;
                    ++it;
                    //cout<<value <<endl;
			    }
                // cout<<endl;
			    vec.clear();
				/*double sum=0;
				for(int l=0; l<no_arr_arrset; l++)
				{
				  seed();
				  double* arr = *it;
				  double val;
				  if(l == no_arr_arrset-1)
					val = 1-sum;
				  else
				        val = unifRand()/(arr_set+4);
				  arr[m] = val;
				  sum = sum +val;
				  ++it;
				}*/
			}
			vector<double*>::iterator it1;
			it1=Arrays.begin();
			for(int l=0; l<no_arr_arrset; l++)
			{
				double* arr = *it1;
				for(int m=0; m<arr_size; m++)
				{
				 stringstream v;
				 v << fixed << setprecision(4) << arr[m];
				 c1<<stat<<"   "<<v.str()<<endl;
				 stat++;
				}
				++it1;
			}

		  }
		}
		p_c++;
	}
	c1.close();
}
return 0;
}


#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_by_value.hpp>
#include <cctbx/sgtbx/seminvariant.h>

namespace cctbx { namespace sgtbx { namespace boost_python {

namespace {

  struct ss_vec_mod_wrappers
  {
    typedef ss_vec_mod w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("ss_vec_mod", no_init)
        .add_property("v", make_getter(&w_t::v, rbv()))
        .def_readonly("m", &w_t::m)
      ;
    }
  };

  struct structure_seminvariants_wrappers
  {
    typedef structure_seminvariants w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<copy_const_reference> ccr;
      class_<w_t>("structure_seminvariants", no_init)
        .def(init<space_group const&>((arg("space_group"))))
        .def("vectors_and_moduli", &w_t::vectors_and_moduli, ccr())
        .def("size", &w_t::size)
        .def("is_ss", &w_t::is_ss, (arg("miller_index")))
        .def("apply_mod", &w_t::apply_mod, (arg("miller_index")))
        .def("select", &w_t::select, (arg("discrete")))
        .def("continuous_shifts_are_principal",
          &w_t::continuous_shifts_are_principal)
        .def("principal_continuous_shift_flags",
          &w_t::principal_continuous_shift_flags, (
            arg("assert_principal")=true))
        .def("subtract_principal_continuous_shifts",
          &w_t::subtract_principal_continuous_shifts, (
            arg("translation"),
            arg("assert_principal")=true))
        .def("gridding", &w_t::gridding)
        .def("refine_gridding",
          (sg_vec3(w_t::*)(sg_vec3 const&) const)
          &w_t::refine_gridding, (arg("grid")))
        .def("grid_adapted_moduli",
          (af::small<ss_vec_mod, 3>(w_t::*)(sg_vec3 const&) const)
          &w_t::grid_adapted_moduli, (arg("dim")))
      ;
    }
  };

} // namespace <anoymous>

  void wrap_seminvariant()
  {
    ss_vec_mod_wrappers::wrap();
    structure_seminvariants_wrappers::wrap();
  }

}}} // namespace cctbx::sgtbx::boost_python


//
// Copyright 2005-2007 Adobe Systems Incorporated
//
// 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
//
#include <boost/gil.hpp>
#include <boost/gil/extension/io/jpeg.hpp>
#include <boost/gil/extension/numeric/sampler.hpp>
#include <boost/gil/extension/numeric/resample.hpp>

// Example for resize_view() in the numeric extension

int main()
{
    namespace bg = boost::gil;

    bg::rgb8_image_t img;
    bg::read_image("test.jpg", img, bg::jpeg_tag{});

    // test resize_view
    // Scale the image to 100x100 pixels using bilinear resampling
    bg::rgb8_image_t square100x100(100, 100);
    bg::resize_view(bg::const_view(img), bg::view(square100x100), bg::bilinear_sampler{});
    bg::write_view("out-resize.jpg", bg::const_view(square100x100), bg::jpeg_tag{});

    return 0;
}