#include <clpoly/clpoly.hh>
#include <boost/container_hash/hash.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <time.h>
clpoly::polynomial_ZZ read_file(std::string s)
{
    long numn,numv;
    std::ifstream fin(s);
    if (!fin)
    {
        // std::cout<<"无法读取文件!\n";
        throw "无法读取文件!";
    }
    fin>>numn>>numv;
    std::vector<clpoly::variable> var;
    var.reserve(numv);
    for (char i='a';i<'a'+numv;var.push_back(std::string(1,i)),++i);
    std::vector<std::pair<clpoly::variable,int64_t>> m;
    std::vector<std::pair<clpoly::monomial,clpoly::ZZ>> p;
    p.reserve(numn);
    long tmp;
    for (long i=0;i<numn;++i)
    {
        m.reserve(numv);
        for (auto &j:var)
        {
            fin>>tmp;
            if (tmp!=0)
                m.push_back(std::pair<clpoly::variable,int64_t>(j,tmp));
        }
        fin>>tmp;
        if (tmp!=0)
            p.push_back(std::pair<clpoly::monomial,clpoly::ZZ>(std::move(m),clpoly::ZZ(tmp)));
    }
    fin.close();
    return clpoly::polynomial_ZZ(p);
}
int main(){
    clpoly::variable x("x");
    clpoly::variable y("y");
    clpoly::variable z("z");
    clpoly::variable d("d");
    clpoly::variable r("r");
    clpoly::variable x1("x1"),x2("x2"),x3("x3");
    clpoly::variable x7("x7"),x8("x8");
    clpoly::polynomial_ZZ p={{{{x,1}},1}};
    clpoly::lex_<clpoly::custom_var_order> mo;
    mo=clpoly::lex_<clpoly::custom_var_order>(clpoly::custom_var_order({x8,x7,x1}));
    clpoly::polynomial_<clpoly::ZZ,clpoly::lex_<clpoly::custom_var_order>> p_1(&mo);
    std::cout<<"p="<< p<<std::endl;
    p=1;
    p=2*(2-x1)*x8+x7-2;
    std::cout<<"p="<< p<<std::endl;
    p_1=p;
    std::cout<<"p_1="<< p_1<<std::endl;

    clpoly::monomial m=pow(x,3);
    p=z*z;
    time_t t;
    std::cout<< p<<std::endl;

    std::cout<< 2*x*x*y+1<<std::endl;
    // auto l=p.variables();
    // for (auto &i:l)
    //     std::cout<<i.first<<":"<<i.second<<" ";
    // std::cout<<std::endl;
    // clpoly::univariate_priority_order comp_z(z);
    // clpoly::polynomial_<clpoly::ZZ,clpoly::univariate_priority_order> p2(&comp_z);
    // clpoly::poly_convert(std::move(p),p2);
    // std::cout<<p2<<std::endl;
    // l=p2.variables();
    // for (auto &i:l)
    //     std::cout<<i.first<<":"<<i.second<<" ";
    // std::cout<<std::endl;
    //clpoly::__is_monomial_compression=true;
    // std::cout<<pow(p2,2)<<std::endl;
    // std::cout<<"4*pow(x,4)*pow(y,2)*pow(z,4)+4*pow(x,2)*pow(y,2)*d^3*pow(z,4)+pow(y,2)*d^6*pow(z,4)+4*pow(x,2)*y*pow(z,2)+2*y*d^3*pow(z,2)+1\n";
    // std::cout<< p<<std::endl;
    clpoly::polynomial_ZZ f=x*pow(y,2);
    clpoly::polynomial_ZZ g=2*pow(y,3)-pow(y,2)+pow(x,2)*y;

    f=2*(2-7*x1+pow(x1,2)*x2)-(x3-x1);
    std::cout<<"f:="<<f<<":"<<std::endl;
    f=-pow(x,2)*pow(z,3) - pow(x,4) - pow(z,4) + pow(x,2) + 2*pow(z,2) - 1;
    g=-pow(r,2)*pow(x,2) + pow(x,4) + pow(x,2)*pow(z,2) + pow(z,4) +x- 2*pow(z,2) + 1;
    // clpoly::polynomial_ZZ f=clpoly::random_polynomial<clpoly::ZZ>({x,y,z,d},10,0.2,10,-10);
    // clpoly::polynomial_ZZ g=clpoly::random_polynomial<clpoly::ZZ>({x,y,z,d},10,0.2,10,-10);
    clpoly::polynomial_ZZ o;
     clpoly::polynomial_ZZ o1;
    std::cout<<"f:="<<f<<":"<<std::endl;
    std::cout<<"g:="<<g<<":"<<std::endl;
    std::cout<<clpoly::coeff(g,z)<<std::endl;
    // std::cout<<"o:="<<clpoly::prem(g,f,y)<<":"<<std::endl;
    // std::cout<<"t:="<<(double(clock()-t)/CLOCKS_PER_SEC)<<";"<<std::endl;
    // std::cout<<"st := time():o1:= expand(prem(g, f, y)):time() - st;o1-o;"<<std::endl;

    // std::cout<<"t=Association[];t2=Association[];\n";

    // clpoly::polynomial_ZZ p=clpoly::random_polynomial<clpoly::ZZ>({x,y,z,d},5,0.05,10,-10);
    // std::cout<<"p="<<p<<std::endl;
    // auto l=p.variables();
    // for (auto &i:l)
    //     std::cout<<i.first<<":"<<i.second<<" ";
    // std::cout<<std::endl<<p.degree()<<std::endl;
    t=clock();
    clpoly::polynomial_ZZ PP;
    try
    {
        PP=read_file("j621_data.txt");
    }
    catch(const char* msg)
    {
        std::cout<<msg<<std::endl;
        return 1;
    }
    std::cout<<PP.size()<<std::endl;
    std::cout<<"( "<<double(clock()-t)/CLOCKS_PER_SEC<<"s)\n";
    t=clock();
    auto ll=PP.variables();
    for (auto &i:ll)
        std::cout<<i.first<<":"<<i.second<<" ";
    std::cout<<std::endl;
    std::cout<<"("<<double(clock()-t)/CLOCKS_PER_SEC<<"s)\n";
     t=clock();
    std::cout<<std::hash<clpoly::polynomial_ZZ>()(PP)<<std::endl;
    std::cout<<"("<<double(clock()-t)/CLOCKS_PER_SEC<<"s)\n";
    // t=clock();
    // std::cout<<PP.degree()<<std::endl;
    // std::cout<<"("<<double(clock()-t)/CLOCKS_PER_SEC<<"s)\n";

    // clpoly::Zp a(10,7);
    // clpoly::Zp b(-10,7);
    // a=-2;
    // std::cout<<a<<" "<<b<<" "<<a*b<<" "<<a/b<<" "<<-a<<std::endl;

    // g=-pow(r,2)*pow(x,2) + pow(x,4) + pow(x,2)*pow(z,2) + pow(z,4) - 2*pow(z,2) + 1;
    // std::cout<<g<<std::endl;
    // std::cout<<clpoly::polynomial_mod(std::move(g),7)<<std::endl;
    // a=std::move(b);
    // std::cout<<a<<b<<std::endl;
    // f=pow(x,4)+25*pow(x,3)+145*pow(x,2)-171*x-360;
    // g=pow(x,5)+14*pow(x,4)+15*pow(x,3)-pow(x,2)-14*x-15;
    // f=2*pow(x,4)-7*pow(x,3)-4*pow(x,2)-4*x-15;
    // g=4*pow(x,5)+4*pow(x,3)-7*pow(x,2)-2*pow(x,4)+x-12;

    // f=9*pow(x,5)+2*pow(x,4)*y*z-189*pow(x,3)*pow(y,3)*z+117*pow(x,3)*y*pow(z,2)+3*pow(x,3)-42*pow(x,2)*pow(y,4)*pow(z,2)
    //                 +26*pow(x,2)*pow(y,2)*pow(z,3)+18*pow(x,2)-63*x*pow(y,3)*z+39*x*y*pow(z,2)+4*x*y*z+6;
    // g=6*pow(x,6)-126*pow(x,4)*pow(y,3)*z+78*pow(x,4)*y*pow(z,2)+pow(x,4)*y+pow(x,4)*z+13*pow(x,3)
    //     -21*pow(x,2)*pow(y,4)*z-21*pow(x,2)*pow(y,3)*pow(z,2)+13*pow(x,2)*pow(y,2)*pow(z,2)+13*pow(x,2)*y*pow(z,3)
    //     -21*x*pow(y,3)*z+13*x*y*pow(z,2)+2*x*y+2*x*z+2;
    // g=-3*pow(y,8)*pow(d,2)-2*pow(y,5)*pow(d,5)-2*pow(d,10)+5*pow(y,8)*d+3*pow(y,5)*pow(d,4)
    //     -7*pow(y,4)*pow(d,5)+5*pow(y,3)*pow(d,6)+10*pow(d,6)+pow(d,5)+3*pow(y,3)+4;
    // f=-9*pow(x,8)*pow(z,2)+6*pow(x,5)*pow(z,5)+2*pow(x,4)*pow(z,6)+9*pow(x,2)*z-9;
    // g=-7*pow(x,8)*pow(z,2)+2*pow(x,6)*y*pow(z,3)-3*pow(x,5)*pow(y,3)*pow(z,2)+pow(x,5)*y*pow(z,4)+10*pow(x,5)*pow(z,5)-9*pow(x,4)*pow(y,3)*pow(z,3)-pow(x,2)*pow(y,5)*pow(z,3)-6*pow(x,2)*y*pow(z,7)
    //   -2*x*pow(z,9)+2*pow(x,5)*pow(y,4)-8*pow(x,2)*pow(y,3)*pow(z,4)-pow(x,2)*pow(z,7)-4*x*pow(y,5)*pow(z,2)+3*x*pow(y,4)*pow(z,2)-2*x*pow(y,2)*pow(z,4)-7*pow(x,5)*z+7*pow(x,3)*pow(y,3)-2*x*pow(y,2)*pow(z,3)+6*y*pow(z,5)+9*pow(x,2)*pow(y,3)-8*x*pow(z,4)+9*pow(x,3)*z+2*pow(x,2)*pow(z,2)+5;
    // f=-7*pow(x,6)*y*pow(z,3)-8*pow(x,4)*pow(y,5)*z+10*pow(x,3)*pow(y,5)*pow(z,2)-3*pow(x,2)*pow(y,3)*pow(z,5)+pow(x,2)*pow(y,2)*pow(z,6)-2*x*y*pow(z,8)+10*pow(y,5)*pow(z,5)+pow(x,6)*y*pow(z,2)
    //    +8*pow(x,5)*pow(y,4)-9*pow(y,7)*pow(z,2)+pow(y,5)*pow(z,4)-pow(x,5)*y*pow(z,2)+10*pow(x,4)*pow(z,4)-5*pow(x,2)*pow(y,6)-3*x*pow(y,5)*pow(z,2)-6*pow(x,2)*pow(y,3)*pow(z,2)-x*pow(y,6)-5*pow(y,5)*pow(z,2)
    //   -9*pow(z,7)+2*pow(x,4)*pow(y,2)+6*pow(x,4)*y*z+7*x*pow(z,5)-pow(y,4)*pow(z,2)+9*pow(x,4)*z+4*pow(x,2)*pow(y,3)-9*x*pow(y,3)*z-3*x*y*pow(z,3)+9*y*pow(z,4)-10*pow(x,4)-6*x*pow(z,3)-9*y*pow(z,3)+6*x*y*z+8;
    // g=6*pow(x,10)+9*pow(x,8)*pow(y,2)-2*pow(y,4)*pow(z,6)-9*pow(x,4)*pow(z,5)+2*pow(x,3)*pow(y,4)*pow(z,2)+5*pow(x,8)+9*pow(x,6)*pow(z,2)+pow(x,3)*pow(y,5)-2*pow(x,3)*pow(y,2)*pow(z,3)+6*pow(x,2)*pow(z,6)+4*pow(y,6)*pow(z,2)+5*pow(y,4)*pow(z,4)-pow(x,7)-10*pow(x,6)*z+10*pow(x,3)*pow(y,2)*pow(z,2)+8*pow(y,6)*z-4*pow(y,5)*pow(z,2)+10*pow(y,2)*pow(z,5)-5*pow(x,2)*pow(y,2)*pow(z,2)+2*pow(x,2)*y*pow(z,3)-7*x*pow(y,3)*pow(z,2)-10*pow(x,4)+5*pow(y,4)+8;
    // f=-2*pow(x,5)*pow(y,2)*pow(z,3)-3*x*pow(y,7)*pow(z,2)+10*x*pow(y,5)*pow(z,4)-4*pow(y,10)+4*pow(y,6)*pow(z,4)+7*pow(x,8)*z-5*pow(x,5)*pow(y,4)+4*pow(x,4)*pow(z,5)+7*x*pow(y,2)*pow(z,6)-4*pow(y,5)*pow(z,4)+6*pow(x,6)*y*z+7*pow(x,5)*pow(y,2)*z+5*pow(x,5)*pow(z,3)-7*pow(x,4)*pow(y,3)*z-10*pow(x,2)*pow(z,6)+2*x*pow(y,5)*pow(z,2)-2*pow(x,5)*y*z-10*pow(x,3)*pow(y,3)*z+x*pow(y,4)*z-5*pow(y,4)*pow(z,2)+9*pow(y,3)*pow(z,2)+2*pow(y,2)*pow(z,3)+8*pow(x,3)*y-5*x*y*pow(z,2)+10;
    // // clpoly::polynomial_<clpoly::ZZ,clpoly::lex> f_,g_;
    // // clpoly::poly_convert(f,f_);
    // // clpoly::poly_convert(g,g_);
    // std::cout<<"f:"<<f<<std::endl;
    // std::cout<<"g:"<<g<<std::endl;
    // // std::cout<<clpoly::polynomial_GCD(f*g,g*g)<<std::endl;
    // clpoly::lex_<clpoly::custom_var_order> mo(std::vector<clpoly::variable>({z,y,x}));
    // clpoly::polynomial_<clpoly::ZZ,clpoly::lex_<clpoly::custom_var_order>> p1(&mo);
    // clpoly::poly_convert(g,p1);
    // std::cout<< p1<<std::endl;
    return 0;

}


#ifndef STAN_MCMC_CHAINS_HPP
#define STAN_MCMC_CHAINS_HPP

#include <stan/util/io/stan_csv_reader.hpp>
#include <stan/math/prim/mat.hpp>
#include <stan/util/analyze/mcmc/compute_effective_sample_size.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/tail_quantile.hpp>
#include <boost/accumulators/statistics/p_square_quantile.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/covariance.hpp>
#include <boost/accumulators/statistics/variates/covariate.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/additive_combine.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <sstream>
#include <utility>
#include <vector>
#include <cstdlib>

namespace stan {
  namespace mcmc {
    using Eigen::Dynamic;

    /**
     * An mcmc::chains object stores parameter names and
     * dimensionalities along with samples from multiple chains.
     *
     *
Synchronization: For arbitrary concurrent use, the
     * read and write methods need to be read/write locked.  Multiple
     * writers can be used concurrently if they write to different
     * chains.  Readers for single chains need only be read/write locked
     * with writers of that chain.  For reading across chains, full
     * read/write locking is required.  Thus methods will be classified
     * as global or single-chain read or write methods.
     *
     *
Storage Order: Storage is column/last-index major.
     */
    template <class RNG = boost::random::ecuyer1988>
    class chains {
    private:
      Eigen::Matrix<std::string, Dynamic, 1> param_names_;
      Eigen::Matrix<Eigen::MatrixXd, Dynamic, 1> samples_;
      Eigen::VectorXi warmup_;

      static double mean(const Eigen::VectorXd& x) {
        return (x.array() / x.size()).sum();
      }

      static double variance(const Eigen::VectorXd& x) {
        double m = mean(x);
        return ((x.array() - m) / std::sqrt((x.size() - 1.0))).square().sum();
      }

      static double sd(const Eigen::VectorXd& x) {
        return std::sqrt(variance(x));
      }

      static double covariance(const Eigen::VectorXd& x,
                               const Eigen::VectorXd& y,
                               std::ostream* err = 0) {
        if (x.rows() != y.rows() && err)
          *err << "warning: covariance of different length chains";
        using boost::accumulators::accumulator_set;
        using boost::accumulators::stats;
        using boost::accumulators::tag::variance;
        using boost::accumulators::tag::covariance;
        using boost::accumulators::tag::covariate1;

        accumulator_set<double, stats<covariance<double, covariate1> > > acc;

        int M = std::min(x.size(), y.size());
        for (int i = 0; i < M; i++)
          acc(x(i), boost::accumulators::covariate1 = y(i));

        return boost::accumulators::covariance(acc) * M / (M-1);
      }

      static double correlation(const Eigen::VectorXd& x,
                                const Eigen::VectorXd& y,
                                std::ostream* err = 0) {
        if (x.rows() != y.rows() && err)
          *err << "warning: covariance of different length chains";
        using boost::accumulators::accumulator_set;
        using boost::accumulators::stats;
        using boost::accumulators::tag::variance;
        using boost::accumulators::tag::covariance;
        using boost::accumulators::tag::covariate1;

        accumulator_set<double, stats<variance,
                                      covariance<double, covariate1> > > acc_xy;
        accumulator_set<double, stats<variance> > acc_y;

        int M = std::min(x.size(), y.size());
        for (int i = 0; i < M; i++) {
          acc_xy(x(i), boost::accumulators::covariate1 = y(i));
          acc_y(y(i));
        }

        double cov = boost::accumulators::covariance(acc_xy);
        if (cov > -1e-8 && cov < 1e-8)
          return cov;
        return cov / std::sqrt(boost::accumulators::variance(acc_xy)
                               * boost::accumulators::variance(acc_y));
      }

      static double quantile(const Eigen::VectorXd& x, const double prob) {
        using boost::accumulators::accumulator_set;
        using boost::accumulators::left;
        using boost::accumulators::quantile;
        using boost::accumulators::quantile_probability;
        using boost::accumulators::right;
        using boost::accumulators::stats;
        using boost::accumulators::tag::tail;
        using boost::accumulators::tag::tail_quantile;
        double M = x.rows();
        // size_t cache_size = std::min(prob, 1-prob)*M + 2;
        size_t cache_size = M;

        if (prob < 0.5) {
          accumulator_set<double, stats<tail_quantile<left> > >
            acc(tail<left>::cache_size = cache_size);
          for (int i = 0; i < M; i++)
            acc(x(i));
          return quantile(acc, quantile_probability = prob);
        }
        accumulator_set<double, stats<tail_quantile<right> > >
          acc(tail<right>::cache_size = cache_size);
        for (int i = 0; i < M; i++)
          acc(x(i));
        return quantile(acc, quantile_probability = prob);
      }

      static Eigen::VectorXd
      quantiles(const Eigen::VectorXd& x, const Eigen::VectorXd& probs) {
        using boost::accumulators::accumulator_set;
        using boost::accumulators::left;
        using boost::accumulators::quantile_probability;
        using boost::accumulators::quantile;
        using boost::accumulators::right;
        using boost::accumulators::stats;
        using boost::accumulators::tag::tail;
        using boost::accumulators::tag::tail_quantile;
        double M = x.rows();

        // size_t cache_size = M/2 + 2;
        size_t cache_size = M;  // 2 + 2;

        accumulator_set<double, stats<tail_quantile<left> > >
          acc_left(tail<left>::cache_size = cache_size);
        accumulator_set<double, stats<tail_quantile<right> > >
          acc_right(tail<right>::cache_size = cache_size);

        for (int i = 0; i < M; i++) {
          acc_left(x(i));
          acc_right(x(i));
        }

        Eigen::VectorXd q(probs.size());
        for (int i = 0; i < probs.size(); i++) {
          if (probs(i) < 0.5)
            q(i) = quantile(acc_left,
                            quantile_probability = probs(i));
          else
            q(i) = quantile(acc_right,
                            quantile_probability = probs(i));
        }
        return q;
      }

      static Eigen::VectorXd autocorrelation(const Eigen::VectorXd& x) {
        using std::vector;
        using stan::math::index_type;
        typedef typename index_type<vector<double> >::type idx_t;

        std::vector<double> ac;
        std::vector<double> sample(x.size());
        for (int i = 0; i < x.size(); i++)
          sample[i] = x(i);
        stan::math::autocorrelation(sample, ac);

        Eigen::VectorXd ac2(ac.size());
        for (idx_t i = 0; i < ac.size(); i++)
          ac2(i) = ac[i];
        return ac2;
      }

      static Eigen::VectorXd autocovariance(const Eigen::VectorXd& x) {
        using std::vector;
        using stan::math::index_type;
        typedef typename index_type<vector<double> >::type idx_t;

        std::vector<double> ac;
        std::vector<double> sample(x.size());
        for (int i = 0; i < x.size(); i++)
          sample[i] = x(i);
        stan::math::autocovariance(sample, ac);

        Eigen::VectorXd ac2(ac.size());
        for (idx_t i = 0; i < ac.size(); i++)
          ac2(i) = ac[i];
        return ac2;
      }

      /**
       * Return the split potential scale reduction (split R hat)
       * for the specified parameter.
       *
       * Current implementation takes the minimum number of samples
       * across chains as the number of samples per chain.
       *
       * @param VectorXd
       * @param Dynamic
       * @param samples
       *
       * @return
       */
      double
      split_potential_scale_reduction(
                                      const Eigen::Matrix<Eigen::VectorXd,
                                      Dynamic, 1> &samples) const {
        int chains = samples.size();
        int n_samples = samples(0).size();
        for (int chain = 1; chain < chains; chain++) {
          n_samples = std::min(n_samples,
                               static_cast<int>(samples(chain).size()));
        }
        if (n_samples % 2 == 1)
          n_samples--;
        int n = n_samples / 2;

        Eigen::VectorXd split_chain_mean(2*chains);
        Eigen::VectorXd split_chain_var(2*chains);

        for (int chain = 0; chain < chains; chain++) {
          split_chain_mean(2*chain) = mean(samples(chain).topRows(n));
          split_chain_mean(2*chain+1) = mean(samples(chain).bottomRows(n));

          split_chain_var(2*chain) = variance(samples(chain).topRows(n));
          split_chain_var(2*chain+1) = variance(samples(chain).bottomRows(n));
        }

        double var_between = n * variance(split_chain_mean);
        double var_within = mean(split_chain_var);

        // rewrote [(n-1)*W/n + B/n]/W as (n-1+ B/W)/n
        return sqrt((var_between/var_within + n-1)/n);
      }

    public:
      explicit chains(const Eigen::Matrix<std::string, Dynamic, 1>& param_names)
        : param_names_(param_names) { }

      explicit chains(const std::vector<std::string>& param_names)
        : param_names_(param_names.size()) {
        for (size_t i = 0; i < param_names.size(); i++)
          param_names_(i) = param_names[i];
      }

      explicit chains(const stan::io::stan_csv& stan_csv)
        : param_names_(stan_csv.header) {
        if (stan_csv.samples.rows() > 0)
          add(stan_csv);
      }

      inline int num_chains() const {
        return samples_.size();
      }

      inline int num_params() const {
        return param_names_.size();
      }

      const Eigen::Matrix<std::string, Dynamic, 1>& param_names() const {
        return param_names_;
      }

      const std::string& param_name(int j) const {
        return param_names_(j);
      }

      int index(const std::string& name) const {
        int index = -1;
        for (int i = 0; i < param_names_.size(); i++)
          if (param_names_(i) == name)
            return i;
        return index;
      }

      void set_warmup(const int chain, const int warmup) {
        warmup_(chain) = warmup;
      }

      void set_warmup(const int warmup) {
        warmup_.setConstant(warmup);
      }

      const Eigen::VectorXi& warmup() const {
        return warmup_;
      }

      int warmup(const int chain) const {
        return warmup_(chain);
      }

      int num_samples(const int chain) const {
        return samples_(chain).rows();
      }

      int num_samples() const {
        int n = 0;
        for (int chain = 0; chain < num_chains(); chain++)
          n += num_samples(chain);
        return n;
      }

      int num_kept_samples(const int chain) const {
        return num_samples(chain) - warmup(chain);
      }

      int num_kept_samples() const {
        int n = 0;
        for (int chain = 0; chain < num_chains(); chain++)
          n += num_kept_samples(chain);
        return n;
      }

      void add(const int chain,
               const Eigen::MatrixXd& sample) {
        if (sample.cols() != num_params())
          throw std::invalid_argument("add(chain, sample): number of columns"
                                      " in sample does not match chains");
        if (num_chains() == 0 || chain >= num_chains()) {
          int n = num_chains();

          // Need this block for Windows. conservativeResize
          // does not keep the references.
          Eigen::Matrix<Eigen::MatrixXd, Dynamic, 1>
            samples_copy(num_chains());
          Eigen::VectorXi warmup_copy(num_chains());
          for (int i = 0; i < n; i++) {
            samples_copy(i) = samples_(i);
            warmup_copy(i) = warmup_(i);
          }

          samples_.resize(chain+1);
          warmup_.resize(chain+1);
          for (int i = 0; i < n; i++) {
            samples_(i) = samples_copy(i);
            warmup_(i) = warmup_copy(i);
          }
          for (int i = n; i < chain+1; i++) {
            samples_(i) = Eigen::MatrixXd(0, num_params());
            warmup_(i) = 0;
          }
        }
        int row = samples_(chain).rows();
        Eigen::MatrixXd new_samples(row+sample.rows(), num_params());
        new_samples << samples_(chain), sample;
        samples_(chain) = new_samples;
      }

      void add(const Eigen::MatrixXd& sample) {
        if (sample.rows() == 0)
          return;
        if (sample.cols() != num_params())
          throw std::invalid_argument("add(sample): number of columns in"
                                      " sample does not match chains");
        add(num_chains(), sample);
      }

      /**
       * Convert a vector of vector<double> to Eigen::MatrixXd
       *
       * This method is added for the benefit of software wrapping
       * Stan (e.g., PyStan) so that it need not additionally wrap Eigen.
       *
       */
      void add(const std::vector<std::vector<double> >& sample) {
        int n_row = sample.size();
        if (n_row == 0)
          return;
        int n_col = sample[0].size();
        Eigen::MatrixXd sample_copy(n_row, n_col);
        for (int i = 0; i < n_row; i++) {
          sample_copy.row(i)
            = Eigen::VectorXd::Map(&sample[i][0], sample[0].size());
        }
        add(sample_copy);
      }

      void add(const stan::io::stan_csv& stan_csv) {
        if (stan_csv.header.size() != num_params())
          throw std::invalid_argument("add(stan_csv): number of columns in"
                                      " sample does not match chains");
        if (!param_names_.cwiseEqual(stan_csv.header).all()) {
          throw std::invalid_argument("add(stan_csv): header does not match"
                                      " chain's header");
        }
        add(stan_csv.samples);
        if (stan_csv.metadata.save_warmup)
          set_warmup(num_chains()-1, stan_csv.metadata.num_warmup);
      }

      Eigen::VectorXd samples(const int chain, const int index) const {
        return samples_(chain).col(index).bottomRows(num_kept_samples(chain));
      }

      Eigen::VectorXd samples(const int index) const {
        Eigen::VectorXd s(num_kept_samples());
        int start = 0;
        for (int chain = 0; chain < num_chains(); chain++) {
          int n = num_kept_samples(chain);
          s.middleRows(start, n) = samples_(chain).col(index).bottomRows(n);
          start += n;
        }
        return s;
      }

      Eigen::VectorXd samples(const int chain, const std::string& name) const {
        return samples(chain, index(name));
      }

      Eigen::VectorXd samples(const std::string& name) const {
        return samples(index(name));
      }

      double mean(const int chain, const int index) const {
        return mean(samples(chain, index));
      }

      double mean(const int index) const {
        return mean(samples(index));
      }

      double mean(const int chain, const std::string& name) const {
        return mean(chain, index(name));
      }

      double mean(const std::string& name) const {
        return mean(index(name));
      }

      double sd(const int chain, const int index) const {
        return sd(samples(chain, index));
      }

      double sd(const int index) const {
        return sd(samples(index));
      }

      double sd(const int chain, const std::string& name) const {
        return sd(chain, index(name));
      }

      double sd(const std::string& name) const {
        return sd(index(name));
      }

      double variance(const int chain, const int index) const {
        return variance(samples(chain, index));
      }

      double variance(const int index) const {
        return variance(samples(index));
      }

      double variance(const int chain, const std::string& name) const {
        return variance(chain, index(name));
      }

      double variance(const std::string& name) const {
        return variance(index(name));
      }

      double
      covariance(const int chain, const int index1, const int index2) const {
        return covariance(samples(chain, index1), samples(chain, index2));
      }

      double covariance(const int index1, const int index2) const {
        return covariance(samples(index1), samples(index2));
      }

      double covariance(const int chain, const std::string& name1,
                        const std::string& name2) const {
        return covariance(chain, index(name1), index(name2));
      }

      double
      covariance(const std::string& name1, const std::string& name2) const {
        return covariance(index(name1), index(name2));
      }

      double
      correlation(const int chain, const int index1, const int index2) const {
        return correlation(samples(chain, index1), samples(chain, index2));
      }

      double correlation(const int index1, const int index2) const {
        return correlation(samples(index1), samples(index2));
      }

      double correlation(const int chain, const std::string& name1,
                         const std::string& name2) const {
        return correlation(chain, index(name1), index(name2));
      }

      double
      correlation(const std::string& name1, const std::string& name2) const {
        return correlation(index(name1), index(name2));
      }

      double
      quantile(const int chain, const int index, const double prob) const {
        return quantile(samples(chain, index), prob);
      }

      double quantile(const int index, const double prob) const {
        return quantile(samples(index), prob);
      }

      double quantile(int chain, const std::string& name, double prob) const {
        return quantile(chain, index(name), prob);
      }

      double quantile(const std::string& name, const double prob) const {
        return quantile(index(name), prob);
      }

      Eigen::VectorXd
      quantiles(int chain, int index, const Eigen::VectorXd& probs) const {
        return quantiles(samples(chain, index), probs);
      }

      Eigen::VectorXd quantiles(int index, const Eigen::VectorXd& probs) const {
        return quantiles(samples(index), probs);
      }

      Eigen::VectorXd
      quantiles(int chain, const std::string& name,
                const Eigen::VectorXd& probs) const {
        return quantiles(chain, index(name), probs);
      }

      Eigen::VectorXd
      quantiles(const std::string& name, const Eigen::VectorXd& probs) const {
        return quantiles(index(name), probs);
      }

      Eigen::Vector2d central_interval(int chain, int index,
                                       double prob) const {
        double low_prob = (1-prob)/2;
        double high_prob = 1-low_prob;

        Eigen::Vector2d interval;
        interval
          << quantile(chain, index, low_prob),
          quantile(chain, index, high_prob);
        return interval;
      }

      Eigen::Vector2d central_interval(int index, double prob) const {
        double low_prob = (1-prob)/2;
        double high_prob = 1-low_prob;

        Eigen::Vector2d interval;
        interval << quantile(index, low_prob), quantile(index, high_prob);
        return interval;
      }

      Eigen::Vector2d
      central_interval(int chain, const std::string& name,
                       double prob) const {
        return central_interval(chain, index(name), prob);
      }

      Eigen::Vector2d central_interval(const std::string& name,
                                       double prob) const {
        return central_interval(index(name), prob);
      }

      Eigen::VectorXd autocorrelation(const int chain, const int index) const {
        return autocorrelation(samples(chain, index));
      }

      Eigen::VectorXd autocorrelation(int chain,
                                      const std::string& name) const {
        return autocorrelation(chain, index(name));
      }

      Eigen::VectorXd autocovariance(const int chain, const int index) const {
        return autocovariance(samples(chain, index));
      }

      Eigen::VectorXd autocovariance(int chain, const std::string& name) const {
        return autocovariance(chain, index(name));
      }

      // FIXME: reimplement using autocorrelation.
      double effective_sample_size(const int index) const {
        int n_chains = num_chains();
        std::vector<const double*> draws(n_chains);
        std::vector<size_t> sizes(n_chains);
        int n_kept_samples = 0;
        for (int chain = 0; chain < n_chains; ++chain) {
          n_kept_samples = num_kept_samples(chain);
          draws[chain]
            = samples_(chain).col(index).bottomRows(n_kept_samples).data();
          sizes[chain] = n_kept_samples;
        }
        return analyze::compute_effective_sample_size(draws, sizes);
      }

      double effective_sample_size(const std::string& name) const {
        return effective_sample_size(index(name));
      }

      double split_effective_sample_size(const int index) const {
        int n_chains = num_chains();
        std::vector<const double*> draws(n_chains);
        std::vector<size_t> sizes(n_chains);
        int n_kept_samples = 0;
        for (int chain = 0; chain < n_chains; ++chain) {
          n_kept_samples = num_kept_samples(chain);
          draws[chain]
            = samples_(chain).col(index).bottomRows(n_kept_samples).data();
          sizes[chain] = n_kept_samples;
        }
        return analyze::compute_split_effective_sample_size(draws, sizes);
      }

      double split_effective_sample_size(const std::string& name) const {
        return split_effective_sample_size(index(name));
      }

      double split_potential_scale_reduction(const int index) const {
        Eigen::Matrix<Eigen::VectorXd, Dynamic, 1>
          samples(num_chains());
        for (int chain = 0; chain < num_chains(); chain++) {
          samples(chain) = this->samples(chain, index);
        }
        return split_potential_scale_reduction(samples);
      }

      double split_potential_scale_reduction(const std::string& name) const {
        return split_potential_scale_reduction(index(name));
      }
    };

  }
}

#endif


#include "mpi_fsi.h"
#include "mpi_scnsim.h"
#include "mpi_shared_hypo_elasticity.h"
#include "parameters.h"
#include "utilities.h"
#include <deal.II/grid/grid_in.h>
#include <deal.II/grid/grid_out.h>

extern template class Fluid::MPI::SCnsIM<3>;
extern template class Solid::MPI::SharedHypoElasticity<3>;
extern template class MPI::FSI<3>;

using namespace dealii;

int main(int argc, char *argv[])
{
  using namespace dealii;

  try
    {
      Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

      std::string infile("parameters.prm");
      if (argc > 1)
        {
          infile = argv[1];
        }
      Parameters::AllParameters params(infile);

      if (params.dimension == 3)
        {
          // Read solid mesh
          Triangulation<3> tria_solid;
          dealii::GridGenerator::subdivided_hyper_rectangle(tria_solid,
                                                            {20u, 20u, 8u},
                                                            Point<3>(0, 0, 0),
                                                            Point<3>(1, 1, 0.4),
                                                            true);

          // Read fluid mesh
          parallel::distributed::Triangulation<3> tria_fluid(MPI_COMM_WORLD);
          dealii::GridGenerator::subdivided_hyper_rectangle(tria_fluid,
                                                            {10u, 10u, 40u},
                                                            Point<3>(0, 0, 0),
                                                            Point<3>(1, 1, 4),
                                                            true);
          for (auto cell : tria_fluid.active_cell_iterators())
            {
              auto center = cell->center();
              if (center[2] >= 2 && center[2] <= 2.4)
                cell->set_refine_flag();
            }
          tria_fluid.execute_coarsening_and_refinement();

          // Translate solid mesh
          Tensor<1, 3> offset({0, 0, 2});
          GridTools::shift(offset, tria_solid);

          Fluid::MPI::SCnsIM<3> fluid(tria_fluid, params);
          Solid::MPI::SharedHypoElasticity<3> solid(
            tria_solid, params, 0.05, 1.3);
          MPI::FSI<3> fsi(fluid, solid, params);
          fsi.run();
        }
      else
        {
          AssertThrow(false, ExcNotImplemented());
        }
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
  return 0;
}


///////////////////////////////////////////////////////////////////////////////
// kolmogorov_smirnov::statistic.hpp                                         //
//                                                                           //
//  Copyright 2010 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_KOLMOGOROV_SMIRNOV_STATISTIC_HPP_ER_2010
#define BOOST_STATISTICS_DETAIL_KOLMOGOROV_SMIRNOV_STATISTIC_HPP_ER_2010
#include <boost/type_traits.hpp>
#include <boost/range.hpp>

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/if.hpp>

#include <boost/foreach.hpp>
#include <boost/parameter/binding.hpp>

#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/count.hpp>

#include <boost/statistics/detail/non_parametric/empirical_distribution/count.hpp>

namespace boost {
namespace statistics{
namespace detail{
namespace empirical_distribution{
namespace impl{

    // Warning : See empirical_distribution::impl::count
    template<typename T>
    class kolmogorov_smirnov : public boost::accumulators::accumulator_base{

        typedef std::size_t size_;
        typedef boost::accumulators::dont_care dont_care_;

        public:

        kolmogorov_smirnov(dont_care_){};

        typedef size_ size_type;
        typedef T sample_type;
        typedef void result_type;

        void operator()(dont_care_)const{}

        template<typename Args>
        result_type result(dont_care_) const
        {
        }
	};

}
namespace tag
{
    struct kolmogorov_smirnov
      : boost::accumulators::depends_on<
      	statistics::detail::empirical_distribution::tag::ordered_sample,
        boost::accumulators::tag::count
    >
    {
        struct impl{
            template<typename T,typename W>
            struct apply{
                typedef detail::kolmogorov_smirnov::impl::kolmogorov_smirnov<T> type;
            };
        };
    };
}
namespace result_of{

    template<typename T1,typename AccSet,typename D>
    struct kolmogorov_smirnov_statistic
    {
        typedef T1 type;
    };

}

    // Usage : statistic<T1>(acc,dist)
    template<typename T1,typename AccSet,typename D>
    typename kolmogorov_smirnov
        ::result_of::template kolmogorov_smirnov_statistic<T1,AccSet,D>::type
    kolmogorov_smirnov_statistic(AccSet const& acc,const D& dist)
    {
            namespace ed = boost::statistics::detail::empirical_distribution;
            namespace ks = boost::statistics::detail::kolmogorov_smirnov;
            typedef T1 val_;
            typedef std::size_t size_;
            typedef boost::accumulators::tag::count tag_n_;
            typedef ed::tag::ordered_sample tag_os_;

            typedef typename ed::result_of::ordered_sample<
                AccSet>::type ref_os_;
            typedef typename boost::remove_const< //in case ref changed to cref
            	typename boost::remove_reference<
                	ref_os_
                >::type
            >::type os_;
            typedef typename boost::range_reference<os_>::type ref_elem_;

            ref_os_ ref_os
                = boost::accumulators::extract_result<tag_os_>( acc );

            val_ m1 = static_cast<val_>(0);
            size_ i = 0;
            size_ n = boost::accumulators::extract::count( acc );

            BOOST_FOREACH(ref_elem_ e,ref_os){
                i += e.second;
                val_ ecdf = static_cast<val_>(i) / static_cast<val_>(n);
                val_ true_cdf = cdf( dist, e.first );
                val_ m2
                	= (true_cdf > ecdf)?(true_cdf - ecdf) : (ecdf - true_cdf);
                if(m2 > m1){ m1 = m2; }
            }

            return m1;
    }

}// kolgorov_statistic
}// empirical_distribution
}// detail
}// statistics
}// boost

#endif


#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cfenv>

#include <complex>
#include <iomanip>
#include <iostream>
#include <vector>

#include <armadillo>

#ifdef HAVE_OMP
#include <omp.h>
#endif

#include "pme.h"
#include "abort_unless.h"
#include "energies.h"

using namespace std;
using namespace arma;

const complex<double> I(0.0, 1.0);

ewald::exclusion_vector exclusions;

static bool excluded(size_t i, size_t j);

int main(int argc, char* argv[]) {
  if (argc < 8) {
    cout << "Usage: " << argv[0]
         << " dimension cell-file positions-file charges-file cut-off tolerance num-shells\n";
    return EXIT_FAILURE;
  }

  const long dim0 = atoi(argv[1]);
  const long dim1 = dim0;
  const long dim2 = dim0;

  mat::fixed<3, 3> L;
  L.load(argv[2]);
  const mat::fixed<3, 3> LinvT = trans(inv(L));
  L.print();

  mat r;
  r.load(argv[3]);
  abort_unless(r.n_rows == 3);
  const mat x = inv(L) * r;

  vec qq;
  qq.load(argv[4]);
  std::vector<double> q(r.n_cols);
  {
    for (size_t k = 0; k < q.size(); ++k)
      q[k] = qq(k);
  }

  const size_t num_particles = q.size();
  clog << "Read " << num_particles << " particles into memory.\n";

  std::vector<double> coor_x(num_particles);
  std::vector<double> coor_y(num_particles);
  std::vector<double> coor_z(num_particles);
  {
    for (size_t k = 0; k < num_particles; ++k) {
      coor_x[k] = r(0, k);
      coor_y[k] = r(1, k);
      coor_z[k] = r(2, k);
    }
  }

  const double cut_off = atof(argv[5]);
  const double cut_off2 = cut_off * cut_off;
  const double tolerance = atof(argv[6]);
  clog << "Real space cut-off: " << cut_off << "\n"
       << "Real space tolerance: " << tolerance << "\n\n";

  exclusions = ewald::exclusion_vector(num_particles);
  {                                     // Populate exclusion vector.
/*
    for (size_t i = 0; i < num_particles; ++i)
      for (size_t j = 0; j < num_particles; ++j)
        // if (i != j && ((i % 2) != (j % 2))) {
        if (abs(int(i) - int(j)) <= 2) {
          exclusions[i].push_back(j);
        }
*/
    /*
    for (size_t i = 0; i < num_particles; ++i) {
      std::cout << i << ": ";
      ewald::index_vector const& is = exclusions[i];
      for (size_t j = 0; j < is.size(); ++j)
        std::cout << is[j] << " ";
      std::cout << std::endl;
    }
    */
  }

  const double max_shell = int(atof(argv[7]));

  mat forces = zeros<mat>(3, num_particles);
  mat force_cutoff = zeros<mat>(3, num_particles);
  mat force_direct = zeros<mat>(3, num_particles);
  mat force_recip = zeros<mat>(3, num_particles);
  mat force_excluded = zeros<mat>(3, num_particles);
  mat force_excluded_pme = zeros<mat>(3, num_particles);

  std::vector<double> force_x(num_particles, 0.0);
  std::vector<double> force_y(num_particles, 0.0);
  std::vector<double> force_z(num_particles, 0.0);
  mat force_recip_pme = zeros<mat>(3, num_particles);
  double LL[3 * 3];
  for (size_t i = 0; i < 3; ++i)
    for (size_t j = 0; j < 3; ++j)
      LL[3*i+j] = L(i, j);

  energies energy_pme, energy_ewald;

  wall_clock timer;
  timer.tic();

#ifdef HAVE_OMP
  ewald::pme p(dim0, dim1, dim2, LL, num_particles, &q[0], cut_off, tolerance, omp_get_max_threads());
#else
  ewald::pme p(dim0, dim1, dim2, LL, num_particles, &q[0], cut_off, tolerance, 1);
#endif
  double seconds = timer.toc();

  clog << "PME initialization took " << seconds << " seconds.\n\n";

  const double t = p.get_threshold();
  if (std::isnan(t)) {
    cerr << "Unable to run PME for the prescribed values of cut off and tolerance.\n";
    return EXIT_FAILURE;
  }

  timer.tic();

  energy_pme.recip = p.energy(&coor_x[0], &coor_y[0], &coor_z[0],
                              &force_x[0], &force_y[0], &force_z[0]);

  for (size_t k = 0; k < num_particles; ++k) {
    force_recip_pme(0, k) = force_x[k];
    force_recip_pme(1, k) = force_y[k];
    force_recip_pme(2, k) = force_z[k];
  }

  seconds = timer.toc();
  clog << "PME took " << seconds << " seconds.\n\n";

  std::vector<double> force_excl_x(num_particles, 0.0);
  std::vector<double> force_excl_y(num_particles, 0.0);
  std::vector<double> force_excl_z(num_particles, 0.0);

  energy_pme.masked = p.excluded(exclusions,
                                 &coor_x[0],
                                 &coor_y[0],
                                 &coor_z[0],
                                 &force_excl_x[0],
                                 &force_excl_y[0],
                                 &force_excl_z[0]);

  for (size_t k = 0; k < num_particles; ++k) {
    force_excluded_pme(0, k) = force_excl_x[k];
    force_excluded_pme(1, k) = force_excl_y[k];
    force_excluded_pme(2, k) = force_excl_z[k];
  }

  energy_ewald.extra = energy_pme.extra = p.energy_extra();
  energy_ewald.self = energy_pme.self = p.energy_self();

  double Eexhaustive = 0.0;
  double Ecutoff = 0.0;

  mat force_ewald, force_pme;

  double elapsed = 0.0;

  for (int n = 0; n <= max_shell; n++) {
    wall_clock timer;
    timer.tic();
    for (int nx = -n; nx <= n; nx++) {
      for (int ny = -n; ny <= n; ny++) {
        for (int nz = -n; nz <= n; nz++) {
          if (abs(nx) != n && abs(ny) != n && abs(nz) != n)
            continue;

          const bool origin = (n == 0);

          const vec::fixed<3> m = { double(nx), double(ny), double(nz) };
          const vec::fixed<3> Lm = L * m;
          const vec::fixed<3> LinvTm = LinvT * m;

          // Compute direct space contribution.
          for (size_t i = 0; i < num_particles; i++) {
            for (size_t j = 0; j < num_particles; j++) {
              if (origin && i == j)
                continue;

              const vec::fixed<3> v = r.col(i) - r.col(j) + Lm;
              const double r2 = dot(v, v);
              const double r6 = r2 * r2 * r2;
              const double r8 = r6 * r2;

              const double qij = q[i] * q[j];

              const bool is_excluded = excluded(i, j);

              const double ener = 0.5 * qij / r6;
              const vec::fixed<3> f = 6.0 * qij / r8 * v;

              if (origin) {
                if (!is_excluded) {     // Unmasked pair.
                  Eexhaustive  += ener;
                  forces.col(i) += f;

                  if (r2 < cut_off2) {
                    Ecutoff += ener;
                    force_cutoff.col(i) += f;

                    vec::fixed<3> dv;
                    const double pot = 0.5 * qij
                        * p.direct_convergence_term(v.memptr(), dv.memptr());
                    energy_pme.direct += pot;
                    energy_ewald.direct += pot;
                    force_direct.col(i) += -qij * dv;
                  }
                } else {                // Masked pair.
                  // Note that we don't do cut offs here because we
                  // want to cancel the Fourier space contribution,
                  // which does not involve cut offs.
                  vec::fixed<3> dv;
                  energy_ewald.masked += (ener - 0.5 * qij
                                          * p.direct_convergence_term(v.memptr(),
                                                                      dv.memptr()));
                  force_excluded.col(i) += f + qij * dv;
                }
              } else {                  // Outer shells.
                Eexhaustive  += ener;
                forces.col(i) += f;

                if (r2 < cut_off2) {
                  Ecutoff += ener;
                  force_cutoff.col(i) += f;

                  vec::fixed<3> dv;
                  const double pot = 0.5 * qij
                      * p.direct_convergence_term(v.memptr(), dv.memptr());
                  energy_pme.direct += pot;
                  energy_ewald.direct += pot;
                  force_direct.col(i) += -qij * dv;
                }
              }
            }
          }

          // Compute contribution from Fourier space.
          if (!origin) {
            complex<double> rho(0.0, 0.0);

            for (size_t i = 0; i < num_particles; i++)
              rho += q[i] * exp(2.0 * M_PI * I * dot(m, x.col(i)));

            const double h2 = dot(LinvTm, LinvTm);
            energy_ewald.recip += p.recip_convergence_term(h2, rho);

            // force_recip.col(0) = -4.0 * M_PI * q[0] * q[1] * p.psi(h2) * sin(2.0 * M_PI * dot(LinvTm, r.col(0) - r.col(1))) * (-m);
            // force_recip.col(1) = -4.0 * M_PI * q[0] * q[1] * p.psi(h2) * sin(2.0 * M_PI * dot(LinvTm, r.col(0) - r.col(1))) * m;
          }
        }
      }
    }

    elapsed += timer.toc();

    force_ewald = force_direct + force_recip;
    force_pme   = force_direct + force_recip_pme - force_excluded_pme;

    {
      const double pme_total   = energy_pme.total();
      const double ewald_total = energy_ewald.total();

      const double rel_error_ewald_vs_pme    = fabsl(ewald_total - pme_total) / fabsl(ewald_total);
      const double rel_error_ewald_vs_cutoff = fabsl(ewald_total - Ecutoff) / fabsl(ewald_total);

      const double rel_error_exhaustive_vs_cutoff = fabsl(Eexhaustive - Ecutoff) / fabsl(Eexhaustive);
      const double rel_error_exhaustive_vs_ewald  = fabsl(Eexhaustive - ewald_total) / fabsl(Eexhaustive);
      const double rel_error_exhaustive_vs_pme    = fabsl(Eexhaustive - pme_total) / fabsl(Eexhaustive);

      cout << "==============================================\n"
           << fixed << setprecision(4)
           << "Shell " << n << " (" << elapsed << " seconds)" << "\n"
           << "==============================================\n"
           << scientific << setprecision(9)
           << "\n"
           << "Eexhaustive = " << Eexhaustive << "\n"
           << "Ecutoff     = " << Ecutoff << "\n"
           << "Ewald       = " << energy_ewald << "\n"
           << "PME         = " << energy_pme << "\n"
           << "\n"
           << "Abs. error in excluded forces = " << norm(force_excluded - force_excluded_pme, "inf") << "\n"
           << "\n"
           << "Abs. error in forces (Exhaustive vs. cutoff) = " << norm(forces - force_cutoff, "inf") << "\n"
           << "Abs. error in forces (Exhaustive vs. PME)    = " << norm(forces - force_pme, "inf") << "\n"
           << "\n"
           << "Rel. error in total energies (Ewald vs. cutoff) = " << rel_error_ewald_vs_cutoff << "\n"
           << "Rel. error in total energies (Ewald vs. PME)    = " << rel_error_ewald_vs_pme << "\n"
           << "\n"
           << "Rel. error in total energies (Exhaustive vs. cutoff) = " << rel_error_exhaustive_vs_cutoff << "\n"
           << "Rel. error in total energies (Exhaustive vs. Ewald)  = " << rel_error_exhaustive_vs_ewald << "\n"
           << "Rel. error in total energies (Exhaustive vs. PME)    = " << rel_error_exhaustive_vs_pme << "\n"
           << endl;
    }
  }

  return 0;
}

bool excluded(size_t i, size_t j) {
  // assert(i < num_particles);
  // assert(j < num_particles);

  ewald::index_vector const& is = exclusions[i];
  for (size_t k = 0; k < is.size(); ++k)
    if (j == is[k])
      return true;

  return false;
}


/* ============================================================================
* Copyright (c) 2009-2016 BlueQuartz Software, LLC
*
* 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 BlueQuartz Software, the US Air Force, 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.
*
* The code contained herein was partially funded by the followig contracts:
*    United States Air Force Prime Contract FA8650-07-D-5800
*    United States Air Force Prime Contract FA8650-10-D-5210
*    United States Prime Contract Navy N00173-07-C-2068
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

#include "ComputeUmeyamaTransform.h"

#include "SIMPLib/Common/Constants.h"
#include "SIMPLib/FilterParameters/AbstractFilterParametersReader.h"
#include "SIMPLib/FilterParameters/BooleanFilterParameter.h"
#include "SIMPLib/FilterParameters/DataContainerSelectionFilterParameter.h"
#include "SIMPLib/FilterParameters/LinkedPathCreationFilterParameter.h"
#include "SIMPLib/FilterParameters/SeparatorFilterParameter.h"
#include "SIMPLib/FilterParameters/StringFilterParameter.h"
#include "SIMPLib/Geometry/EdgeGeom.h"
#include "SIMPLib/Geometry/IGeometry2D.h"
#include "SIMPLib/Geometry/IGeometry3D.h"
#include "SIMPLib/Geometry/VertexGeom.h"

#include <Eigen/Dense>
#include <Eigen/Geometry>

#include "DREAM3DReview/DREAM3DReviewConstants.h"
#include "DREAM3DReview/DREAM3DReviewVersion.h"

/* Create Enumerations to allow the created Attribute Arrays to take part in renaming */
enum createdPathID : RenameDataPath::DataID_t
{
  AttributeMatrixID21 = 21,

  DataArrayID30 = 30,
  DataArrayID31 = 31,
};

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
ComputeUmeyamaTransform::ComputeUmeyamaTransform()
: m_SourcePointSet("")
, m_DestPointSet("")
, m_UseScaling(false)
, m_TransformationAttributeMatrixName("TransformationData")
, m_TransformationMatrixName("TransformationMatrix")
{
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
ComputeUmeyamaTransform::~ComputeUmeyamaTransform()
= default;

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::setupFilterParameters()
{
  FilterParameterVectorType parameters;
  parameters.push_back(SIMPL_NEW_BOOL_FP("Use Scaling", UseScaling, FilterParameter::Parameter, ComputeUmeyamaTransform));
  DataContainerSelectionFilterParameter::RequirementType dcReq;
  dcReq.dcGeometryTypes = {IGeometry::Type::Vertex, IGeometry::Type::Edge, IGeometry::Type::Triangle, IGeometry::Type::Quad, IGeometry::Type::Tetrahedral};
  parameters.push_back(SIMPL_NEW_DC_SELECTION_FP("Moving Geometry", SourcePointSet, FilterParameter::RequiredArray, ComputeUmeyamaTransform, dcReq));
  parameters.push_back(SIMPL_NEW_DC_SELECTION_FP("Fixed Geometry", DestPointSet, FilterParameter::RequiredArray, ComputeUmeyamaTransform, dcReq));
  parameters.push_back(SeparatorFilterParameter::New("Transformation Data", FilterParameter::CreatedArray));
  parameters.push_back(SIMPL_NEW_AM_WITH_LINKED_DC_FP("Transformation Attribute Matrix", TransformationAttributeMatrixName, SourcePointSet, FilterParameter::CreatedArray, ComputeUmeyamaTransform));
  parameters.push_back(SIMPL_NEW_DA_WITH_LINKED_AM_FP("Transformation Matrix", TransformationMatrixName, SourcePointSet, TransformationAttributeMatrixName, FilterParameter::CreatedArray, ComputeUmeyamaTransform));
  setFilterParameters(parameters);
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::readFilterParameters(AbstractFilterParametersReader* reader, int index)
{
  reader->openFilterGroup(this, index);
  setSourcePointSet(reader->readDataArrayPath("SourcePointSet", getSourcePointSet()));
  setDestPointSet(reader->readDataArrayPath("DestPointSet", getDestPointSet()));
  setUseScaling(reader->readValue("UseScaling", getUseScaling()));
  setTransformationAttributeMatrixName(reader->readString("TransformationAttributeMatrixName", getTransformationAttributeMatrixName()));
  setTransformationMatrixName(reader->readString("TransformationMatrixName", getTransformationMatrixName()));
  reader->closeFilterGroup();
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::initialize()
{
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::dataCheck()
{
  clearErrorCode();
  clearWarningCode();

  IGeometry::Pointer movingGeom = getDataContainerArray()->getPrereqGeometryFromDataContainer<IGeometry, AbstractFilter>(this, getSourcePointSet());
  IGeometry::Pointer fixedGeom = getDataContainerArray()->getPrereqGeometryFromDataContainer<IGeometry, AbstractFilter>(this, getDestPointSet());

  if(getErrorCode() < 0)
  {
    return;
  }

  if(!std::dynamic_pointer_cast<IGeometry2D>(movingGeom) && !std::dynamic_pointer_cast<IGeometry3D>(movingGeom) && !std::dynamic_pointer_cast<VertexGeom>(movingGeom) &&
     !std::dynamic_pointer_cast<EdgeGeom>(movingGeom))
  {
    QString ss = QObject::tr("Moving Geometry must be an unstructured geometry (Vertex, Edge, Triangle, Quadrilateral, or Tetrahedral), but the type is %1").arg(movingGeom->getGeometryTypeAsString());
    setErrorCondition(-702, ss);
  }

  if(!std::dynamic_pointer_cast<IGeometry2D>(fixedGeom) && !std::dynamic_pointer_cast<IGeometry3D>(fixedGeom) && !std::dynamic_pointer_cast<VertexGeom>(fixedGeom) &&
     !std::dynamic_pointer_cast<EdgeGeom>(fixedGeom))
  {
    QString ss = QObject::tr("Fixed Geometry must be an unstructured geometry (Vertex, Edge, Triangle, Quadrilateral, or Tetrahedral), but the type is %1").arg(fixedGeom->getGeometryTypeAsString());
    setErrorCondition(-702, ss);
  }

  size_t numMovingVertices = 0;
  size_t numFixedVertices = 0;

  if(IGeometry2D::Pointer igeom2D = std::dynamic_pointer_cast<IGeometry2D>(movingGeom))
  {
    numMovingVertices = igeom2D->getNumberOfVertices();
  }
  else if(IGeometry3D::Pointer igeom3D = std::dynamic_pointer_cast<IGeometry3D>(movingGeom))
  {
    numMovingVertices = igeom3D->getNumberOfVertices();
  }
  else if(VertexGeom::Pointer vertex = std::dynamic_pointer_cast<VertexGeom>(movingGeom))
  {
    numMovingVertices = vertex->getNumberOfVertices();
  }
  else if(EdgeGeom::Pointer edge = std::dynamic_pointer_cast<EdgeGeom>(movingGeom))
  {
    numMovingVertices = edge->getNumberOfVertices();
  }

  if(IGeometry2D::Pointer igeom2D = std::dynamic_pointer_cast<IGeometry2D>(fixedGeom))
  {
    numFixedVertices = igeom2D->getNumberOfVertices();
  }
  else if(IGeometry3D::Pointer igeom3D = std::dynamic_pointer_cast<IGeometry3D>(fixedGeom))
  {
    numFixedVertices = igeom3D->getNumberOfVertices();
  }
  else if(VertexGeom::Pointer vertex = std::dynamic_pointer_cast<VertexGeom>(fixedGeom))
  {
    numFixedVertices = vertex->getNumberOfVertices();
  }
  else if(EdgeGeom::Pointer edge = std::dynamic_pointer_cast<EdgeGeom>(fixedGeom))
  {
    numFixedVertices = edge->getNumberOfVertices();
  }

  if(numMovingVertices != numFixedVertices)
  {
    QString ss = QObject::tr("The moving and fixed Geometries must have the same number of Vertices; the number of moving Vertices is %1 and the number of fixed Vertices is %2")
                     .arg(numMovingVertices)
                     .arg(numFixedVertices);
    setErrorCondition(-11000, ss);
  }

  DataContainer::Pointer m = getDataContainerArray()->getPrereqDataContainer(this, getSourcePointSet());

  if(getErrorCode() < 0)
  {
    return;
  }

  std::vector<size_t> tDims(1, 1);
  m->createNonPrereqAttributeMatrix(this, getTransformationAttributeMatrixName(), tDims, AttributeMatrix::Type::Generic, AttributeMatrixID21);

  std::vector<size_t> cDims(2, 4);
  DataArrayPath path(getSourcePointSet().getDataContainerName(), getTransformationAttributeMatrixName(), getTransformationMatrixName());

  m_TransformationMatrixPtr = getDataContainerArray()->createNonPrereqArrayFromPath<DataArray<float>, AbstractFilter, float>(this, path, 0, cDims, "", DataArrayID31);
  if(nullptr != m_TransformationMatrixPtr.lock()) /* Validate the Weak Pointer wraps a non-nullptr pointer to a DataArray<T> object */
  {
    m_TransformationMatrix = m_TransformationMatrixPtr.lock()->getPointer(0);
  } /* Now assign the raw pointer to data from the DataArray<T> object */
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::preflight()
{
  // These are the REQUIRED lines of CODE to make sure the filter behaves correctly
  setInPreflight(true);              // Set the fact that we are preflighting.
  emit preflightAboutToExecute();    // Emit this signal so that other widgets can do one file update
  emit updateFilterParameters(this); // Emit this signal to have the widgets push their values down to the filter
  dataCheck();                       // Run our DataCheck to make sure everthing is setup correctly
  emit preflightExecuted();          // We are done preflighting this filter
  setInPreflight(false);             // Inform the system this filter is NOT in preflight mode anymore.
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void ComputeUmeyamaTransform::execute()
{
  clearErrorCode();
  clearWarningCode();
  dataCheck();
  if(getErrorCode() < 0)
  {
    return;
  }

  IGeometry::Pointer movingGeom = getDataContainerArray()->getDataContainer(m_SourcePointSet)->getGeometry();
  IGeometry::Pointer fixedGeom = getDataContainerArray()->getDataContainer(m_DestPointSet)->getGeometry();

  float* movingPointsPtr = nullptr;
  float* fixedPointsPtr = nullptr;
  size_t numMovingVertices = 0;
  size_t numFixedVertices = 0;

  if(IGeometry2D::Pointer igeom2D = std::dynamic_pointer_cast<IGeometry2D>(movingGeom))
  {
    movingPointsPtr = igeom2D->getVertexPointer(0);
    numMovingVertices = igeom2D->getNumberOfVertices();
  }
  else if(IGeometry3D::Pointer igeom3D = std::dynamic_pointer_cast<IGeometry3D>(movingGeom))
  {
    movingPointsPtr = igeom3D->getVertexPointer(0);
    numMovingVertices = igeom3D->getNumberOfVertices();
  }
  else if(VertexGeom::Pointer vertex = std::dynamic_pointer_cast<VertexGeom>(movingGeom))
  {
    movingPointsPtr = vertex->getVertexPointer(0);
    numMovingVertices = vertex->getNumberOfVertices();
  }
  else if(EdgeGeom::Pointer edge = std::dynamic_pointer_cast<EdgeGeom>(movingGeom))
  {
    movingPointsPtr = edge->getVertexPointer(0);
    numMovingVertices = edge->getNumberOfVertices();
  }

  if(IGeometry2D::Pointer igeom2D = std::dynamic_pointer_cast<IGeometry2D>(fixedGeom))
  {
    fixedPointsPtr = igeom2D->getVertexPointer(0);
    numFixedVertices = igeom2D->getNumberOfVertices();
  }
  else if(IGeometry3D::Pointer igeom3D = std::dynamic_pointer_cast<IGeometry3D>(fixedGeom))
  {
    fixedPointsPtr = igeom3D->getVertexPointer(0);
    numFixedVertices = igeom3D->getNumberOfVertices();
  }
  else if(VertexGeom::Pointer vertex = std::dynamic_pointer_cast<VertexGeom>(fixedGeom))
  {
    fixedPointsPtr = vertex->getVertexPointer(0);
    numFixedVertices = vertex->getNumberOfVertices();
  }
  else if(EdgeGeom::Pointer edge = std::dynamic_pointer_cast<EdgeGeom>(fixedGeom))
  {
    fixedPointsPtr = edge->getVertexPointer(0);
    numFixedVertices = edge->getNumberOfVertices();
  }

  // Eigen does best with things in column major, but we'll ultimately want
  // the matrix as row major; perform the actual computation in column
  // major and then transpose when we're ready to move it into a DataArray
  typedef Eigen::Matrix<float, 3, Eigen::Dynamic, Eigen::ColMajor> PointCloud;
  typedef Eigen::Matrix<float, 4, 4, Eigen::ColMajor> UmeyamaTransform;

  // Map our DataArray pointers directly to Eigen objects in memory to avoid making copies
  Eigen::Map<PointCloud> moving(movingPointsPtr, 3, numMovingVertices);
  Eigen::Map<PointCloud> fixed(fixedPointsPtr, 3, numFixedVertices);

  UmeyamaTransform transformMatrix = Eigen::umeyama(moving, fixed, m_UseScaling);

  // The matrix is in column major, transpose it in place to make it row major
  transformMatrix.transposeInPlace();
  float* umeyamaArray = &transformMatrix(0);

  // Copy the transformation matrix over to our DataArray
  // We know the size will be exactly sixteen...
  for(size_t i = 0; i < 16; i++)
  {
    m_TransformationMatrix[i] = umeyamaArray[i];
  }

}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
AbstractFilter::Pointer ComputeUmeyamaTransform::newFilterInstance(bool copyFilterParameters) const
{
  ComputeUmeyamaTransform::Pointer filter = ComputeUmeyamaTransform::New();
  if(copyFilterParameters)
  {
    copyFilterParameterInstanceVariables(filter.get());
  }
  return filter;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getCompiledLibraryName() const
{
  return DREAM3DReviewConstants::DREAM3DReviewBaseName;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getBrandingString() const
{
  return "DREAM3DReview";
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getFilterVersion() const
{
  QString version;
  QTextStream vStream(&version);
  vStream << DREAM3DReview::Version::Major() << "." << DREAM3DReview::Version::Minor() << "." << DREAM3DReview::Version::Patch();
  return version;
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getGroupName() const
{
  return DREAM3DReviewConstants::FilterGroups::DREAM3DReviewFilters;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QUuid ComputeUmeyamaTransform::getUuid()
{
  return QUuid("{3192d494-d1ec-5ee7-a345-e9963f02aaab}");
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getSubGroupName() const
{
  return DREAM3DReviewConstants::FilterSubGroups::RegistrationFilters;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
const QString ComputeUmeyamaTransform::getHumanLabel() const
{
  return "Compute Umeyama Transform";
}