//
// Visualization of Gaussians in 2D, using GLUT
//

//
//
// Plan
// - Submit an array of 2D information matrices
// and an array of x, y means
// - Render an array summing an exp of them exp(x)
//

#include <Eigen/Dense>

#include <GL/freeglut.h>

#include <cuda_gl_interop.h>
#include <cuda_profiler_api.h>
#include <cuda_runtime.h>
#include <driver_functions.h>
#include <vector_functions.h>
#include <vector_types.h>

#include <cstdio>
#include <cstdlib>

#include <helper_cuda.h>
#include <helper_math.h>

#include <cuda_gl_interop.h>
#include <helper_gl.h>

template <typename Derived>
using EigStdVector = std::vector<Derived, Eigen::aligned_allocator<Derived>>;

void render_kernel(dim3 gridSize,
                   dim3 blockSize,
                   uint *d_output,
                   uint imageW,
                   uint imageH,
                   float scale,
                   float2 view_center,
                   Eigen::Vector2f *means,
                   Eigen::Matrix2f *information_matrices,
                   int N,
                   float normalization,
                   float tstep);

int iDivUp(int a, int b) {
  return (a % b != 0) ? (a / b + 1) : (a / b);
}

struct State {
  float2 view_center = make_float2(0.0, 0.0);
  float2 prev_view_center = make_float2(0.0, 0.0);
  float2 prev_mouse_pos = make_float2(0.0, 0.0);

  // OpenGL pixel buffer object
  GLuint pbo = 0;

  // OpenGL texture object
  GLuint tex = 0;

  // CUDA Graphics Resource (to transfer PBO)
  struct cudaGraphicsResource *cuda_pbo_resource;

  // Image render dimensions
  uint width = 512;
  uint height = 512;

  float scale = 1.0;

  dim3 blockSize = dim3(16, 16);
  dim3 gridSize = dim3(iDivUp(width, blockSize.x), iDivUp(height, blockSize.y));

  //
  // Rendering paramters
  //
  float time_step = 0.0;
  float direction = 1.0;
  const float time_spacing = 0.005;
  const float min_time = 0.5;

  //
  // Distribution paramters
  //
  EigStdVector<Eigen::Vector2f> means;
  EigStdVector<Eigen::Matrix2f> information_matrices;
  float normalization = 1.0;

  Eigen::Vector2f *d_means;
  Eigen::Matrix2f *d_covariances;

} gstate;

void initGL(int *argc, char **argv) {
  // initialize GLUT callback functions
  glutInit(argc, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  glutInitWindowSize(gstate.width, gstate.height);
  glutCreateWindow("CUDA volume rendering");

  if (!isGLVersionSupported(2, 0) || !areGLExtensionsSupported("GL_ARB_pixel_buffer_object")) {
    printf("Required OpenGL extensions are missing.");
    exit(EXIT_SUCCESS);
  }
}

void initPixelBuffer() {
  if (gstate.pbo) {
    // unregister this buffer object from CUDA C
    checkCudaErrors(cudaGraphicsUnregisterResource(gstate.cuda_pbo_resource));

    // delete old buffer
    glDeleteBuffers(1, &(gstate.pbo));
    glDeleteTextures(1, &(gstate.tex));
  }

  // create pixel buffer object for display
  glGenBuffers(1, &(gstate.pbo));
  glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, gstate.pbo);
  glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, gstate.width * gstate.height * sizeof(GLubyte) * 4, 0, GL_STREAM_DRAW_ARB);
  glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);

  // register this buffer object with CUDA
  checkCudaErrors(
      cudaGraphicsGLRegisterBuffer(&(gstate.cuda_pbo_resource), gstate.pbo, cudaGraphicsMapFlagsWriteDiscard));

  // create texture for display
  glGenTextures(1, &(gstate.tex));
  glBindTexture(GL_TEXTURE_2D, gstate.tex);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gstate.width, gstate.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glBindTexture(GL_TEXTURE_2D, 0);
}

void send_parameters(const EigStdVector<Eigen::Vector2f> &means,
                     const EigStdVector<Eigen::Matrix2f> &information_matrices) {
  const int n = means.size();

  checkCudaErrors(cudaMalloc((void **)&(gstate.d_means), sizeof(Eigen::Vector2f) * n));
  checkCudaErrors(cudaMalloc((void **)&(gstate.d_covariances), sizeof(Eigen::Matrix2f) * n));

  checkCudaErrors(cudaMemcpy(gstate.d_means, means.data(), sizeof(Eigen::Vector2f) * n, cudaMemcpyHostToDevice));
  checkCudaErrors(cudaMemcpy(gstate.d_covariances, information_matrices.data(), sizeof(Eigen::Matrix2f) * n,
                             cudaMemcpyHostToDevice));
}

float compute_eta(const EigStdVector<Eigen::Vector2f> &means,
                  const EigStdVector<Eigen::Matrix2f> &information_matrices) {
  float sum_norm = 0.0;
  for (size_t k = 0; k < means.size(); ++k) {
    const float norm = sqrt((2.0f * 3.14f * information_matrices[k].determinant()));
    sum_norm += norm;
  }
  return sum_norm;
}
void render() {
  uint *d_output;
  checkCudaErrors(cudaGraphicsMapResources(1, &(gstate.cuda_pbo_resource), 0));

  size_t num_bytes;
  checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&d_output, &num_bytes, gstate.cuda_pbo_resource));

  checkCudaErrors(cudaMemset(d_output, 0, gstate.width * gstate.height * 4));

  // Create performance metrics
  cudaEvent_t start, stop;
  cudaEventCreate(&start);
  cudaEventCreate(&stop);

  cudaEventRecord(start);

  if (gstate.time_step >= 1.0) {
    gstate.direction = -1.0;
  } else if (gstate.time_step <= gstate.min_time) {
    gstate.direction = 1.0;
  }
  gstate.time_step += gstate.time_spacing * gstate.direction;
  std::cout << gstate.time_step << std::endl;

  // Render the acutal image
  render_kernel(gstate.gridSize, gstate.blockSize, d_output, gstate.width, gstate.height, gstate.scale,
                gstate.view_center, gstate.d_means, gstate.d_covariances, gstate.information_matrices.size(),
                gstate.normalization, gstate.time_step);

  cudaEventRecord(stop);
  cudaEventSynchronize(stop);

  float milliseconds = 0;
  cudaEventElapsedTime(&milliseconds, start, stop);

  checkCudaErrors(cudaGraphicsUnmapResources(1, &(gstate.cuda_pbo_resource), 0));
}

// display results using OpenGL (called by GLUT)
void display() {
  // use OpenGL to build view matrix
  GLfloat modelView[16];
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
  glPopMatrix();
  render();

  // Display results
  glClear(GL_COLOR_BUFFER_BIT);
  glDisable(GL_DEPTH_TEST);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

  //
  // Prepare render to texture
  //
  glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, gstate.pbo);
  glBindTexture(GL_TEXTURE_2D, gstate.tex);
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gstate.width, gstate.height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
  glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);

  //
  // Draw the texture on the simplest quad
  //
  // Draw textured quad
  glEnable(GL_TEXTURE_2D);
  glBegin(GL_QUADS);

  // This is just a quad that occupies the whole screen
  glTexCoord2f(0, 0);
  glVertex2f(-1, -1);
  glTexCoord2f(1, 0);
  glVertex2f(1, -1);
  glTexCoord2f(1, 1);
  glVertex2f(1, 1);
  glTexCoord2f(0, 1);
  glVertex2f(-1, 1);

  glEnd();

  glDisable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, 0);

  //
  // And it's over
  //

  glutSwapBuffers();
  glutReportErrors();
}

void idle() {
  glutPostRedisplay();
}

void motion(int x, int y) {
  float2 mouse_pos = make_float2(x, y);

  float2 delta_pos = (mouse_pos - gstate.prev_mouse_pos) / (gstate.height / 2.0);

  gstate.view_center = make_float2(-delta_pos.x, delta_pos.y) + gstate.prev_view_center;

  glutPostRedisplay();
}

void mouse(int button, int state, int x, int y) {
  int mods;

  float2 mouse_pos = make_float2(x, y);
  if (state == GLUT_DOWN) {
    gstate.prev_mouse_pos = mouse_pos;
  } else if (state == GLUT_UP) {
    gstate.prev_view_center = gstate.view_center;
  }

  // It's a wheel event
  if ((button == 3) || (button == 4)) {
    if (state == GLUT_UP) return;

    gstate.scale *= (button == 3) ? 0.9 : 1.1;
  }

  std::cout << gstate.view_center.x << " , " << gstate.view_center.y << " :: " << gstate.scale << std::endl;
}

void keyboard(unsigned char key, int x, int y) {
  switch (key) {
    case 27:
      glutDestroyWindow(glutGetWindow());
      return;

    case 'f':
      break;

    case 'G':
      std::cout << "Resetting" << std::endl;
      gstate.view_center = make_float2(0.0, 0.0);
      gstate.scale = 1.0f;
      break;

    case 'q':
    case 'Q':
      std::cout << "Attempting to exit" << std::endl;
      glutDestroyWindow(glutGetWindow());
      return;

    default:
      break;
  }

  glutPostRedisplay();
}

int main(int argc, char **argv) {
#if defined(__linux__)
  setenv("DISPLAY", ":0", 0);
#endif
  std::cout << "Jake cuViewer starting\n" << std::endl;

  //
  // Initialize openGL
  //
  initGL(&argc, argv);

  //
  // Initialize glut
  //
  glutMouseFunc(mouse);
  glutMotionFunc(motion);
  glutDisplayFunc(display);
  glutKeyboardFunc(keyboard);
  glutIdleFunc(idle);

  //
  // Zero the pbo
  //
  initPixelBuffer();

  const int count = 60;
  for (int k = 0; k < count; ++k) {
    const Eigen::Vector2f mean = Eigen::Vector2f::Random() * 5.0f;

    const Eigen::Vector2f diag = 3.0 * (Eigen::Vector2f::Random().array() + 1.25f).matrix();
    const float rand = Eigen::Vector2f::Random()(0);
    const Eigen::Rotation2Df rot = Eigen::Rotation2Df(rand);
    const Eigen::Matrix2f cov = rot * diag.asDiagonal() * rot.inverse();
    gstate.means.emplace_back(mean);
    gstate.information_matrices.emplace_back(cov);
  }
  send_parameters(gstate.means, gstate.information_matrices);
  const float eta = compute_eta(gstate.means, gstate.information_matrices);
  gstate.normalization = static_cast<float>(count) / eta;

  glutMainLoop();
}


#include <armadillo>
#include <iostream>
#include <cmath>

using namespace std;
using namespace arma;

int main()
{
  cx_double temp;
  temp = cos(0.25);
  cout << temp << endl;
}


// copyright (c) 2012-2014 the moorecoin core developers
// distributed under the mit software license, see the accompanying
// file copying or http://www.opensource.org/licenses/mit-license.php.

#include "bloom.h"

#include "primitives/transaction.h"
#include "hash.h"
#include "script/script.h"
#include "script/standard.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define ln2squared 0.4804530139182014246671025263266649717305529515945455
#define ln2 0.6931471805599453094172321214581765680755001343602552

using namespace std;

cbloomfilter::cbloomfilter(unsigned int nelements, double nfprate, unsigned int ntweakin, unsigned char nflagsin) :
    /**
     * the ideal size for a bloom filter with a given number of elements and false positive rate is:
     * - nelements * log(fp rate) / ln(2)^2
     * we ignore filter parameters which will create a bloom filter larger than the protocol limits
     */
    vdata(min((unsigned int)(-1  / ln2squared * nelements * log(nfprate)), max_bloom_filter_size * 8) / 8),
    /**
     * the ideal number of hash functions is filter size * ln(2) / number of elements
     * again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
     * see https://en.wikipedia.org/wiki/bloom_filter for an explanation of these formulas
     */
    isfull(false),
    isempty(false),
    nhashfuncs(min((unsigned int)(vdata.size() * 8 / nelements * ln2), max_hash_funcs)),
    ntweak(ntweakin),
    nflags(nflagsin)
{
}

// private constructor used by crollingbloomfilter
cbloomfilter::cbloomfilter(unsigned int nelements, double nfprate, unsigned int ntweakin) :
    vdata((unsigned int)(-1  / ln2squared * nelements * log(nfprate)) / 8),
    isfull(false),
    isempty(true),
    nhashfuncs((unsigned int)(vdata.size() * 8 / nelements * ln2)),
    ntweak(ntweakin),
    nflags(bloom_update_none)
{
}

inline unsigned int cbloomfilter::hash(unsigned int nhashnum, const std::vector<unsigned char>& vdatatohash) const
{
    // 0xfba4c795 chosen as it guarantees a reasonable bit difference between nhashnum values.
    return murmurhash3(nhashnum * 0xfba4c795 + ntweak, vdatatohash) % (vdata.size() * 8);
}

void cbloomfilter::insert(const vector<unsigned char>& vkey)
{
    if (isfull)
        return;
    for (unsigned int i = 0; i < nhashfuncs; i++)
    {
        unsigned int nindex = hash(i, vkey);
        // sets bit nindex of vdata
        vdata[nindex >> 3] |= (1 << (7 & nindex));
    }
    isempty = false;
}

void cbloomfilter::insert(const coutpoint& outpoint)
{
    cdatastream stream(ser_network, protocol_version);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void cbloomfilter::insert(const uint256& hash)
{
    vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool cbloomfilter::contains(const vector<unsigned char>& vkey) const
{
    if (isfull)
        return true;
    if (isempty)
        return false;
    for (unsigned int i = 0; i < nhashfuncs; i++)
    {
        unsigned int nindex = hash(i, vkey);
        // checks bit nindex of vdata
        if (!(vdata[nindex >> 3] & (1 << (7 & nindex))))
            return false;
    }
    return true;
}

bool cbloomfilter::contains(const coutpoint& outpoint) const
{
    cdatastream stream(ser_network, protocol_version);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool cbloomfilter::contains(const uint256& hash) const
{
    vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void cbloomfilter::clear()
{
    vdata.assign(vdata.size(),0);
    isfull = false;
    isempty = true;
}

bool cbloomfilter::iswithinsizeconstraints() const
{
    return vdata.size() <= max_bloom_filter_size && nhashfuncs <= max_hash_funcs;
}

bool cbloomfilter::isrelevantandupdate(const ctransaction& tx)
{
    bool ffound = false;
    // match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isfull)
        return true;
    if (isempty)
        return false;
    const uint256& hash = tx.gethash();
    if (contains(hash))
        ffound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++)
    {
        const ctxout& txout = tx.vout[i];
        // match if the filter contains any arbitrary script data element in any scriptpubkey in tx
        // if this matches, also add the specific output that was matched.
        // this means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        cscript::const_iterator pc = txout.scriptpubkey.begin();
        vector<unsigned char> data;
        while (pc < txout.scriptpubkey.end())
        {
            opcodetype opcode;
            if (!txout.scriptpubkey.getop(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
            {
                ffound = true;
                if ((nflags & bloom_update_mask) == bloom_update_all)
                    insert(coutpoint(hash, i));
                else if ((nflags & bloom_update_mask) == bloom_update_p2pubkey_only)
                {
                    txnouttype type;
                    vector<vector<unsigned char> > vsolutions;
                    if (solver(txout.scriptpubkey, type, vsolutions) &&
                            (type == tx_pubkey || type == tx_multisig))
                        insert(coutpoint(hash, i));
                }
                break;
            }
        }
    }

    if (ffound)
        return true;

    boost_foreach(const ctxin& txin, tx.vin)
    {
        // match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // match if the filter contains any arbitrary script data element in any scriptsig in tx
        cscript::const_iterator pc = txin.scriptsig.begin();
        vector<unsigned char> data;
        while (pc < txin.scriptsig.end())
        {
            opcodetype opcode;
            if (!txin.scriptsig.getop(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void cbloomfilter::updateemptyfull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vdata.size(); i++)
    {
        full &= vdata[i] == 0xff;
        empty &= vdata[i] == 0;
    }
    isfull = full;
    isempty = empty;
}

crollingbloomfilter::crollingbloomfilter(unsigned int nelements, double fprate, unsigned int ntweak) :
    b1(nelements * 2, fprate, ntweak), b2(nelements * 2, fprate, ntweak)
{
    // implemented using two bloom filters of 2 * nelements each.
    // we fill them up, and clear them, staggered, every nelements
    // inserted, so at least one always contains the last nelements
    // inserted.
    nbloomsize = nelements * 2;
    ninsertions = 0;
}

void crollingbloomfilter::insert(const std::vector<unsigned char>& vkey)
{
    if (ninsertions == 0) {
        b1.clear();
    } else if (ninsertions == nbloomsize / 2) {
        b2.clear();
    }
    b1.insert(vkey);
    b2.insert(vkey);
    if (++ninsertions == nbloomsize) {
        ninsertions = 0;
    }
}

bool crollingbloomfilter::contains(const std::vector<unsigned char>& vkey) const
{
    if (ninsertions < nbloomsize / 2) {
        return b2.contains(vkey);
    }
    return b1.contains(vkey);
}

void crollingbloomfilter::clear()
{
    b1.clear();
    b2.clear();
    ninsertions = 0;
}


#include <iostream>
#include <fstream>
#include <Eigen/Eigen>
//include the bie header files
#include "material.hh"
#include "precomputed_kernel.hh"
#include "infinite_boundary.hh"
//include the fem header files
#include "mesh_Generated_multi_faults.hpp"
#include "bcdof.hpp"
#include "bcdof_ptr.hpp"
#include "cal_ke.hpp"
#include "cal_fe_global_plastic.hpp"
#include "cal_fe_global_const_ke.hpp"
#include "mapglobal.hpp"
#include "Slip_Weakening_reg.hpp"
#include "Slip_Weakening.hpp"
#include "cal_slip_sliprate.hpp"
#include "time_advance.hpp"
#include "BIE_correct.hpp"
//#include "igl/list_to_matrix.h"
#include "Getplastic.hpp"
#include "cal_Bmat.hpp"
#include "Slip_Weakening_lumpM_with_buffer.hpp"

//#include <omp.h>
using namespace Eigen;
using namespace std;

typedef Eigen::Matrix<int, -1, -1,RowMajor> MatrixXi_rm;

void read_matrix(std::string fileName, Eigen::MatrixXd &outputMat);

double time_fem=0;
double time_bie;
int main() {
    time_fem = 0.0;
    // Domain Size
    double x_min = -36e3;
    double x_max = 36e3;
    double y_min = -1.0e3;
    double y_max = 1.0e3;
    int dim = 2.0;
    double dx = 12.5;
    double dy = 12.5;
    int nx_el = (x_max-x_min)/dx;
    int ny = (y_max-y_min)/dy;
    MatrixXd Node = MatrixXd::Zero((nx_el+1)*(ny+1),2);
    MatrixXi_rm Element(nx_el*ny,4); Element.setZero();
    ArrayXi BIE_top_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    ArrayXi BIE_bot_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    int num_faults = 1;
    // Mesh
    // Position of the fault : y_pos, x_left, x_right
    MatrixXd fault_pos(num_faults,3);
    fault_pos << 0.0,  x_min ,x_max;

    //    MatrixXd fault_pos;
    //    read_matrix("fault_pos.txt", fault_pos);
    //    int num_faults = fault_pos.rows();
    cout<<"test_mat="<<"\n"<<fault_pos<<"\n"<<"rows="<<fault_pos.rows()<<"\n";
    std::vector<std::vector<int>> fault_nodes(num_faults*2);
    mesh_Generated_multi_faults(x_min,x_max,y_min,y_max,dx,dy,nx_el,ny,Node, Element,BIE_top_surf_nodes, BIE_bot_surf_nodes,fault_pos, fault_nodes);

    std::ofstream Element_output("results/Element.txt");
    std::ofstream Node_output("results/Node.txt");

    std::ofstream u_n_test("results/u_n_test.txt");
    std::ofstream v_n_test("results/v_n_test.txt");
    std::ofstream u_n_test_2("results/u_n_test_2.txt");
    std::ofstream v_n_test_2("results/v_n_test_2.txt");
    Node_output<<Node;
    Element_output<<Element;

    int n_nodes = Node.rows();
    int n_el = Element.rows();
    int Ndofn = 2;
    int Nnel = Element.cols();
    // Material
    double density = 2670.0;
    double v_s =3.464e3;
    double v_p = 6.0e3;
    double G= pow(v_s,2)*density;
    double Lambda = pow(v_p,2)*density-2.0*G;
    double E  = G*(3.0*Lambda+2.0*G)/(Lambda+G);
    double nu = Lambda/(2.0*(Lambda+G));
    // Time
    double alpha = 0.4;
    double dt = alpha*dx/v_p;
    //dt = 5e-4;
    dt = 4e-4;
    //dt = 0.0025;
    //dt = 0.000625;
    // Reyleigh Damping
    double beta =0.1;
    double q = beta*dt;
    double time_run = 0.5;
    int numt = time_run/dt;
    std::ofstream time_output("results/time.txt");
    time_output<<time_run<<"\n";
    time_output<<dt<<"\n";
    time_output<<numt<<std::endl;
    // numt = 100;
    //numt = 200;
    //numt = 3;
    // numt =8;
    VectorXd time = dt*VectorXd::LinSpaced(numt,1,numt);
    // Intialization
    // disp velocity current and next time step (new)
    VectorXd u_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd u_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd a_n = VectorXd::Zero(n_nodes*Ndofn,1);
    // Vector containing number of elements on each faults (nx)

    VectorXd F_ext_global = VectorXd::Zero(n_nodes*Ndofn,1);

    // Get the index degree of freedome for each element
    VectorXi index_el = VectorXi::Zero(Ndofn*Nnel,1);
    MatrixXi index_store = MatrixXi::Zero(Nnel*Ndofn,n_el);
    for (int i=0;i<n_el;i++)
    {
        bcdof(Element.row(i),dim,index_el);
        index_store.col(i) = index_el;
    }
    VectorXi BIE_top_surf_index = VectorXi::Zero(Ndofn*(BIE_top_surf_nodes.size()),1);
    VectorXi BIE_bot_surf_index = VectorXi::Zero(Ndofn*(BIE_bot_surf_nodes.size()),1);

    bcdof(BIE_top_surf_nodes,dim,BIE_top_surf_index);
    bcdof(BIE_bot_surf_nodes,dim,BIE_bot_surf_index);
    // Calculating the Global Mass Vector (lumped mass)
    // Element mass
    double M=density*dx*dy*1.0;
    VectorXd M_el_vec = M/4*VectorXd::Ones(Nnel*Ndofn,1);
    VectorXd M_global_vec=VectorXd::Zero(n_nodes*Ndofn,1);
    for (int i=0 ; i<n_el;i++)
    {
        index_el = index_store.col(i);
        mapglobal(index_el,M_global_vec,M_el_vec);
    }
    // Element matrix
    MatrixXd ke = MatrixXd::Zero(8,8);
    MatrixXd coord = MatrixXd::Zero(4,2);
    VectorXi Element_0= Element.row(0);
    coord.row(0) = Node.row(Element_0(0));
    coord.row(1) = Node.row(Element_0(1));
    coord.row(2) = Node.row(Element_0(2));
    coord.row(3) = Node.row(Element_0(3));

    std::cout<<coord<<std::endl;

    cal_ke (coord,E,nu,ke);

    std::cout<<ke<<std::endl;
    double detJ = 0.0;
    std::vector<Eigen::MatrixXd> B_mat(4);
    cal_Bmat(coord, E, nu, B_mat, detJ);
    // BIE part initiation
    // Setting up the material property for the BIE code
    Material BIE_top_mat = Material(E,nu,density);
    Material BIE_bot_mat = Material(E,nu,density);
    double length = x_max-x_min;
    // infinte bc BIE call infinite_boundary.cc
    PrecomputedKernel h11("kernels/nu_.25_h11.dat");
    PrecomputedKernel h12("kernels/nu_.25_k12.dat");
    PrecomputedKernel h22("kernels/nu_.25_h22.dat");
    InfiniteBoundary BIE_inf_top(length,nx_el+1,1.0,&BIE_top_mat,&h11,&h12,&h22);
    InfiniteBoundary BIE_inf_bot(length,nx_el+1,-1.0,&BIE_bot_mat,&h11,&h12,&h22);
    // BIE setting time step
    BIE_inf_top.setTimeStep(dt);
    BIE_inf_bot.setTimeStep(dt);
    // BIE initialization
    BIE_inf_top.init();
    BIE_inf_bot.init();
    printf("ready to start\n");
    // Output
    ofstream file;
    //
    file.open("results/u_n.bin",ios::binary);
    file.close();
    file.open("results/v_n.bin",ios::binary);
    file.close();

    // output BIE boundary u_n v_n
//    VectorXd BIE_top_u_n = VectorXd::Zero(2*(nx_el+1),1);
//    VectorXd BIE_bot_u_n = VectorXd::Zero(2*(nx_el+1),1);
//    VectorXd BIE_top_v_n = VectorXd::Zero(2*(nx_el+1),1);
//    VectorXd BIE_bot_v_n = VectorXd::Zero(2*(nx_el+1),1);
    VectorXd BIE_top_u_n ;
    // Add output files BIE_top BIE_bot u_n v_n
    streampos size;
    ifstream binaryIo("results/BIE_bot_u_n.bin",ios::in|ios::binary|ios::ate);
    size = binaryIo.tellg();
    char * memblock;
    cout<<"size = "<<size <<"\n";
    memblock = new char [size];
    binaryIo.seekg(0,ios::beg);
    binaryIo.read(memblock,size);
    binaryIo.close();
    cout<<"the entire file content is in memory \n";
    double *double_u_n = (double*)memblock;  // reinterpret as dobule

    streampos sizev;
    ifstream binaryIov("results/BIE_bot_v_n.bin",ios::in|ios::binary|ios::ate);
    sizev = binaryIov.tellg();
    char * memblockv;
    cout<<"size = "<<sizev <<"\n";
    memblockv = new char [sizev];
    binaryIov.seekg(0,ios::beg);
    binaryIov.read(memblockv,sizev);
    binaryIov.close();
    cout<<"the entire file content is in memory \n";
    double *double_v_n = (double*)memblockv;  // reinterpret as dobule

    VectorXd u_n_local = VectorXd::Zero(2*(nx_el+1),1);
    VectorXd v_n_local = VectorXd::Zero(2*(nx_el+1),1);

    // Main time loop
    for (int j=0;j<numt;j++)
    {
       // mapglobal(BIE_top_surf_index, u_n, u_n_local);
       // mapglobal(BIE_top_surf_index, v_n, v_n_local);
        // Compute the global internal force
        VectorXd fe_global= VectorXd::Zero(n_nodes*Ndofn,1);
          cal_fe_global_const_ke(n_nodes, n_el, index_store, q, u_n, v_n, Ndofn, ke, fe_global);
        // Friction subroutine
        VectorXd F_total = F_ext_global-fe_global;
        // Central Difference Time integration
        time_advance(u_n, v_n, F_total, M_global_vec, dt);

        for (int i=0; i<v_n_local.size();i++)
        {
            double u_n_temp = double_u_n[j*(2*(nx_el+1))+i];
            double v_n_temp = double_v_n[j*(2*(nx_el+1))+i];
            u_n_local(i) = u_n_temp;
            v_n_local(i) = v_n_temp;
        }

       u_n_test<<u_n_local<<std::endl;
       v_n_test<<v_n_local<<std::endl;

        mapglobal(BIE_top_surf_index, u_n, u_n_local);
        mapglobal(BIE_top_surf_index, v_n, v_n_local);

        VectorXd u_n_local_2 = VectorXd::Zero(2*(nx_el+1),1);
        VectorXd v_n_local_2 = VectorXd::Zero(2*(nx_el+1),1);

        maplocal(BIE_top_surf_index, u_n, u_n_local_2);
      //  maplocal(BIE_top_surf_index, v_n, v_n_local_2);

        u_n_test_2<<u_n_local_2<<std::endl;
        v_n_test_2<<v_n_local_2<<std::endl;

        // Get the slip and slip rate
        if (j%10==1)
        {
            file.open("results/u_n.bin",ios::binary | ios::app);
            file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
            file.close();
            file.open("results/v_n.bin",ios::binary | ios::app);
            file.write((char*)(v_n.data()),v_n.size()*sizeof(double));
            file.close();
        }
        printf("Simulation time = %f\n",time(j));
    }
    return 0;
}


#include <armadillo>
#include <cstring>

#include "gyro_ideal.hh"

GyroIdeal::GyroIdeal() { snprintf(name, sizeof(name), "Ideal Gyro Sensor"); }

void GyroIdeal::init(LaunchVehicle *VehicleIn)
{
    VehicleIn->Sensor->WBICB = VehicleIn->DM->WBIB;
    VehicleIn->Sensor->EWBIB.zeros();
    return;
}

void GyroIdeal::algorithm(LaunchVehicle *VehicleIn)
{
    VehicleIn->Sensor->WBICB = VehicleIn->DM->WBIB;
    return;
}


//
// Created by Hao Wu on 11/11/16.
//

#include <iostream>
#include <boost/numeric/odeint.hpp>
#include "ROWPlus/ROWPlus.h"
#include "RxnFunctor.h"

/* Header files for CVODE */
#include <nvector/nvector_serial.h>  /* serial N_Vector types, fcts., macros */
#include <cvode/cvode.h>             /* prototypes for CVODE fcts., consts. */
#include <cvode/cvode_dense.h>       /* prototype for CVDense */
#include <sundials/sundials_dense.h> /* definitions DlsMat DENSE_ELEM */
#include <sundials/sundials_types.h> /* definition of type realtype */

using namespace Eigen;
using namespace ROWPlus;
using namespace boost::numeric::odeint;
using namespace Cantera;
using namespace std;

template<typename vec>
static void initX(vec& x, IdealGasMix& gas) {
  x.resize(gas.nSpecies()+1);
  gas.setState_TPX(1500.0, OneAtm, "CH4:0.5, O2:1.0, N2:3.76, AR:1e-6");
  x[0] = gas.temperature();
  gas.getMassFractions(x.data()+1);
}

static void initXCvode(N_Vector x, IdealGasMix& gas) {
  gas.setState_TPX(1500.0, OneAtm, "CH4:0.5, O2:1.0, N2:3.76, AR:1e-6");
  NV_Ith_S(x,0) = gas.temperature();
  gas.getMassFractions(NV_DATA_S(x) + 1);
}

static int check_flag(void *flagvalue, const string& funcname, int opt)
{
  int *errflag;

  /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
  if (opt == 0 && flagvalue == NULL) {
    fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
            funcname.c_str());
    return(1); }

    /* Check if flag < 0 */
  else if (opt == 1) {
    errflag = (int *) flagvalue;
    if (*errflag < 0) {
      fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n",
              funcname.c_str(), *errflag);
      return(1); }}

    /* Check if function returned NULL pointer - no memory allocated */
  else if (opt == 2 && flagvalue == NULL) {
    fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
            funcname.c_str());
    return(1); }

  return(0);
}

extern "C"
{
static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) {
  double *ydata = NV_DATA_S(y);
  double *dydata = NV_DATA_S(ydot);
  RxnFunctor *fun = (RxnFunctor *) user_data;
  return fun->f(t, Map<VectorXd>(ydata, fun->inputs()),
                Map<VectorXd>(dydata, fun->inputs()));
}
}

int main(const int argc, const char *argv[]) {

  double TIME1 = strtod(argv[1], NULL);
  double DTIME = 1e-4;

  IdealGasMix gas("gri30.xml");
  RxnFunctor fun(gas);
  fun.checkBound(false);
  std::cout << "Cantera 0D" << std::endl;

  /* Create serial vector of length NEQ for I.C.*/
  size_t NEQ = fun.inputs();
  realtype cvode_t1, cvode_hlast, cvode_t;
  N_Vector cvode_y = N_VNew_Serial(NEQ);
  void *cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON);
  /* Initialize y */
  initXCvode(cvode_y, gas);
  /* Call CVodeCreate to create the solver memory and specify the
   * Backward Differentiation Formula and the use of a Newton iteration */
  int flag = CVodeInit(cvode_mem, f, 0.0, cvode_y);
  flag = CVodeSStolerances(cvode_mem, 1e-12, 1e-12);
  flag = CVodeSetMaxNumSteps(cvode_mem, 10000);
  flag = CVodeSetUserData(cvode_mem, &fun);
  flag = CVDense(cvode_mem, NEQ);

  // Generate reference solution
  CVode(cvode_mem, cvode_t1 = TIME1, cvode_y, &cvode_t, CV_NORMAL);
  assert(cvode_t == cvode_t1);
  cout << "T_end_REF = " << NV_Ith_S(cvode_y,0) << endl;
  VectorXd y_ref = Map<const VectorXd>(NV_DATA_S(cvode_y), fun.inputs());

  // CVODE solution
  initXCvode(cvode_y, gas);
  flag = CVodeReInit(cvode_mem, 0.0, cvode_y);
  flag = CVodeSStolerances(cvode_mem, 1e-8, 1e-10);
  CVode(cvode_mem, cvode_t1 = TIME1, cvode_y, &cvode_t, CV_NORMAL);
  assert(cvode_t == cvode_t1);
  cout << "CVODE : T_end = " << NV_Ith_S(cvode_y,0) << " ";
  VectorXd y_cvode = Map<const VectorXd>(NV_DATA_S(cvode_y), fun.inputs());
  y_cvode -= y_ref;
  y_cvode.array() /= (y_ref.array() + 1e-6);
  cout << "ERR = " << y_cvode.stableNorm() / sqrt(y_cvode.size()) << endl;

  VectorXd rowplus_y;
  double rowplus_hlast, rowplus_t;
  rosenbrock_krylov4<RxnFunctor, double> stepper_rok4e(4);
  initX(rowplus_y, gas);
  stepper_rok4e.makeAdaptiveStepper(&fun, 1e-5, 1e-8);
  ROWPlusSolverSpace::Status ret = stepper_rok4e.step(rowplus_y, 0.0, TIME1,
                                                      1e-9);
  if (ret != ROWPlusSolverSpace::ComputeSucessful) {
    cout << "STAT = " << ret << endl;
    return 1;
  }
  cout << "ROK4E: T_end = " << rowplus_y[0] << " ";
  rowplus_y -= y_ref;
  rowplus_y.array() /= (y_ref.array() + 1e-6);
  cout << "ERR = " << rowplus_y.stableNorm() / sqrt(rowplus_y.size()) << endl;
  cout << "NFEVAL = " << stepper_rok4e.getSolver()->getStats().nfeval << " "
       << "NSTEPS = " << stepper_rok4e.getSolver()->getStats().nsteps << " "
       << "NSTEPSR = " << stepper_rok4e.getSolver()->getStats().nstepsr << endl;
  /* Free y and abstol vectors */
  N_VDestroy_Serial(cvode_y);
  /* Free integrator memory */
  CVodeFree(&cvode_mem);
  return 0;
};


//
//  ContextParameterProvider.h
//  ConsensusCoreXCode
//
//  Created by Nigel Delaney on 2/27/15.
//  Copyright (c) 2015 Pacific Biosciences. All rights reserved.
//

#pragma once

#include <stdio.h>
#include <unordered_map>
#include <boost/numeric/ublas/matrix.hpp>
#include <cmath>
#include <vector>

#include <ConsensusCore/Arrow/TransitionParameters.hpp>

namespace ConsensusCore {
namespace Arrow {

    template<class T>
    using Matrix = std::vector<std::vector<T> >;

    /**
     A data structure to store SNR values.
     */
    class SNR {
    public:
        const double A, C, G, T;
        SNR(double A, double C, double G, double T);

        // support arbitrary loading from vector
        template<typename RealType>
        SNR(const std::vector<RealType>& snrs)
            : A{snrs[0]}
            , C{snrs[1]}
            , G{snrs[2]}
            , T{snrs[3]}
        { }
    };

    /**

     This class is designed to provide the relative transition probabilities for
     a given dinculeotide context at a given SNR value.
     @param <#parameter#>
     @returns <#retval#>
     @exception <#throws#>
     */
    class ContextParameterProvider {
        public:
        static TransitionParameters GetTransitionParameters(const std::string& context, const SNR& snrs);
    };
}
}


//***********************************************************
// 07/07/2021	1.0.0	Rémi Saint-Amant   Creation
//***********************************************************
#include "LaricobiusOsakensisModel.h"
#include "ModelBase/EntryPoint.h"
#include "Basic\DegreeDays.h"
#include <boost/math/distributions/weibull.hpp>
#include <boost/math/distributions/beta.hpp>
#include <boost/math/distributions/Rayleigh.hpp>
#include <boost/math/distributions/logistic.hpp>
#include <boost/math/distributions/exponential.hpp>
#include <boost/math/distributions/non_central_f.hpp>
#include <boost/math/distributions/extreme_value.hpp>
#include <boost/math/distributions/fisher_f.hpp>

using namespace WBSF::HOURLY_DATA;
using namespace WBSF::LOF;
using namespace std;

static const bool BEGIN_DECEMBER = true;
static const size_t FIRST_Y = BEGIN_DECEMBER ? 1 : 0;

namespace WBSF
{
	static const CDegreeDays::TDailyMethod DD_METHOD = CDegreeDays::MODIFIED_ALLEN_WAVE;
	enum { ACTUAL_CDD, DATE_DD717, DIFF_DAY, NB_OUTPUTS };

	//this line link this model with the EntryPoint of the DLL
	static const bool bRegistred =
		CModelFactory::RegisterModel(CLaricobiusOsakensisModel::CreateObject);

	CLaricobiusOsakensisModel::CLaricobiusOsakensisModel()
	{
		//NB_INPUT_PARAMETER is used to determine if the dll
		//uses the same number of parameters than the model interface
		NB_INPUT_PARAMETER = -1;
		VERSION = "1.0.0 (2021)";

		m_bApplyAttrition = false;
		m_bCumul = false;

		for (size_t p = 0; p < NB_CEC_PARAMS; p++)
			m_CEC[p] = CLaricobiusOsakensisEquations::CEC[p];

		for (size_t p = 0; p < NB_ADE_PARAMS; p++)
			m_ADE[p] = CLaricobiusOsakensisEquations::ADE[p];

		for (size_t p = 0; p < NB_EAS_PARAMS; p++)
			m_EAS[p] = CLaricobiusOsakensisEquations::EAS[p];
	}

	CLaricobiusOsakensisModel::~CLaricobiusOsakensisModel()
	{
	}

	//this method is call to load your parameter in your variable
	ERMsg CLaricobiusOsakensisModel::ProcessParameters(const CParameterVector& parameters)
	{
		ERMsg msg;

		size_t c = 0;

		m_bApplyAttrition = parameters[c++].GetBool();
		m_bCumul = parameters[c++].GetBool();

		if (parameters.size() == 2 + NB_CEC_PARAMS + NB_ADE_PARAMS + NB_EAS_PARAMS)
		{
			for (size_t p = 0; p < NB_CEC_PARAMS; p++)
				m_CEC[p] = parameters[c++].GetFloat();

			for (size_t p = 0; p < NB_ADE_PARAMS; p++)
				m_ADE[p] = parameters[c++].GetFloat();

			for (size_t p = 0; p < NB_EAS_PARAMS; p++)
				m_EAS[p] = parameters[c++].GetFloat();

		}

		return msg;
	}

	//This method is called to compute the solution
	ERMsg CLaricobiusOsakensisModel::OnExecuteDaily()
	{
		ERMsg msg;

		if (m_weather.GetNbYears() < 2)
		{
			msg.ajoute("Laricobius Osakensis model need at least 2 years of data");
			return msg;
		}

		if (!m_weather.IsHourly())
			m_weather.ComputeHourlyVariables();

		//This is where the model is actually executed
		CTPeriod p = m_weather.GetEntireTPeriod(CTM(CTM::DAILY));
		m_output.Init(p, NB_STATS, 0);

		//we simulate 2 years at a time.
		//we also manager the possibility to have only one year
		for (size_t y = FIRST_Y; y < m_weather.size(); y++)
		{
			ExecuteDaily(m_weather[y].GetTRef().GetYear(), m_weather, m_output);
		}

		return msg;
	}

	void CLaricobiusOsakensisModel::ExecuteDaily(int year, const CWeatherYears& weather, CModelStatVector& output)
	{
		//Create stand
		CLNFStand stand(this, m_CEC[Τᴴ¹], m_CEC[Τᴴ²]);

		stand.m_bApplyAttrition = m_bApplyAttrition;
		//Set parameters to equation
		for (size_t p = 0; p < NB_CEC_PARAMS; p++)
			stand.m_equations.m_CEC[p] = m_CEC[p];

		for (size_t p = 0; p < NB_ADE_PARAMS; p++)
			stand.m_equations.m_ADE[p] = m_ADE[p];

		for (size_t p = 0; p < NB_EAS_PARAMS; p++)
			stand.m_equations.m_EAS[p] = m_EAS[p];

		stand.init(year, weather);

		//Create host
		CLNFHostPtr pHost(new CLNFHost(&stand));

		pHost->m_nbMinObjects = 10;
		pHost->m_nbMaxObjects = 1000;

		CTRef begin = BEGIN_DECEMBER ? CTRef(year-1, SEPTEMBER, DAY_01) : CTRef(year, JANUARY, DAY_01);
		CTRef end = BEGIN_DECEMBER ? CTRef(year, DECEMBER, DAY_31) : CTRef(year, DECEMBER, DAY_31);
		pHost->Initialize<CLaricobiusOsakensis>(CInitialPopulation(begin, 0, 400, 100, -1));

		//add host to stand
		stand.m_host.push_front(pHost);

		//CTPeriod p = weather[year].GetEntireTPeriod(CTM(CTM::DAILY));
		CTPeriod p(begin, end);
		if(output.empty())
			output.Init(p, NB_STATS, 0);

		//if have other year extend period to February
		ASSERT(weather[year].HavePrevious());
		//if (BEGIN_DECEMBER)
		//{
		//	p.Begin() = CTRef(year - 1, JULY, DAY_01);
		//}
		//else
		//{
		//if have other year extend period to February
		if (weather[year].HaveNext())
			p.End() = CTRef(year + 1, JUNE, DAY_30);

		//}
		//if have previous year extend period to DECEMBER
		//if (weather[year].HavePrevious())
			//p.Begin() = CTRef(year - 1, DECEMBER, DAY_01);

		for (CTRef d = p.Begin(); d <= p.End(); d++)
		{
			stand.Live(weather.GetDay(d));
			if (output.IsInside(d))
				stand.GetStat(d, output[d]);

			stand.AdjustPopulation();
			HxGridTestConnection();
		}

		if (m_bCumul)
		{
			//cumulative result
			for (size_t s = S_EGG; s < S_ACTIVE_ADULT; s++)
			{
				//CTPeriod p = weather[year].GetEntireTPeriod(CTM(CTM::DAILY));

				CStatistic stat = output.GetStat(s, p);
				if (stat.IsInit() && stat[SUM] > 0)
				{
					output[0][s] = output[0][s] * 100 / stat[SUM];//when first day is not 0
					for (CTRef d = p.Begin() + 1; d <= p.End(); d++)
					{
						output[d][s] = output[d - 1][s] + output[d][s] * 100 / stat[SUM];
						_ASSERTE(!_isnan(output[d][s]));
					}
				}
			}

			CStatistic stat = output.GetStat(S_LARVAE, p);
			if (stat.IsInit() && stat[SUM] > 0)
			{
				output[0][S_LARVAE] = output[0][S_LARVAE] * 100 / stat[SUM];//when first day is not 0
				for (CTRef d = p.Begin() + 1; d <= p.End(); d++)
				{
					output[d][S_LARVAE] = output[d - 1][S_LARVAE] + output[d][S_LARVAE] * 100 / stat[SUM];
					_ASSERTE(!_isnan(output[d][S_LARVAE]));
				}
			}
		}
	}

	void CLaricobiusOsakensisModel::AddDailyResult(const StringVector& header, const StringVector& data)
	{
		ASSERT(data.size() == 4);

		CSAResult obs;

		CStatistic egg_creation_date;

		obs.m_ref.FromFormatedString(data[1]);
		obs.m_obs.resize(NB_INPUTS);
		for (size_t i = 0; i < NB_INPUTS; i++)
		{
			obs.m_obs[i] = stod(data[i + 2]);

			if (i == 0 && obs.m_obs[i] > -999)
				m_egg_creation_date[data[0] + "_" + to_string(obs.m_ref.GetYear())] += obs.m_ref.GetJDay();

			if (obs.m_obs[i] > -999)
			{
				m_nb_days[i] += obs.m_ref.GetJDay();
				m_years[i].insert(obs.m_ref.GetYear());
			}
		}

		m_SAResult.push_back(obs);

	}

	double GetSimX(size_t s, CTRef TRefO, double obs, const CModelStatVector& output)
	{
		double x = -999;

		if (obs > -999)
		{
			//if (obs > 0.01 && obs < 99.99)
			if (obs >= 100)
				obs = 99.99;//to avoid some problem of truncation

			long index = output.GetFirstIndex(s, ">=", obs, 1, CTPeriod(TRefO.GetYear()-1, DECEMBER, DAY_01, TRefO.GetYear(), DECEMBER, DAY_31));
			if (index >= 1)
			{
				double obsX1 = output.GetFirstTRef().GetJDay() + index;
				double obsX2 = output.GetFirstTRef().GetJDay() + index + 1;

				double obsY1 = output[index][s];
				double obsY2 = output[index + 1][s];
				if (obsY2 != obsY1)
				{
					double slope = (obsX2 - obsX1) / (obsY2 - obsY1);
					double obsX = obsX1 + (obs - obsY1)*slope;
					ASSERT(!_isnan(obsX) && _finite(obsX));

					x = obsX;
				}
			}
		}

		return x;
	}

	bool CLaricobiusOsakensisModel::IsParamValid()const
	{
		if (m_CEC[Τᴴ¹] >= m_CEC[Τᴴ²])
			return false;

		bool bValid = true;

		return bValid;
	}

	static const int ROUND_VAL = 4;

	//void CLaricobiusOsakensisModel::CalibrateCumulativeEggCreation(CStatisticXY& stat)
	//{
	//	size_t EVALUATE_STAGE = 0;

	//	if (m_CEC[Τᴴ¹] >= m_CEC[Τᴴ²])
	//		return;

	//	if (m_SAResult.empty())
	//		return;

	//	if (!m_weather.IsHourly())
	//		m_weather.ComputeHourlyVariables();

	//	for (size_t y = 0; y < m_weather.GetNbYears(); y++)
	//	{
	//		int year = m_weather[y].GetTRef().GetYear();
	//		if (m_years[EVALUATE_STAGE].find(year) == m_years[EVALUATE_STAGE].end())
	//			continue;

	//		double sumDD = 0;
	//		vector<double> CDD;
	//		CTPeriod p;

	//		p = m_weather[year].GetEntireTPeriod(CTM(CTM::DAILY));
	//		CDD.resize(p.size(), 0);

	//		CDegreeDays DDModel(CDegreeDays::MODIFIED_ALLEN_WAVE, m_CEC[Τᴴ¹], m_CEC[Τᴴ²]);

	//		for (CTRef TRef = p.Begin(); TRef <= p.End(); TRef++)
	//		{
	//			const CWeatherDay& wday = m_weather.GetDay(TRef);
	//			size_t ii = TRef - p.Begin();
	//			sumDD += DDModel.GetDD(wday);
	//			CDD[ii] = sumDD;
	//		}

	//		for (size_t i = 0; i < m_SAResult.size(); i++)
	//		{
	//			size_t ii = m_SAResult[i].m_ref - p.Begin();
	//			if (m_SAResult[i].m_ref.GetYear() == year && ii < CDD.size())
	//			{
	//				double obs_y = m_SAResult[i].m_obs[EVALUATE_STAGE];

	//				if (obs_y > -999)
	//				{

	//					double sim_y = 0;

	//					/*if (EVALUATE_STAGE == I_EMERGED_ADULT)
	//					{
	//						boost::math::weibull_distribution<double> emerged_dist(m_EAS[ʎ], m_EAS[к]);
	//						sim_y = Round(cdf(emerged_dist, CDD[ii]) * 100, ROUND_VAL);
	//					}
	//					else
	//					{*/
	//					boost::math::logistic_distribution<double> create_dist(m_CEC[μ], m_CEC[ѕ]);
	//					sim_y = Round(cdf(create_dist, CDD[ii]) * 100, ROUND_VAL);
	//					//}

	//					if (sim_y < 0.1)
	//						sim_y = 0;
	//					if (sim_y > 99.9)
	//						sim_y = 100;

	//					stat.Add(obs_y, sim_y);
	//				}
	//			}
	//		}
	//	}//for all years

	//	return;
	//}

	void CLaricobiusOsakensisModel::CalibrateCumulativeEggCreation(CStatisticXY& stat)
	{

		if (m_CEC[Τᴴ¹] >= m_CEC[Τᴴ²])
			return;

		if (m_SAResult.empty())
			return;

		if (!m_weather.IsHourly())
			m_weather.ComputeHourlyVariables();

		for (size_t y = FIRST_Y; y < m_weather.GetNbYears(); y++)
		{
			int year = m_weather[y].GetTRef().GetYear();
			//if (m_years[EVALUATE_STAGE].find(year) == m_years[EVALUATE_STAGE].end())
				//continue;

			double sumDD = 0;
			vector<double> CDD;
			CTPeriod p;

			p = m_weather[year].GetEntireTPeriod(CTM(CTM::DAILY));
			if(BEGIN_DECEMBER)
				p.Begin() = CTRef(year - 1, DECEMBER, DAY_01);

			CDD.resize(p.size(), 0);

			CDegreeDays DDModel(CDegreeDays::MODIFIED_ALLEN_WAVE, m_CEC[Τᴴ¹], m_CEC[Τᴴ²]);

			for (CTRef TRef = p.Begin(); TRef <= p.End(); TRef++)
			{
				const CWeatherDay& wday = m_weather.GetDay(TRef);
				size_t ii = TRef - p.Begin();
				sumDD += DDModel.GetDD(wday);
				CDD[ii] = sumDD;
			}

			for (size_t i = 0; i < m_SAResult.size(); i++)
			{
				size_t ii = m_SAResult[i].m_ref - p.Begin();
				if (p.IsInside(m_SAResult[i].m_ref) && ii < CDD.size())
				{
					double obs_y = m_SAResult[i].m_obs[I_EGGS];

					if (obs_y > -999)
					{

						double sim_y = 0;
						//boost::math::logistic_distribution<double> create_dist(m_CEC[μ], m_CEC[ѕ]);
						boost::math::weibull_distribution<double> create_dist(m_CEC[μ], m_CEC[ѕ]);
						//boost::math::non_central_f_distribution<double> create_dist( m_CEC[μ], m_CEC[ѕ], m_CEC[ʎf]);

						sim_y = Round(cdf(create_dist, CDD[ii]) * 100, ROUND_VAL);

						if (sim_y < 0.1)
							sim_y = 0;
						if (sim_y > 99.9)
							sim_y = 100;

						stat.Add(obs_y, sim_y);
					}
				}
			}
		}//for all years

		return;
	}

	void CLaricobiusOsakensisModel::GetFValueDaily(CStatisticXY& stat)
	{
		//return CalibrateCumulativeEggCreation(stat);

		if (!m_SAResult.empty())
		{
			if (!m_bCumul)
				m_bCumul = true;//SA always cumulative

			if (!m_weather.IsHourly())
				m_weather.ComputeHourlyVariables();

			//low and hi relative development rate must be approximatively the same
			//if (!IsParamValid())
				//return;

			for (size_t y = FIRST_Y; y < m_weather.GetNbYears(); y++)
			{
				int year = m_weather[y].GetTRef().GetYear();
				if (m_years[I_EGGS].find(year) != m_years[I_EGGS].end())
				{

					CModelStatVector output;
					CTPeriod p = m_weather[y].GetEntireTPeriod(CTM(CTM::DAILY));
//					p = m_weather[year].GetEntireTPeriod(CTM(CTM::DAILY));
					if (BEGIN_DECEMBER)
						p.Begin() = CTRef(year - 1, DECEMBER, DAY_01);

					//not possible to add a second year without having problem in evaluation....
					//if (m_weather[y].HaveNext())
						//p.End() = m_weather[y + 1].GetEntireTPeriod(CTM(CTM::DAILY)).End();

					output.Init(p, NB_STATS, 0);
					ExecuteDaily(m_weather[y].GetTRef().GetYear(), m_weather, output);

					static const std::array<bool, NB_INPUTS> TEST_STAGES = { {true, true} };
					static const size_t STAT_STAGE[NB_INPUTS] = { S_EGG, S_LARVAE };

					for (size_t i = 0; i < m_SAResult.size(); i++)
					{
						if (output.IsInside(m_SAResult[i].m_ref))
						{
							for (size_t j = 0; j < NB_INPUTS; j++)
							{
								if (TEST_STAGES[j])
								{
									double obs_y = Round(m_SAResult[i].m_obs[j], ROUND_VAL);
									double sim_y = Round(output[m_SAResult[i].m_ref][STAT_STAGE[j]], ROUND_VAL);

									if (obs_y > -999)
									{
										stat.Add(obs_y, sim_y);

										//double obs_x = m_SAResult[i].m_ref.GetJDay();
										//double sim_x = GetSimX(STAT_STAGE[j], m_SAResult[i].m_ref, obs_y, output);

										/*if (sim_x > -999)
										{
											obs_x = Round(100 * (obs_x - m_nb_days[j][LOWEST]) / m_nb_days[j][RANGE],ROUND_VAL);
											sim_x = Round(100 * (sim_x - m_nb_days[j][LOWEST]) / m_nb_days[j][RANGE],ROUND_VAL);
											stat.Add(obs_x, sim_x);
										}*/
									}
								}
							}
						}
					}//for all results
				}//have data
			}
		}
	}
}


#include "AHTLP.hpp"
#include "MHTLP.hpp"
#include "Puzzle.hpp"

#include <NTL/ZZ.h>
#include <vector>
#include <assert.h>
#include <openssl/sha.h>

using namespace std;
using namespace NTL;

clock_t start_time, end_time;

void TestAHTLP(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Init AHTLP scheme. " << endl;
    AHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    ZZ s_prime = scheme.GenerateRandomElement();
    ZZ r = scheme.GenerateRandomExponent(); // Generate randomness for puzzle s
    APuzzle Z = scheme.GeneratePuzzle(s);   // Generate a AHTLP for solution s
    APuzzle Z_prime = scheme.GeneratePuzzle(s_prime);
    APuzzle Z_AValid = scheme.GeneratePuzzle(s, r);

    APuzzle Z_add = Z + Z_prime;

    APuzzle invalid_Z(Z.n());
    invalid_Z.u = scheme.GenerateJacobiOne();
    invalid_Z.v = RandomBnd(scheme.n_square());

    auto sol_and_proof = scheme.SolvePuzzleWithProof(k, gamma, Z);
    auto invalid_Z_proof = scheme.SolvePuzzleWithProof(k, gamma, invalid_Z);
    auto sol_and_invalid_proof = sol_and_proof;
    get<2>(sol_and_invalid_proof) = scheme.GenerateRandomElement();

    auto s_add = scheme.SolvePuzzle(Z_add);
    assert((s + s_prime) % scheme.n() == s_add);

    auto AValid_proof = scheme.GenerateAValidProof(Z_AValid, s, r);

    start_time = clock();
    assert(scheme.VerifyProofOfSol(Z, sol_and_proof) == 1);
    end_time = clock();
    cout << "Time of VerifyProofOfSol for 1 \t\t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(invalid_Z, invalid_Z_proof) == -1);
    end_time = clock();
    cout << "Time of VerifyProofOfSol for -1 \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(Z, sol_and_invalid_proof) == 0);
    end_time = clock();
    cout << "Time of VerifyProofOfSol for 0 \t\t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyAValidProof(Z_AValid, AValid_proof) == true);
    end_time = clock();
    cout << "Time of VerifyAValidProof \t\t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestACorSol(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test ACorSol. " << endl;
    AHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    APuzzle Z = scheme.GeneratePuzzle(s);  // Generate a AHTLP for solution s

    start_time = clock();
    auto sol_and_proof = scheme.SolvePuzzleWithProof(k, gamma, Z);
    end_time = clock();
    cout << "Time of solving a puzzle and generating a ACorSol proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(Z, sol_and_proof) == 1);
    end_time = clock();
    cout << "Time of verifying a ACorSol proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestAInvalid(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test AInvalid. " << endl;
    AHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    APuzzle Z = scheme.GeneratePuzzle(s);  // Generate a AHTLP for solution s

    APuzzle invalid_Z(Z.n());
    invalid_Z.u = scheme.GenerateJacobiOne();
    invalid_Z.v = RandomBnd(scheme.n_square());

    start_time = clock();
    auto invalid_Z_proof = scheme.SolvePuzzleWithProof(k, gamma, invalid_Z);
    end_time = clock();
    cout << "Time of solving a puzzle and generating a AInvalid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(invalid_Z, invalid_Z_proof) == -1);
    end_time = clock();
    cout << "Time of verifying a AInvalid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestAValid(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test AValid. " << endl;
    AHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement();  // Generate a random solution from Z_n
    ZZ r = scheme.GenerateRandomExponent(); // Generate randomness for puzzle s
    APuzzle Z_AValid = scheme.GeneratePuzzle(s, r);

    start_time = clock();
    auto AValid_proof = scheme.GenerateAValidProof(Z_AValid, s, r);
    end_time = clock();
    cout << "Time of generating a AValid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyAValidProof(Z_AValid, AValid_proof) == true);
    end_time = clock();
    cout << "Time of verifying a AValid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestMHTLP(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Init MHTLP scheme. " << endl;
    MHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    ZZ s_prime = scheme.GenerateRandomElement();
    ZZ r = scheme.GenerateRandomExponent();       // Custom randomness
    ZZ r_prime = scheme.GenerateRandomExponent(); // Custom randomness
    MPuzzle Z = scheme.GeneratePuzzle(s);         // Generate a AHTLP for solution s
    MPuzzle Z_prime = scheme.GeneratePuzzle(s_prime);
    MPuzzle Z_MValid = scheme.GeneratePuzzle(s, r, r_prime);

    MPuzzle Z_mul = Z * Z_prime;

    MPuzzle invalid_Z(Z.n());
    invalid_Z.u = scheme.GenerateJacobiOne();
    invalid_Z.u_prime = scheme.GenerateJacobiOne();
    invalid_Z.v = scheme.GenerateJacobiOne();
    invalid_Z.theta = RandomBnd(scheme.n_square());

    auto sol_and_proof = scheme.SolvePuzzleWithProof(k, gamma, Z);
    // auto sol_and_proof = scheme.QuickSolvePuzzleWithProof(Z);
    auto invalid_Z_proof = scheme.SolvePuzzleWithProof(k, gamma, invalid_Z);
    auto sol_and_invalid_proof = sol_and_proof;
    get<2>(sol_and_invalid_proof) = scheme.GenerateRandomElement();

    auto s_mul = scheme.SolvePuzzle(Z_mul);
    assert(s * s_prime % scheme.n() == s_mul);

    auto MValid_proof = scheme.GenerateMValidProof(Z_MValid, s, r, r_prime);

    assert(s == std::get<0>(sol_and_proof));
    assert(-1 == std::get<0>(invalid_Z_proof));

    assert(scheme.VerifyProofOfSol(Z, sol_and_proof) == 1);
    assert(scheme.VerifyProofOfSol(invalid_Z, invalid_Z_proof) == -1);
    assert(scheme.VerifyProofOfSol(Z, sol_and_invalid_proof) == 0);

    assert(scheme.VerifyMValidProof(Z_MValid, MValid_proof) == true);
}

void TestMCorSol(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test MCorSol. " << endl;
    MHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    MPuzzle Z = scheme.GeneratePuzzle(s);  // Generate a AHTLP for solution s

    start_time = clock();
    auto sol_and_proof = scheme.SolvePuzzleWithProof(k, gamma, Z);
    end_time = clock();
    cout << "Time of solving a puzzle and generating a MCorSol proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(Z, sol_and_proof) == 1);
    end_time = clock();
    cout << "Time of verifying a MCorSol proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestMInvalid(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test MInvalid. " << endl;
    MHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement(); // Generate a random solution from Z_n
    MPuzzle Z = scheme.GeneratePuzzle(s);  // Generate a AHTLP for solution s

    MPuzzle invalid_Z(Z.n());
    invalid_Z.u = scheme.GenerateJacobiOne();
    invalid_Z.u_prime = scheme.GenerateJacobiOne();
    invalid_Z.v = scheme.GenerateJacobiOne();
    invalid_Z.theta = RandomBnd(scheme.n_square());

    start_time = clock();
    auto invalid_Z_proof = scheme.SolvePuzzleWithProof(k, gamma, invalid_Z);
    end_time = clock();
    cout << "Time of solving a puzzle and generating a MInvalid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyProofOfSol(invalid_Z, invalid_Z_proof) == -1);
    end_time = clock();
    cout << "Time of verifying a MInvalid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

void TestMValid(const long k, const long gamma, const long T, const long MODULUS_LEN = 2048, const long kappa = 128)
{
    cout << "Test MValid. " << endl;
    MHTLP scheme(MODULUS_LEN, T, kappa, true); // Init AHTLP scheme.

    ZZ s = scheme.GenerateRandomElement();        // Generate a random solution from Z_n
    ZZ r = scheme.GenerateRandomExponent();       // Custom randomness
    ZZ r_prime = scheme.GenerateRandomExponent(); // Custom randomness
    MPuzzle Z_MValid = scheme.GeneratePuzzle(s, r, r_prime);

    start_time = clock();
    auto MValid_proof = scheme.GenerateMValidProof(Z_MValid, s, r, r_prime);
    end_time = clock();
    cout << "Time of solving a puzzle and generating a MValid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;

    start_time = clock();
    assert(scheme.VerifyMValidProof(Z_MValid, MValid_proof) == true);
    end_time = clock();
    cout << "Time of verifying a MValid proof \t" << (double)(end_time - start_time) / CLOCKS_PER_SEC << endl;
}

int main(void)
{
    // Parameter setting for VDF
    const long T = 10000000;
    const long k = max(1, long(log2(T)) / 3);
    const long gamma = max(1, long(sqrt(T)));

    TestACorSol(k, gamma, T);

    TestAInvalid(k, gamma, T);

    TestAValid(k, gamma, T);

    TestMCorSol(k, gamma, T);

    TestMInvalid(k, gamma, T);

    TestMValid(k, gamma, T);

    // TestAHTLP(k, gamma, T);

    // TestMHTLP(k, gamma, T);

    return 0;
}


#include "arf_tsr/task_space_region.h"

#include <vector>
#include <Eigen/Geometry>
#include <memory>

#include <arf_sampling/sampler.h>

namespace arf
{
Eigen::Vector3d minNormEquivalent(const Eigen::Vector3d& angles)
{
  Eigen::Matrix<double, 9, 3> m;
  double x(angles.x()), y(angles.y()), z(angles.z());
  // clang-format off
   m <<  x,  y, z,
        x - M_PI, -y - M_PI, z - M_PI,
        x - M_PI, -y - M_PI, z + M_PI,
        x - M_PI, -y + M_PI, z - M_PI,
        x - M_PI, -y + M_PI, z + M_PI,
        x + M_PI, -y - M_PI, z - M_PI,
        x + M_PI, -y - M_PI, z + M_PI,
        x + M_PI, -y + M_PI, z - M_PI,
        x + M_PI, -y + M_PI, z + M_PI;
  // clang-format on
  // get the index of the row with the lowest norm
  Eigen::VectorXd::Index index;
  m.rowwise().norm().minCoeff(&index);
  return m.row(index);
}

Eigen::Vector6d poseDistance(const Transform& tf_ref, const Transform& tf)
{
  const Transform tf_diff = tf_ref.inverse() * tf;
  Eigen::Vector3d angles = minNormEquivalent(tf_diff.rotation().eulerAngles(0, 1, 2));
  Eigen::Vector6d d;
  d << tf_diff.translation(), angles;
  return d;
}

/*********************
 * Task Space Region
 * *******************/
TSR::TSR(Transform tf, std::array<Bound, 6> bounds, arf::SamplerPtr sampler, const std::vector<int>& num_samples)
  : tf_nominal_(tf), bounds_(bounds), sampler_(sampler)
{
  sampler_->addDimension(bounds_[0].lower, bounds_[0].upper, num_samples[0]);
  sampler_->addDimension(bounds_[1].lower, bounds_[1].upper, num_samples[1]);
  sampler_->addDimension(bounds_[2].lower, bounds_[2].upper, num_samples[2]);
  sampler_->addDimension(bounds_[3].lower, bounds_[3].upper, num_samples[3]);
  sampler_->addDimension(bounds_[4].lower, bounds_[4].upper, num_samples[4]);
  sampler_->addDimension(bounds_[5].lower, bounds_[5].upper, num_samples[5]);
}

std::vector<Transform> TSR::getSamples(const int n) const
{
  std::vector<Transform> samples;
  auto tsr_samples = sampler_->getSamples(n);
  for (auto& tsr_sample : tsr_samples)
  {
    Eigen::Vector6d v(tsr_sample.data());
    samples.push_back(valuesToPose(v));
  }
  return samples;
}

Transform TSR::valuesToPose(const Eigen::Vector6d& values) const
{
  using Translation = Eigen::Translation3d;
  using AngleAxis = Eigen::AngleAxisd;
  using Vector = Eigen::Vector3d;

  // clang-format off
    Transform t;
      t = Translation(values[0], values[1], values[2]) *
          AngleAxis(values[3], Vector::UnitX()) *
          AngleAxis(values[4], Vector::UnitY()) *
          AngleAxis(values[5], Vector::UnitZ());
  // clang-format on

  // the values are expressed in the nominal tsr frame,
  // convert them to the world frame
  return tf_nominal_ * t;
}

Eigen::Vector6d TSR::poseToValues(const Transform& tf) const
{
  // values are calculated in nominal frame of this tsr
  Eigen::Isometry3d tf_diff = tf_nominal_.inverse() * tf;
  // Eigen documentation:
  //    The returned angles are in the ranges [0:pi]x[-pi:pi]x[-pi:pi].
  Eigen::Vector3d angles = tf_diff.rotation().eulerAngles(0, 1, 2);

  Eigen::Vector6d values;
  values << tf_diff.translation(), angles;
  return values;
}

Eigen::Vector6d TSR::applyBounds(const Eigen::Vector6d& values) const
{
  Eigen::Vector6d distance;
  for (std::size_t i{ 0 }; i < 6; ++i)
  {
    distance[i] = bounds_[i].distance(values[i]);
  }
  return distance;
}

}  // namespace arf


#include "neal2_algorithm.h"

#include <Eigen/Dense>
#include <memory>
#include <stan/math/prim/fun.hpp>
#include <vector>

#include "algorithm_id.pb.h"
#include "hierarchy_id.pb.h"
#include "marginal_state.pb.h"
#include "mixing_id.pb.h"
#include "src/hierarchies/base_hierarchy.h"
#include "src/mixings/marginal_mixing.h"
#include "src/utils/distributions.h"
#include "src/utils/rng.h"

//! \param hier Hierarchy object
//! \return     Vector of evaluation of component on the provided grid
Eigen::VectorXd Neal2Algorithm::lpdf_marginal_component(
    std::shared_ptr<AbstractHierarchy> hier, const Eigen::MatrixXd &grid,
    const Eigen::MatrixXd &covariates) const {
  return hier->prior_pred_lpdf_grid(grid, covariates);
}

Eigen::VectorXd Neal2Algorithm::get_cluster_prior_mass(
    const unsigned int data_idx) const {
  unsigned int n_data = data.rows();
  unsigned int n_clust = unique_values.size();
  Eigen::VectorXd logprior(n_clust + 1);
  for (size_t j = 0; j < n_clust; j++) {
    // Probability of being assigned to an already existing cluster
    logprior(j) = marg_mixing->mass_existing_cluster(
        n_data - 1, true, true, unique_values[j],
        mix_covariates.row(data_idx));
  }
  // Further update with marginal component
  logprior(n_clust) = marg_mixing->mass_new_cluster(
      n_data - 1, true, true, n_clust, mix_covariates.row(data_idx));

  return logprior;
}

Eigen::VectorXd Neal2Algorithm::get_cluster_lpdf(
    const unsigned int data_idx) const {
  unsigned int n_data = data.rows();
  unsigned int n_clust = unique_values.size();
  Eigen::VectorXd loglpdf(n_clust + 1);
  for (size_t j = 0; j < n_clust; j++) {
    // Probability of being assigned to an already existing cluster
    loglpdf(j) = unique_values[j]->like_lpdf(data.row(data_idx),
                                             hier_covariates.row(data_idx));
  }
  // Probability of being assigned to a newly created cluster
  loglpdf(n_clust) = unique_values[0]->prior_pred_lpdf(
      data.row(data_idx), hier_covariates.row(data_idx));
  return loglpdf;
}

void Neal2Algorithm::print_startup_message() const {
  std::string msg = "Running Neal2 algorithm with " +
                    bayesmix::HierarchyId_Name(unique_values[0]->get_id()) +
                    " hierarchies, " +
                    bayesmix::MixingId_Name(marg_mixing->get_id()) +
                    " mixing...";
  std::cout << msg << std::endl;
}

void Neal2Algorithm::sample_allocations() {
  // Initialize relevant values
  unsigned int n_data = data.rows();
  auto &rng = bayesmix::Rng::Instance().get();
  // Loop over data points
  for (size_t i = 0; i < n_data; i++) {
    unsigned int n_clust = unique_values.size();
    bool singleton = (unique_values[allocations[i]]->get_card() <= 1);
    // Remove datum from cluster
    unique_values[allocations[i]]->remove_datum(
        i, data.row(i), update_hierarchy_params(), hier_covariates.row(i));
    // Compute probabilities of clusters in log-space
    Eigen::VectorXd logprobas =
        get_cluster_prior_mass(i) + get_cluster_lpdf(i);
    // Draw a NEW value for datum allocation
    unsigned int c_new =
        bayesmix::categorical_rng(stan::math::softmax(logprobas), rng, 0);
    unsigned int c_old = allocations[i];

    if (c_new == n_clust) {
      std::shared_ptr<AbstractHierarchy> new_unique =
          unique_values[0]->clone();
      new_unique->add_datum(i, data.row(i), update_hierarchy_params(),
                            hier_covariates.row(i));
      // Generate new unique values with posterior sampling
      new_unique->sample_full_cond(!update_hierarchy_params());
      unique_values.push_back(new_unique);
      allocations[i] = unique_values.size() - 1;
    } else {
      allocations[i] = c_new;
      unique_values[c_new]->add_datum(
          i, data.row(i), update_hierarchy_params(), hier_covariates.row(i));
    }
    if (singleton) {
      // Relabel allocations so that they are consecutive numbers
      for (auto &c : allocations) {
        if (c > c_old) {
          c -= 1;
        }
      }
      unique_values.erase(unique_values.begin() + c_old);
    }
  }
}

void Neal2Algorithm::sample_unique_values() {
  for (auto &un : unique_values) {
    un->sample_full_cond(!update_hierarchy_params());
  }
}


#include "calotypes/CameraCalibration.h"
#include "calotypes/KernelDensityEstimation.hpp"
#include "calotypes/ImportanceResampling.hpp"

#include <opencv2/calib3d.hpp>
#include <Eigen/Dense>

namespace calotypes
{

bool CompareDataName( const CameraTrainingData& a, const CameraTrainingData& b )
{
	if( a.name.size() == b.name.size() )
	{
		return a.name < b.name;
	}
	return a.name.size() < b.name.size();
}

Pose3D::Pose3D()
	: transform( Eigen::Matrix<double,4,4>::Identity() )
{}

Pose3D::Pose3D( const cv::Mat& transVec, const cv::Mat& rotVec )
{
	Eigen::Translation<double,3> trans( transVec.at<double>(0), transVec.at<double>(1), transVec.at<double>(2) );
	Eigen::Vector3d aaBase( rotVec.at<double>(0), rotVec.at<double>(1), rotVec.at<double>(2) );
	double angle = aaBase.norm();
	aaBase.normalize();
	Eigen::AngleAxisd aa( angle, aaBase );
	transform = trans*aa;
}

Pose3D::VectorType Pose3D::ToVector() const
{
	VectorType vec;
	vec(0) = transform.translation().x();
	vec(1) = transform.translation().y();
	vec(2) = transform.translation().z();
	Eigen::Quaterniond q( transform.rotation() );
	Eigen::Vector3d aa = q.vec();
	double angle = std::acos( q.w() )*2;
	if( angle > M_PI ) { angle = angle - 2*M_PI; }
	if( angle < -M_PI ) { angle = angle + 2*M_PI; }
	aa *= angle;
	vec.block<3,1>(3,0) = aa;
	return vec;
}

Pose3D operator/( const Pose3D& a, const Pose3D& b )
{
	Pose3D other;
	other.transform = a.transform * b.transform.inverse();
	return other;
}

CameraModel::CameraModel()
	: cameraMatrix( cv::Mat::eye( 3, 3, CV_64F ) ),
	distortionCoefficients( cv::Mat::eye( 12, 1, CV_64F ) )
{}

CameraModel::CameraModel( const CameraTrainingParams& params )
	: trainingParams( params ), cameraMatrix( cv::Mat::eye(3, 3, CV_64F ) )
{
	unsigned int numDistortionParams = 4;

	if( params.enableRadialDistortion[2] ) { numDistortionParams = 5; }

	if( params.enableRationalDistortion[0] || params.enableRationalDistortion[1]
		|| params.enableRationalDistortion[2] ) { numDistortionParams = 8; }

	if( params.enableThinPrism ) { numDistortionParams = 12; }

	distortionCoefficients = cv::Mat::zeros( numDistortionParams, 1, CV_64F );
}

std::ostream& operator<<( std::ostream& os, const CameraModel& model )
{
	Eigen::Map< const Eigen::MatrixXd > cameraMap( model.cameraMatrix.ptr<double>(), 3, 3 );
	Eigen::Map< const Eigen::MatrixXd > distortionMap( model.distortionCoefficients.ptr<double>(),
												 model.distortionCoefficients.total(), 1 );
	os << "fx: " << cameraMap(0,0) << std::endl;
	os << "fy: " << cameraMap(1,1) << std::endl;
	os << "px: " << cameraMap(2,0) << std::endl;
	os << "py: " << cameraMap(2,1) << std::endl;
	os << "aspect ratio optimized: " << model.trainingParams.optimizeAspectRatio << std::endl;
	os << "optimize principal point: " << model.trainingParams.optimizePrincipalPoint << std::endl;
	os << "radial distortion k1: " << distortionMap(0) << std::endl;
	os << "radial distortion k2: " << distortionMap(1) << std::endl;
	os << "radial distortion k3: " << (model.trainingParams.enableRadialDistortion[2] ? distortionMap(4) : 0.0) << std::endl;
	os << "radial distortion k4: " << (model.trainingParams.enableRationalDistortion[0] ? distortionMap(5) : 0.0) << std::endl;
	os << "radial distortion k5: " << (model.trainingParams.enableRationalDistortion[1] ? distortionMap(6) : 0.0) << std::endl;
	os << "radial distortion k6: " << (model.trainingParams.enableRationalDistortion[2] ? distortionMap(7) : 0.0) << std::endl;
	os << "tangential distortion p1: " << distortionMap(2) << std::endl;
	os << "tangential distortion p2: " << distortionMap(3) << std::endl;
	os << "thin lens s1: " << (model.trainingParams.enableThinPrism ? distortionMap(8) : 0.0) << std::endl;
	os << "thin lens s2: " << (model.trainingParams.enableThinPrism ? distortionMap(9) : 0.0) << std::endl;
	os << "thin lens s3: " << (model.trainingParams.enableThinPrism ? distortionMap(10) : 0.0) << std::endl;
	os << "thin lens s4: " << (model.trainingParams.enableThinPrism ? distortionMap(11) : 0.0) << std::endl;
	return os;
}

CameraTrainingParams::CameraTrainingParams()
	: optimizeAspectRatio( false ), optimizePrincipalPoint( false ),
	enableRadialDistortion{ false, false, false },
	enableRationalDistortion{ false, false, false },
	enableTangentialDistortion( false ), enableThinPrism( false )
	{}

std::ostream& operator<<( std::ostream& os, const CameraTrainingParams& params )
{
	os << "aspect ratio: " << params.optimizeAspectRatio << std::endl;
	os << "principal point: " << params.optimizePrincipalPoint << std::endl;
	os << "radial distortion k1: " << params.enableRadialDistortion[0] << std::endl;
	os << "radial distortion k2: " << params.enableRadialDistortion[1] << std::endl;
	os << "radial distortion k3: " << params.enableRadialDistortion[2] << std::endl;
	os << "radial distortion k4: " << params.enableRationalDistortion[0] << std::endl;
	os << "radial distortion k5: " << params.enableRationalDistortion[1] << std::endl;
	os << "radial distortion k6: " << params.enableRationalDistortion[2] << std::endl;
	os << "tangential distortion: " << params.enableTangentialDistortion << std::endl;
	os << "thin prism: " << params.enableThinPrism << std::endl;
	return os;
}

// double ImportanceTrainCameraModel( CameraModel& model, std::vector<CameraTrainingData>& data,
// 								   const cv::Size& imgSize, unsigned int subsetSize,
// 								   CameraDataMetric::Ptr dataMetric, CameraTrainingParams params )
// {
//
// 	GaussianKernel::Ptr kernel = std::make_shared<GaussianKernel>( 1.0 );
// 	KernelDensityEstimator<CameraTrainingData>::Ptr kde =
// 		std::make_shared< KernelDensityEstimator<CameraTrainingData> >( data, dataMetric, kernel, 0.1 );
// 	UniformDensityFunction<CameraTrainingData>::Ptr target =
// 		std::make_shared<UniformDensityFunction< CameraTrainingData> >();
// 	ImportanceDataSelector<CameraTrainingData>::Ptr dataSelector =
// 		std::make_shared<ImportanceDataSelector< CameraTrainingData> >( kde, target );
//
// 	std::vector<CameraTrainingData> subset;
// 	dataSelector->SelectData( data, subsetSize, subset );
// 	double err = TrainCameraModel( model, subset, imgSize, params );
// 	data = subset;
// 	return err;
// }

double SubsetTrainCameraModel( CameraModel& model, std::vector<CameraTrainingData>& data,
							   const cv::Size& imgSize, DataSelector<CameraTrainingData>& selector,
							   unsigned int subsetSize, CameraTrainingParams params )
{
	std::vector<CameraTrainingData> subset;
	selector.SelectData( data, subsetSize, subset );
	double err = TrainCameraModel( model, subset, imgSize, params );
	data = subset;
	return err;
}

// TODO TermCriteria
double TrainCameraModel( CameraModel& model, std::vector<CameraTrainingData>& data,
						 const cv::Size& imgSize, CameraTrainingParams params )
{

	model = CameraModel( params );

	int flags = 0;

	if( !params.optimizeAspectRatio ) { flags |= cv::CALIB_FIX_ASPECT_RATIO; }
	if( !params.optimizePrincipalPoint ) { flags |= cv::CALIB_FIX_PRINCIPAL_POINT; }

	if( !params.enableRadialDistortion[0] ) { flags |= cv::CALIB_FIX_K1; }
	if( !params.enableRadialDistortion[1] ) { flags |= cv::CALIB_FIX_K2; }
	if( !params.enableRadialDistortion[2] ) { flags |= cv::CALIB_FIX_K3; }

	if( params.enableRationalDistortion[0] || params.enableRationalDistortion[1]
		|| params.enableRationalDistortion[2] )
	{
		flags |= cv::CALIB_RATIONAL_MODEL;
		if( !params.enableRationalDistortion[0] ) { flags |= cv::CALIB_FIX_K4; }
		if( !params.enableRationalDistortion[1] ) { flags |= cv::CALIB_FIX_K5; }
		if( !params.enableRationalDistortion[2] ) { flags |= cv::CALIB_FIX_K6; }
	}

	if( !params.enableTangentialDistortion ) { flags |= cv::CALIB_ZERO_TANGENT_DIST; }

	if( params.enableThinPrism ) { flags |= cv::CALIB_THIN_PRISM_MODEL; }

	std::vector< cv::Mat > rvecs, tvecs;

	std::vector< ImagePoints > imagePoints( data.size() );
	std::vector< ObjectPoints > objectPoints( data.size() );
	for( unsigned int i = 0; i < data.size(); i++ )
	{
		imagePoints[i] = data[i].imagePoints;
		objectPoints[i] = data[i].objectPoints;
	}
	cv::calibrateCamera( objectPoints, imagePoints, imgSize, model.cameraMatrix,
						 model.distortionCoefficients, rvecs, tvecs, flags );

	return TestCameraModel( model, data );
}

double TestCameraModel( const CameraModel& model, const std::vector<CameraTrainingData>& data )
{

// 	double worst = -std::numeric_limits<double>::infinity();
 	double acc = 0;
	unsigned int count = 0;
	std::vector< cv::Point2f > predictions;
	cv::Mat rotation, translation;
	for( unsigned int i = 0; i < data.size(); i++ )
	{
		// First estimate pose with PnP
		// TODO Use zero distortion?
		bool ret = cv::solvePnP( data[i].objectPoints, data[i].imagePoints,
								 model.cameraMatrix, model.distortionCoefficients,
								 rotation, translation );
		// TODO
		if( !ret )
		{
			std::cerr << "Failed PnP!" << std::endl;
			return std::numeric_limits<double>::infinity();
		}

		// Then reproject to image coordinates
		cv::projectPoints( data[i].objectPoints, rotation, translation,
						   model.cameraMatrix, model.distortionCoefficients, predictions );
		count += predictions.size();

		for( unsigned int j = 0; j < predictions.size(); j++ )
		{
			cv::Point2f diff = data[i].imagePoints[j] - predictions[j];
			acc += std::sqrt( diff.x*diff.x + diff.y*diff.y );
		}
	}
	return acc / count;
}

PoseKernelFunction::PoseKernelFunction( const Eigen::Matrix<double,6,6>& w )
: weights( w ) {}

double PoseKernelFunction::Difference( const CameraTrainingData& a, const CameraTrainingData& b ) const
{
	Pose3D diff = a.pose / b.pose;
	Pose3D::VectorType diffVec = diff.ToVector();
	return diffVec.transpose() * weights * diffVec;
}

} // end namespace calotypes


#pragma once
#include <vector>
#include <string>
#include <map>
#include "Reftable.hpp"
#include "cxxopts.hpp"
#include <Eigen/Dense>

struct EstimParamResults {
    std::vector<double> plsvar;
    std::vector<std::pair<std::string,double>> plsweights;
    std::vector<std::pair<std::string,double>> variable_importance;
    std::vector<double> ntree_oob_error;
    std::vector<std::pair<double,double>> values_weights;
    std::map<size_t,size_t> oob_map;
    Eigen::MatrixXd oob_weights;
    std::map<std::string,double> point_estimates;
    std::map<std::string, // Global/Local
        std::map<std::string, // Mean/Median/CI
            std::map<std::string,double>>> errors;
};

template<class MatrixType>
EstimParamResults EstimParam_fun(Reftable<MatrixType> &reftable,
                                   std::vector<double> statobs,
                                   const cxxopts::ParseResult opts,
                                   bool quiet = false,
                                   bool weights = false);


// phase field model

#include <libgeodecomp.h>
#define PSERIAL 1
#ifdef PCUDA
#include <libgeodecomp/parallelization/cudasimulator.h>
#endif

#ifdef PMPI
#include <libgeodecomp/parallelization/hiparsimulator.h>
#include <libgeodecomp/loadbalancer/tracingbalancer.h>
#include <libgeodecomp/loadbalancer/noopbalancer.h>
#include <libgeodecomp/geometry/partitions/recursivebisectionpartition.h>
#include <libgeodecomp/io/bovwriter.h>
#endif

#ifdef PMGPU
#include <libgeodecomp/parallelization/hiparsimulator/cudastepper.h>
#endif

#include <iostream>
#include <vector>

#include <boost/tokenizer.hpp>
#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>

using namespace std;
using namespace LibGeoDecomp;

class RigidMotions;
// constants

const int nf = 4;
const int nx = 1152/4;
const int ny = 1152/4;
//const int nx = 128;
//const int ny = 128;
const int nz = 1;

int outputFrequency = 1000;
int numSteps = 300000;

const double dt = 0.001;
// model

//#include "allen_cahn.hpp"
//#include "cahn_hilliard.hpp"
#include "wang.hpp"

// init
vector<double> Cell::staticData = {0};
class CellInitializer : public SimpleInitializer<Cell>
{
public:
    CellInitializer() : SimpleInitializer<Cell>(Coord<3>(nx, ny, nz), numSteps)
    {}

 	void readparticles(	vector< vector<double> > &particles )
	{
		using namespace boost;
		using namespace std;
		cout << "Reading particles" << endl;
		string data("4particles.txt");
		ifstream in(data.c_str());
		if (!in) {
			cerr << "Can't open the input file " << data << endl;
			exit(1);
		}

		typedef tokenizer<escaped_list_separator<char> > Tokenizer;
		string line;
		vector<string> vec;
		int l = 0;
		while(getline(in,line))
		{
			Tokenizer tok(line);
			vec.assign(tok.begin(),tok.end());
			//vector <double> v = {atof(vec[5].c_str()),atof(vec[6].c_str()),atof(vec[8].c_str())};
			particles[l][0] = atof(vec[0].c_str());
			particles[l][1] = atof(vec[1].c_str());
			particles[l][2] = atof(vec[2].c_str());
			cout << "vector of coor:" << particles[l][0] << " " << particles[l][1] << ',' << particles[l][2]<<  endl;
			l++;
		}

	}

    virtual void grid(GridBase<Cell, 3> *ret)
    {
	vector< vector<double> > particles(30, vector<double>(3, 0.0));
        boost::mt19937 rng;
        boost::normal_distribution<> nd(0.0, 0.5);
        boost::variate_generator<boost::mt19937&,
                           boost::normal_distribution<> > var_nor(rng, nd);

        CoordBox<3> rect = ret->boundingBox();
	// read particles from external file
        readparticles(particles);
        double size_img = 1152/4;
        for (int z = 0; z < nz; ++z)
            for (int y = 0; y < ny; ++y)
                for (int x = 0; x < nx; ++x)
                {
                    Coord<3> c(x, y, z);
                    vector<double> v(nf);

                    //for(int i = 0; i < nf; ++i)
                    //    v[i] = var_nor();

                    //if (rect.inBounds(c))
                    //    ret->set(c, Cell(v));

                    if (rect.inBounds(c))
                    {
						// sets the particles inside domain
                        for(int i = 0; i < nf; ++i)
                        {
							/*
                            double dx = double(x - nx/2)/double(nx);
                            double dy = double(y - ny/2)/double(ny);
                            double dz = double(z - nz/2)/double(nz);
                            double r2 = dx*dx + dy*dy + dz*dz;

                            // reduced the internal pore from 0.04 to 0.02
                            if(r2 > 0.02)
                              v[i] = var_nor() + 0.12;
                            else
                              v[i] = 0;
                      		*/
							double xc = particles[i][0]/size_img;
							double yc = particles[i][1]/size_img;
							double rc = particles[i][2]/size_img;
							//cout << "xc: " << xc << "yc: " << yc << "rc: " << rc << endl;
							double dx = double(x - xc*nx)/double(nx);
							double dy = double(y - yc*ny)/double(ny);
							double dz = double(z - nz/2)/double(nz);
							dz = 0.0;
							double r2 = dx*dx + dy*dy + dz*dz;
							// scale radius of particles
							//double scale = 5.0/160.0;
							//rc *= scale;
							if (r2 < rc*rc) {
								   //v[i] = var_nor() + 0.12;
								   v[i] = 2.0/3.0 * M_PI ;
                            	   				   ret->set(c, Cell(v));
							} else {
								   v[i] = 0.0;
							}
                            //ret->set(c, Cell(v));
                        }
                    }
                }
    }
};
//
// Steerer class
// this class is used to run the rigid motion
// of the sintering particles.
//
//
class RigidMotions : public Steerer<Cell>
{
	public:
		using Steerer<Cell>::CoordType;
		using Steerer<Cell>::GridType;
		using Steerer<Cell>::Topology;
	RigidMotions(const unsigned ioPeriod): Steerer<Cell>(ioPeriod), vol(30),xc(30),yc(30)
	{}
	void nextStep(
		GridType *grid,
		const Region<Topology::DIM>& validRegion,
		const CoordType& globalDimensions,
		unsigned step,
		SteererEvent event,
		std::size_t rank,
		bool lastCall,
		SteererFeedback *feedback)
	{
		Coord<3> dim = grid->dimensions();
		//cout << "Dim: " << "("<< dim.x() <<","<< dim.y()<<"," << dim.z() <<")"<< endl;
		for (int i = 0; i < nf; ++i) vol[i] = 0.0;
		// calculate volume of the particles
		// and center of gravity for the particles
		for (int z = 0; z < nz; ++z)
           	for (int y = 0; y < ny; ++y)
              	for (int x = 0; x < nx; ++x)
                {
                    Coord<3> c(x,y,z);
                    Cell cell = grid->get(c);
                    for(int i = 0; i < nf; ++i) {
                        vol[i] += cell.eta[i];
                        xc[i]  += x * cell.eta[i];
                        yc[i]  += y * cell.eta[i];
                    }
                }
		for (int i = 0; i < nf; ++i) {
			//cout << "Volume of particle first eval." << i << ":" << vol[i] << endl;
			xc[i] /= vol[i];
			yc[i] /= vol[i];
		}

		// write the current value of volume for particles
		for (int i = 0; i < nf; ++i) {
			//xc[i] /= vol[i];
			//yc[i] /= vol[i];
			//cout << "Volume of particle " << i << ":" << vol[i] << endl;
			//cout << "(xc,yc): " << xc[i] << "," << yc[i] << endl;
		}
        feedback->setStaticData(vol);

	}

	private:
	vector<double> vol;
	vector<double> xc, yc;
};
// run

void runSimulation()
{
#ifdef PMPI
    MPI_Aint displacements[] = { 0 };
    MPI_Datatype memberTypes[] = { MPI_CHAR };
    int lengths[] = { sizeof(Cell) };
    MPI_Type_create_struct(1, lengths, displacements, memberTypes, &Cell::MPIDataType);
    MPI_Type_commit(&Cell::MPIDataType);
#endif

    // init

    CellInitializer *init = new CellInitializer();

    // simulators

#ifdef PSERIAL
    SerialSimulator<Cell> sim(init);
#endif

#ifdef PCUDA
    CudaSimulator<Cell> sim(init, Coord<3>(64, 8, 1));
#endif

#ifdef PMPI
#ifdef PMGPU
    HiParSimulator::HiParSimulator
        <Cell, RecursiveBisectionPartition<3>, HiParSimulator::CUDAStepper<Cell> > sim(
            init,
            MPILayer().rank() ? 0 : new NoOpBalancer(),
            numSteps,
            1);
#else
    HiParSimulator<Cell, RecursiveBisectionPartition<3> > sim(
        init,
        MPILayer().rank() ? 0 : new NoOpBalancer(),
        numSteps,
        1);
#endif
#endif

    // writers

#ifdef PMPI
    if (MPILayer().rank() == 0)
        sim.addWriter(new TracingWriter<Cell>(outputFrequency, init->maxSteps()));

    sim.addWriter(
        new BOVWriter<Cell>(
            Selector<Cell>(&Cell::phi, "phi"),
            "phi",
            outputFrequency));

    sim.addWriter(
        new BOVWriter<Cell>(
            Selector<Cell>(&Cell::rho, "rho"),
            "rho",
            outputFrequency));

#else
    sim.addWriter(new TracingWriter<Cell>(outputFrequency, init->maxSteps()));

    sim.addWriter(
        new SerialBOVWriter<Cell>(
            Selector<Cell>(&Cell::phi, "phi"),
            "phi",
            outputFrequency));

    sim.addWriter(
        new SerialBOVWriter<Cell>(
            Selector<Cell>(&Cell::rho, "rho"),
            "rho",
            outputFrequency));

#endif
    cout << "running simulation" << endl;
    sim.addSteerer(new RigidMotions(10));
    sim.run();
}

// main

int main(int argc, char **argv)
{
    //simParamsHost.initParams(argc, argv);
    //cudaSetDevice(simParamsHost.cudaDevice);
#ifdef PMPI
    MPI_Init(&argc, &argv);
#endif

    cout << "** xphase **" << endl;

    runSimulation();

#ifdef PMPI
    MPI_Finalize();
#endif

    return 0;
}


#ifndef MTGP32_CALC_POLY_HPP
#define MTGP32_CALC_POLY_HPP
/**
 * @file mtgp32-calc-poly.hpp
 *
 * @brief functions for calculating jump polynomial.
 *
 * @author Mutsuo Saito (Hiroshima University)
 * @author Makoto Matsumoto (The University of Tokyo)
 *
 * Copyright (C) 2012 Mutsuo Saito, Makoto Matsumoto,
 * Hiroshima University and The University of Tokyo.
 * All rights reserved.
 *
 * The 3-clause BSD License is applied to this software, see
 * LICENSE.txt
 */
#include <string>
#include <NTL/GF2X.h>
#include "mtgp32-fast.h"

void calc_characteristic(NTL::GF2X& poly, mtgp32_fast_t * mtgp32);
void calc_characteristic(std::string& str, mtgp32_fast_t * mtgp32);
void calc_characteristic(uint32_t array[], int size, mtgp32_fast_t * mtgp32);

#endif


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#ifndef UNIFORMDIELECTRIC_HPP
#define UNIFORMDIELECTRIC_HPP

#include <cmath>
#include <iosfwd>
#include <vector>

#include "Config.hpp"

#include <Eigen/Core>

class Element;

#include "DerivativeTypes.hpp"
#include "DerivativeUtils.hpp"
#include "bi_operators/IntegratorForward.hpp"
#include "GreensFunction.hpp"
#include "dielectric_profile/Uniform.hpp"

/*! \file UniformDielectric.hpp
 *  \class UniformDielectric
 *  \brief Green's function for uniform dielectric.
 *  \author Luca Frediani and Roberto Di Remigio
 *  \date 2012-2015
 *  \tparam DerivativeTraits evaluation strategy for the function and its derivatives
 *  \tparam IntegratorPolicy policy for the calculation of the matrix represenation of S and D
 */

template <typename DerivativeTraits = AD_directional,
          typename IntegratorPolicy = CollocationIntegrator>
class UniformDielectric __final : public GreensFunction<DerivativeTraits, IntegratorPolicy, Uniform,
                                     UniformDielectric<DerivativeTraits, IntegratorPolicy> >
{
public:
    UniformDielectric(double eps) : GreensFunction<DerivativeTraits, IntegratorPolicy, Uniform,
                              UniformDielectric<DerivativeTraits, IntegratorPolicy> >() { this->profile_ = Uniform(eps); }
    UniformDielectric(double eps, double f) : GreensFunction<DerivativeTraits, IntegratorPolicy, Uniform,
                              UniformDielectric<DerivativeTraits, IntegratorPolicy> >(f) { this->profile_ = Uniform(eps); }
    virtual ~UniformDielectric() {}

    /*! Calculates the matrix representation of the S operator
     *  \param[in] e list of finite elements
     */
    virtual Eigen::MatrixXd singleLayer(const std::vector<Element> & e) const __override
    {
        return this->integrator_.singleLayer(*this, e);
    }
    /*! Calculates the matrix representation of the D operator
     *  \param[in] e list of finite elements
     */
    virtual Eigen::MatrixXd doubleLayer(const std::vector<Element> & e) const __override
    {
        return this->integrator_.doubleLayer(*this, e);
    }

    double epsilon() const { return this->profile_.epsilon; }

    friend std::ostream & operator<<(std::ostream & os, UniformDielectric & gf) {
        return gf.printObject(os);
    }
private:
    /*! Evaluates the Green's function given a pair of points
     *
     *  \param[in] sp the source point
     *  \param[in] pp the probe point
     */
    virtual DerivativeTraits operator()(DerivativeTraits * sp, DerivativeTraits * pp) const __override
    {
        return 1/(this->profile_.epsilon * distance(sp, pp));
    }
    /*! Returns value of the kernel of the \f$\mathcal{D}\f$ integral operator
     *  for the pair of points p1, p2:
     *  \f$ [\boldsymbol{\varepsilon}\nabla_{\mathbf{p_2}}G(\mathbf{p}_1, \mathbf{p}_2)]\cdot \mathbf{n}_{\mathbf{p}_2}\f$
     *  To obtain the kernel of the \f$\mathcal{D}^\dagger\f$ operator call this methods with \f$\mathbf{p}_1\f$
     *  and \f$\mathbf{p}_2\f$ exchanged and with \f$\mathbf{n}_{\mathbf{p}_2} = \mathbf{n}_{\mathbf{p}_1}\f$
     *  \param[in] direction the direction
     *  \param[in]        p1 first point
     *  \param[in]        p2 second point
     */
    virtual double kernelD_impl(const Eigen::Vector3d & direction,
                              const Eigen::Vector3d & p1, const Eigen::Vector3d & p2) const __override
    {
        return this->profile_.epsilon * (this->derivativeProbe(direction, p1, p2));
    }
    virtual KernelS exportKernelS_impl() const __override {
      return pcm::bind(&UniformDielectric<DerivativeTraits, IntegratorPolicy>::kernelS, *this, pcm::_1, pcm::_2);
    }
    virtual KernelD exportKernelD_impl() const __override {
      return pcm::bind(&UniformDielectric<DerivativeTraits, IntegratorPolicy>::kernelD, *this, pcm::_1, pcm::_2, pcm::_3);
    }
    virtual std::ostream & printObject(std::ostream & os) __override
    {
        os << "Green's function type: uniform dielectric" << std::endl;
        os << this->profile_;
        return os;
    }
};

#endif // UNIFORMDIELECTRIC_HPP


//
// Copyright 2019 Miral Shah <miralshah2211@gmail.com>
//
// Use, modification and distribution are subject to 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/image_processing/threshold.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/algorithm.hpp>
#include <boost/gil/gray.hpp>
#include <boost/core/lightweight_test.hpp>

namespace gil = boost::gil;

int main()
{
    int height = 4;
    int width= 4;
    gil::gray8_image_t original(width, height), threshold(width, height),
        expected(width, height);

    //filling original view's upper half part with gray pixels of value 50
    //filling original view's lower half part with gray pixels of value 150
    gil::fill_pixels(gil::subimage_view(gil::view(original), 0, 0, original.width(),
        original.height()/2), gil::gray8_pixel_t(50));
    gil::fill_pixels(gil::subimage_view(gil::view(original), 0, original.height()/2,
        original.width(), original.height()/2), gil::gray8_pixel_t(150));

    /*------------------------------Threshold Binary-----------------------------------*/

    //expected view after thresholding of the original view with threshold value of 100
    //filling expected view's upper half part with gray pixels of value 0
    //filling expected view's lower half part with gray pixels of value 255
    gil::fill_pixels(gil::subimage_view(gil::view(expected), 0, 0, original.width(),
        original.height() / 2), gil::gray8_pixel_t(0));
    gil::fill_pixels(gil::subimage_view(gil::view(expected), 0, original.height() / 2,
        original.width(), original.height() / 2), gil::gray8_pixel_t(255));

    gil::threshold_binary(gil::view(original), gil::view(threshold), 100);

    //comparing threshold view generated by the function with the expected view
    BOOST_TEST(gil::equal_pixels(gil::view(threshold), gil::view(expected)));

    /*-----------------------Threshold Binary with inverse----------------------------*/

    //expected view after thresholding of the original view with threshold value of 100
    //filling expected view's upper half part with gray pixels of value 200
    //filling expected view's lower half part with gray pixels of value 0
    gil::fill_pixels(gil::subimage_view(gil::view(expected), 0, 0, original.width(),
        original.height() / 2), gil::gray8_pixel_t(200));
    gil::fill_pixels(gil::subimage_view(gil::view(expected), 0, original.height() / 2,
        original.width(), original.height() / 2), gil::gray8_pixel_t(0));

    gil::threshold_binary
    (
        gil::view(original),
        gil::view(threshold),
        100,
        200,
        gil::threshold_direction::inverse
    );

    //comparing threshold view generated by the function with the expected view
    BOOST_TEST(gil::equal_pixels(gil::view(threshold), gil::view(expected)));

    return boost::report_errors();
}


#include <boost/numeric/odeint/stepper/generation/generation_runge_kutta_dopri5.hpp>


#include <tuple>
#include <Eigen/Core>
#include <npe.h>

npe_function(matrix_add)
npe_arg(b, npe_matches(a))
npe_arg(a, dense_double, dense_float)
npe_doc(R"(Add two matrices of the same type)")
npe_begin_code()

    npe_Matrix_a ret1 = a + b;

    return npe::move(ret1);

npe_end_code()

npe_function(matrix_add2)
npe_arg(a, dense_double, dense_float)
npe_arg(b, npe_matches(a))
npe_doc(R"(Add two matrices of the same type)")
npe_begin_code()

    npe_Matrix_a ret1 = a + b;

    return npe::move(ret1);

npe_end_code()

npe_function(matrix_add3)
npe_arg(a, dense_double, dense_float)
npe_arg(b, npe_matches(a))
npe_doc(R"(Add two matrices of the same type)")
npe_begin_code()

    npe_Matrix_a ret1 = a + b;

    return npe::move(ret1);

npe_end_code()

npe_function(matrix_add4)
npe_arg(a, dense_double, dense_float)
npe_arg(b, dense_double, dense_float)
npe_doc(R"(Add two matrices)")
npe_begin_code()

    npe_Matrix_a ret1 = a + b.template cast<npe_Scalar_a>();

    return npe::move(ret1);

npe_end_code()


// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright 2019-2021 Heal Research

#include <doctest/doctest.h>
#include <random>
#include <fmt/core.h>

#include "nanobench.h"

#include "core/types.hpp"

#include <Eigen/Core>

namespace nb = ankerl::nanobench;

namespace Operon {

TEST_CASE("Libm functions cost model")
{
    Operon::RandomGenerator rand(std::random_device{}());

    int rows = 1000, cols = 100;

    std::uniform_int_distribution<int> dist(0, cols-1);

    SUBCASE("double-precision") {
        std::uniform_real_distribution<double> uniformDouble(Operon::Numeric::Min<double>(), Operon::Numeric::Max<double>());
        Eigen::ArrayXd mDouble(rows, cols); mDouble.unaryExpr([&](double){ return uniformDouble(rand); });

        nb::Bench b;
        b.title("double-precision").relative(true).performanceCounters(true).minEpochIterations(1000);

        // binary
        b.run("+", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) += mDouble.col(dist(rand))); });
        b.run("-", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) -= mDouble.col(dist(rand))); });
        b.run("*", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) *= mDouble.col(dist(rand))); });
        b.run("/", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) /= mDouble.col(dist(rand))); });

        // unary
        b.run("exp", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).exp()); });
        b.run("log", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).log()); });
        b.run("sin", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).sin()); });
        b.run("cos", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).cos()); });
        b.run("tan", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).tan()); });
        b.run("sqrt", [&]() { nb::doNotOptimizeAway(mDouble.col(dist(rand)) = mDouble.col(dist(rand)).sqrt()); });
    }

    SUBCASE("single-precision") {
        std::uniform_real_distribution<float> uniformFloat(Operon::Numeric::Min<float>(), Operon::Numeric::Max<float>());
        Eigen::ArrayXf mFloat(rows, cols); mFloat.unaryExpr([&](float){ return uniformFloat(rand); });

        nb::Bench b;
        b.title("single-precision").relative(true).performanceCounters(true).minEpochIterations(1000);

        // binary
        b.run("+", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) += mFloat.col(dist(rand))); });
        b.run("-", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) -= mFloat.col(dist(rand))); });
        b.run("*", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) *= mFloat.col(dist(rand))); });
        b.run("/", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) /= mFloat.col(dist(rand))); });

        // unary
        b.run("exp", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).exp()); });
        b.run("log", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).log()); });
        b.run("sin", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).sin()); });
        b.run("cos", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).cos()); });
        b.run("tan", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).tan()); });
        b.run("sqrt", [&]() { nb::doNotOptimizeAway(mFloat.col(dist(rand)) = mFloat.col(dist(rand)).sqrt()); });
    }
}

}