// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Use, modification and distribution is 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)

#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_VORONOI_ADAPTOR_HPP
#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_VORONOI_ADAPTOR_HPP

#include <boost/geometry/extensions/triangulation/geometries/triangulation.hpp>
#include <boost/geometry/extensions/triangulation/algorithms/delaunay_triangulation.hpp>

namespace boost { namespace geometry
{

namespace model
{

template
<
    typename Triangulation
>
class voronoi_vertex_view {
public:
    typedef typename Triangulation::point_type point_type;
    typedef typename Triangulation::face_iterator face_iterator;
    voronoi_vertex_view(face_iterator f):
        m_p(strategy::delaunay_triangulation::detail::circumcircle_center
            <
                point_type,
                boost::geometry::point_order<typename Triangulation::face_type>::value == clockwise
            >(*f->begin(),
                *(f->begin()+1),
                *(f->begin()+2))),
        m_f(f) {}
    point_type const* point() const { return m_p; }
    face_iterator base() const { return m_f; }
    point_type m_p;
private:
    face_iterator m_f;
};

template
<
    typename Triangulation
>
class voronoi_face_view {
public:
    typedef typename Triangulation::point_type point_type;
    typedef typename Triangulation::vertex_iterator vertex_iterator;
    typedef typename Triangulation::face_iterator face_iterator;
    typedef typename std::vector<voronoi_vertex_view<Triangulation>>::const_iterator const_iterator;
    typedef typename std::vector<voronoi_vertex_view<Triangulation>>::iterator iterator;

    typedef voronoi_vertex_view<Triangulation> voronoi_vertex;
    voronoi_face_view(Triangulation& t, vertex_iterator v):m_v(v) {
        struct transforming_back_insert {
            transforming_back_insert(std::back_insert_iterator<std::vector<voronoi_vertex>> bi):
                m_underlying(bi) {};
            std::back_insert_iterator<std::vector<voronoi_vertex>> m_underlying;
            transforming_back_insert& operator*() {return *this;}
            transforming_back_insert& operator++() {return *this;}
            transforming_back_insert& operator++(int) {return *this;}
            transforming_back_insert& operator=(face_iterator const& f)
            {
                m_underlying = voronoi_vertex(f);
                return *this;
            }
        };
        vertex_incident_faces(t, m_v, transforming_back_insert(std::back_inserter(m_voronoi_v)));
    }
    const_iterator begin() const { return m_voronoi_v.begin(); }
    const_iterator end() const { return m_voronoi_v.end(); }
private:
    std::vector<voronoi_vertex> m_voronoi_v;
    vertex_iterator m_v;
};

} // namespace model

#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS
namespace traits
{

template<typename Triangulation>
struct dimension< model::voronoi_vertex_view<Triangulation> > : boost::mpl::int_<2> {};

template<typename Triangulation>
struct tag< model::voronoi_vertex_view<Triangulation> >
{ typedef point_tag type; };

template<typename Triangulation>
struct coordinate_type< model::voronoi_vertex_view<Triangulation> >
{ typedef typename coordinate_type<typename Triangulation::point_type>::type type; };

template<typename Triangulation>
struct coordinate_system< model::voronoi_vertex_view<Triangulation> >
{ typedef typename coordinate_system<typename Triangulation::point_type>::type type; };

template<typename Triangulation, std::size_t Dimension>
struct access<model::voronoi_vertex_view<Triangulation>, Dimension>
{
    static typename coordinate_type<typename Triangulation::point_type>::type
        get(model::voronoi_vertex_view<Triangulation> const& p)
    {
        return boost::geometry::get<Dimension>(p.m_p);
    }
};

template<typename Triangulation> struct tag<model::voronoi_face_view<Triangulation>>
{ typedef ring_tag type; };

template<typename Triangulation> struct point_order<model::voronoi_face_view<Triangulation>>
{ static const order_selector value = counterclockwise; };

template<typename Triangulation>
struct closure<model::voronoi_face_view<Triangulation>>
{
    static const closure_selector value = open;
};

} // namespace traits
#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_GEOMETRIES_VORONOI_ADAPTOR_HPP


#include <scitbx/array_family/boost_python/flex_fwd.h>

#include <boost/python/def.hpp>
#include <boost/python/args.hpp>

#include <scitbx/matrix/tests.h>

namespace scitbx { namespace matrix { namespace boost_python {

    void wrap_matrix() {
      using namespace boost::python;
      double epsilon = std::numeric_limits<double>::epsilon();
      def("matrix_normality_ratio", normality_ratio<double>, (
        arg("a"), arg("epsilon")=epsilon));
      def("matrix_equality_ratio", equality_ratio<double>, (
        arg("a"), arg("b"), arg("epsilon")=epsilon));
      def("matrix_cholesky_test_ratio", cholesky_test_ratio<double>, (
        arg("a"), arg("x"), arg("b"), arg("epsilon")=epsilon));
    }

}}}


#include <igl/read_triangle_mesh.h>
#include <igl/loop.h>
#include <igl/upsample.h>
#include <igl/false_barycentric_subdivision.h>
#include <igl/opengl/glfw/Viewer.h>
#include <Eigen/Core>
#include <iostream>

#include "tutorial_shared_path.h"

int main(int argc, char * argv[])
{
  using namespace std;
  using namespace igl;
  Eigen::MatrixXi OF,F;
  Eigen::MatrixXd OV,V;
  bool show_swept_volume = false;
  read_triangle_mesh(
      TUTORIAL_SHARED_PATH "/decimated-knight.off",OV,OF);
  V = OV;
  F = OF;
  cout<<R"(Usage:
1  Restore Original mesh
2  Apply In-plane upsampled mesh
3  Apply Loop subdivided mesh
4  Apply False barycentric subdivision
)";
  igl::opengl::glfw::Viewer viewer;
  viewer.data().set_mesh(V,F);
  viewer.data().set_face_based(true);

  viewer.callback_key_down =
    [&](igl::opengl::glfw::Viewer & viewer, unsigned char key, int mod)->bool
    {
      switch(key)
      {
        default:
          return false;
        case '1':
        {
          V = OV;
          F = OF;
          break;
        }
        case '2':
        {
          igl::upsample( Eigen::MatrixXd(V), Eigen::MatrixXi(F), V,F);
          break;
        }
        case '3':
        {
          igl::loop( Eigen::MatrixXd(V), Eigen::MatrixXi(F), V,F);
          break;
        }
        case '4':
        {
          igl::false_barycentric_subdivision(
            Eigen::MatrixXd(V),Eigen::MatrixXi(F),V,F);
          break;
        }
      }
      viewer.data().clear();
      viewer.data().set_mesh(V,F);
      viewer.data().set_face_based(true);
      return true;
    };
  viewer.launch();
}


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <armadillo>
#include <random>

#include <mpi.h>

#include "transactions.hh"
#include "histograms.hh"

using namespace std;
using namespace arma;

int main(int argc, char **argv) {
  int mpiSize, mpiRank;

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
  MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);

  if(argc <= 6) {
    if(mpiRank == 0) {
      fprintf(stderr, "Usage: %s OUTPUT-FILE NUM-AGENTS NUM-TRANSACTIONS NUM-RUNS INITIAL-MONEY [LAMBDA] [ALPHA] [S-FACTOR]\n", argv[0]);
    }
    MPI_Finalize();
    exit(0);
  }

  // read parameters
  const char *filename;
  int N;      // number of agents
  int K;      // number of transactions
  int R;      // number of runs
  double m0;  // initial money for each agent
  double l;   // lambda parameter
  double a;   // alpha parameter
  double S;   // "S-factor", affects accuracy and slowness of simulations with α≠0 or γ≠0 (default: 1.0)
  if(mpiRank == 0) {
    // mandatory arguments
    filename = argv[1];
    N = atoi(argv[2]);
    K = atoi(argv[3]);
    R = atoi(argv[4]);
    m0 = atof(argv[5]);

    // optional arguments
    l  = (argc > 6 ? atof(argv[6]) : 0.0);
    a  = (argc > 7 ? atof(argv[7]) : 0.0);
    S  = (argc > 8 ? atof(argv[8]) : 1.0);
  }

  if(mpiRank == 0) {
    fprintf(stderr, "Running with parameters:\n");
    fprintf(stderr, "  N = %d\n", N);
    fprintf(stderr, "  K = %d\n", K);
    fprintf(stderr, "  R = %d\n", R);
    fprintf(stderr, " m0 = %.3f\n", m0);
    fprintf(stderr, "  λ = %.3f\n", l);
    fprintf(stderr, "  α = %.3f\n", a);
    fprintf(stderr, "  S = %.1f\n", S);
  }

  // broadcast parameters to all nodes
  MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&K, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&R, 1, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&m0, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&l, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&a, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
  MPI_Bcast(&S, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

  // reseed simulation with true random seed
  // (so that the different processes don't make the exact same simulations)
  seed_simulation();

  // list of all agents after each run
  vector<double> m(N * R, 1);

  // find start and end indices for range of runs to be done
  // by this process
  size_t runsPerProcess = R / mpiSize;
  size_t startRun = mpiRank * runsPerProcess;
  size_t endRun = startRun + runsPerProcess;
  if(endRun > R) endRun = R;
  if(mpiRank == mpiSize - 1 && endRun < R) endRun = R;

  // do all runs (parallelized)
  for(size_t r = startRun; r < endRun; r++) {
    // simulate transactions
    // (store resulting money distribution in a subvector)
    vector<double> m_dist = simulate_transactions(N, K, m0, l, S, a);

    copy(begin(m_dist), end(m_dist), begin(m) + (r * N));
  }

  // send all data to process #0
  double *mptr = m.data();
  if(mpiRank == 0) {
    for(int p = 1; p < mpiSize; p++) {
      size_t pStartRun = p * runsPerProcess;
      size_t pEndRun   = pStartRun + runsPerProcess;
      if(pEndRun > R) pEndRun = R;
      if(p == mpiSize - 1 && pEndRun < R) pEndRun = R;

      MPI_Recv(&mptr[N * pStartRun], N * (pEndRun - pStartRun), MPI_DOUBLE, p, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    }
  } else {
    MPI_Send(&mptr[N * startRun], N * (endRun - startRun), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
  }

  // save histogram data to file (for plotting)
  if(mpiRank == 0)
    save_histogram(m, 0.01, filename);

  MPI_Finalize();
  return 0;
}