/*===========================================================================*\

Author: Matthias W. Smith
Email:  mwsmith2@uw.edu
Date:   11/02/14

Detail: This is a new test program for my Fid libraries

\*===========================================================================*/

//--- std includes ----------------------------------------------------------//
#include <iostream>
#include <fstream>
#include <vector>

//--- other includes --------------------------------------------------------//
#include <armadillo>

//--- project includes ------------------------------------------------------//
#include "fid.h"

using std::vector;
using std::endl;
using namespace fid;

int main(int argc, char** argv)
{
  std::ofstream out;
  out.precision(10);

  // declare variables
  int fid_length = sim::num_samples;
  double ti = -1.0;
  double dt = 0.001;
  double ftruth = 23.0;

  vector<double> wf(0.0, fid_length);
  vector<double> tm(0.0, fid_length);

  tm = construct_range(ti, ti + fid_length * dt, dt);

  FidFactory ff;
  ff.SetFidFreq(ftruth);
  ff.IdealFid(wf, tm);

  Fid my_fid(wf, tm);

  my_fid.GetFreq("lz");
  my_fid.GetFreq("ph");

  my_fid.DiagnosticInfo();
  my_fid.DiagnosticPlot("test/");

  return 0;
}


/**
 *  @copyright Copyright 2016 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetSimplePhysSignalReco.cpp
 */

#include <math.h>
#include <cassert>
#include "JPetSimplePhysSignalReco.h"
#include "./JPetWriter/JPetWriter.h"
#include "HelperMathFunctions.h"
#include <boost/property_tree/json_parser.hpp>

using namespace boost::numeric::ublas;

JPetSimplePhysSignalReco::JPetSimplePhysSignalReco():
  fAlpha(1),
  fThresholdSel(-1)
{
  readConfigFileAndSetAlphaAndThreshParams("configParams.json");
}

JPetSimplePhysSignalReco::~JPetSimplePhysSignalReco()
{
  /**/
}

bool JPetSimplePhysSignalReco::exec()
{
  return true;
}

void JPetSimplePhysSignalReco::savePhysSignal(JPetPhysSignal)
{
}

JPetPhysSignal JPetSimplePhysSignalReco::createPhysSignal(JPetRecoSignal& recoSignal)
{
  // create a Phys Signal
  JPetPhysSignal physSignal;

  // use the values from Reco Signal to set the physical properties of signal
  // here, a dummy example - much more sophisticated procedures should go here
  physSignal.setPhe( recoSignal.getCharge() * 1.0 + 0.0 );
  physSignal.setQualityOfPhe(1.0);

  // in the previous module (C2) we have reconstructed one time at arbitrary
  // threshold - now we retrieve it by getting a map of times vs. thresholds,
  // and taking its first (and only) element by the begin() iterator. We get
  // an std::pair, where first is the threshold value, and second is time.
  double time = recoSignal.getRecoTimesAtThreshold().begin()->second;

  // -----------------------------------------------------------
  //
  // Estimate the time of the signal based on raw signal samples
  JPetRawSignal rawSignal = recoSignal.getRawSignal();

  if (rawSignal.getNumberOfPoints(JPetSigCh::Leading) >= 2
      && rawSignal.getNumberOfPoints(JPetSigCh::Trailing) >= 2) {
    // get number of points on leading edge
    int iNumPoints = rawSignal.getNumberOfPoints(JPetSigCh::Leading);

    std::vector<JPetSigCh> leadingPoints = rawSignal.getPoints(
        JPetSigCh::Leading, JPetRawSignal::ByThrValue);

    // create vectors
    vector<float> vecTime(iNumPoints);
    vector<float> vecVolt(iNumPoints);

    for (int j = 0; j < iNumPoints; j++) {
      vecTime(j) = leadingPoints.at(j).getValue();
      vecVolt(j) = leadingPoints.at(j).getThreshold();
    }

    // To evaluate time below parameters should be specified:

    // the parameter alfa of the below expression need to be specified:
    // vecVolt = a(t - vecTime)^(alfa);
    // Here alfa is fixed for the linear case.
    // Caution! alfa has to be an integer and positive value, negative values are ignored.
    int alfa = getAlpha();

    // thr_sel specifies the threshold level to read the time value,
    // and with thr_sel the below equation may be solved:
    // thr_sel = a(t - time)^(alfa);
    // Caution! thr_sel has to negative, and for positive values the program will set thr_sel equal to 0.
    float thr_sel = getThresholdSel();

    // The evaluation of time based on alfa and thr_sel
    assert(thr_sel < 0);
    assert(alfa > 0);
    time = static_cast<double>(polynomialFit(vecTime, vecVolt, alfa, thr_sel));
  }
  // ------------------------------------------------------------

  physSignal.setTime(time);
  physSignal.setQualityOfTime(1.0);

  // store the original JPetRecoSignal in the PhysSignal as a processing history
  physSignal.setRecoSignal(recoSignal);
  return physSignal;
}

void JPetSimplePhysSignalReco::readConfigFileAndSetAlphaAndThreshParams(const char* filename)
{
  boost::property_tree::ptree content;
  try {
    read_json(filename, content);
    int alpha = content.get<int>("alpha");
    float thresholdSel = content.get<float>("thresholdSel");
    setAlpha(alpha);
    setThresholdSel(thresholdSel);
  } catch (const std::runtime_error& error) {
    std::string message = "Error opening config file. Error = " + std::string(error.what());
    std::cerr << message << std::endl;
  }
}


/*
 * $Revision: 477 $ $Date: 2010-08-31 11:49:37 -0700 (Tue, 31 Aug 2010) $
 *
 * Copyright by Astos Solutions GmbH, Germany
 *
 * this file is published under the Astos Solutions Free Public License
 * For details on copyright and terms of use see
 * http://www.astos.de/Astos_Solutions_Free_Public_License.html
 */

#include "PlaneGeometry.h"
#include "RenderContext.h"
#include "IntegerTypes.h"
#include <Eigen/Geometry>
#include <algorithm>
#include <cmath>

using namespace vesta;
using namespace Eigen;
using namespace std;

PlaneGeometry::PlaneGeometry() :
    m_scale(1.0),
    m_color(0.5f, 0.5f, 0.5f),
    m_opacity(0.2f),
    m_gridLineSpacing(0.0),
    m_grid(0),
    m_solidPlane(0),
    m_vertices(0)
{
    buildGeometry();
}

PlaneGeometry::~PlaneGeometry()
{
    delete m_grid;
    delete m_solidPlane;
    delete m_vertices;
}

void
PlaneGeometry::render(RenderContext& rc,
                      double /* clock */) const
{
    Material material;
    material.setEmission(m_color);

    rc.bindVertexArray(m_vertices);

    rc.pushModelView();
    rc.scaleModelView(Vector3f::Constant((float) m_scale));
    // rc.rotateModelView(m_orientation);

    if (rc.pass() == RenderContext::OpaquePass)
    {
        rc.bindMaterial(&material);
        rc.drawPrimitives(*m_grid);
    }
    else
    {
        material.setOpacity(m_opacity);
        rc.bindMaterial(&material);
        rc.drawPrimitives(*m_solidPlane);
    }

    rc.unbindVertexArray();

    rc.popModelView();
}

float
PlaneGeometry::boundingSphereRadius() const
{
    return float(std::sqrt(2.0) * m_scale);
}

bool
PlaneGeometry::isOpaque() const
{
    return m_opacity >= 1.0f;
}

/** Set the spacing between grid lines in units of kilometers. The spacing
  * for vertical and horizontal grid lines is identical. If the spacing is
  * set to zero, no grid lines will be shown.
  */
void
PlaneGeometry::setGridLineSpacing(double gridLineSpacing)
{
    m_gridLineSpacing = gridLineSpacing;
    delete m_grid;
    delete m_solidPlane;
    delete m_vertices;
    m_grid = 0;
    m_solidPlane = 0;
    m_vertices = 0;
    buildGeometry();
}

void
PlaneGeometry::buildGeometry()
{
    unsigned int gridSubdivision = 0;
    double unscaledSpacing = 0.0;
    double bottomLeft = 0.0;

    if (m_gridLineSpacing > 0.0)
    {
        const unsigned int subdivisionPerQuadrant = (unsigned int) floor(m_scale / m_gridLineSpacing) + 1;
        unscaledSpacing = m_gridLineSpacing / m_scale;
        gridSubdivision = 1 + 2 * subdivisionPerQuadrant;
        bottomLeft = subdivisionPerQuadrant * -unscaledSpacing;
    }
    else
    {
        unscaledSpacing = 2.0;
        gridSubdivision = 2;
        bottomLeft = -1.0;
    }

    const unsigned int vertexCount = (gridSubdivision + 1) * 4;

    // Vertex data must be allocated as char, not Vector3; Eigen uses a custom allocator for
    // objects.
    unsigned char* byteData = new unsigned char[vertexCount * sizeof(Vector3f)];
    Vector3f* data = reinterpret_cast<Vector3f*>(byteData);
    int vertexIndex = 0;

    // Top and bottom
    for (unsigned int i = 0; i <= gridSubdivision; ++i)
    {
        float x = float(i * unscaledSpacing + bottomLeft);
        x = max(-1.0f, min(1.0f, x));
        data[vertexIndex++] = Vector3f(x, -1.0f, 0.0f);
        data[vertexIndex++] = Vector3f(x,  1.0f, 0.0f);
    }

    // Left and right
    for (unsigned int i = 0; i <= gridSubdivision; ++i)
    {
        float y = float(i * unscaledSpacing + bottomLeft);
        y = max(-1.0f, min(1.0f, y));
        data[vertexIndex++] = Vector3f(-1.0f, y, 0.0f);
        data[vertexIndex++] = Vector3f( 1.0f, y, 0.0f);
    }

    m_vertices = new VertexArray(data, vertexCount, VertexSpec::Position, sizeof(data[0]));

    m_grid = new PrimitiveBatch(PrimitiveBatch::Lines, (gridSubdivision + 1) * 2, 0);

    // 4 triangles because we want to draw both the front and back of the
    // plane.
    v_uint16* indices = new v_uint16[12];
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = gridSubdivision * 2 + 1;

    indices[3] = 0;
    indices[4] = gridSubdivision * 2 + 1;
    indices[5] = gridSubdivision * 2;

    indices[6] = gridSubdivision * 2 + 1;
    indices[7] = 1;
    indices[8] = 0;

    indices[9] = gridSubdivision * 2;
    indices[10] = gridSubdivision * 2 + 1;
    indices[11] = 0;

    m_solidPlane = new PrimitiveBatch(PrimitiveBatch::Triangles, indices, 4);
    delete[] indices;
}


#include <iostream>
#include <functional>
#include <sstream>
#include <stdlib.h>

#include <boost/random.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>

#include "biggles/sampling/gibbs.hpp"

extern "C" {
#include <ccan/tap/tap.h>
}

using namespace biggles::sampling;

class uniform_sampler : public gibbs::mem_fun_two_var_cond_sampler<uniform_sampler, int, float>
{
public:
    uniform_sampler()
        : uni_int_(1,6)
        , uni_real_(10.f,20.f)
    { }

    int sample_variable_1(float)
    {
        return uni_int_(rng_);
    }

    float sample_variable_2(int)
    {
        return uni_real_(rng_);
    }

protected:
    boost::mt19937 rng_;
    boost::uniform_int<> uni_int_;
    boost::uniform_real<> uni_real_;
};

int main(int argc, char** argv)
{
    plan_tests(4*20);

    // uniform independent sampler
    uniform_sampler es;
    gibbs_sampler<uniform_sampler> s(es);
    for(int i=0; i<20; ++i)
    {
        std::stringstream ss;
        ss << "drawn sample: " << s.draw();
        diag("%s", ss.str().c_str());

        ok(boost::get<0>(s.last_sample()) <= 6, "int sample is not too big");
        ok(boost::get<0>(s.last_sample()) >= 1, "int sample is not too small");

        ok(boost::get<1>(s.last_sample()) <= 20.f, "float sample is not too big");
        ok(boost::get<1>(s.last_sample()) >= 10.f, "float sample is not too small");
    }

    return exit_status();
}


/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Sun 27 Oct 09:02:32 2013
 *
 * @brief Binomial distributions (with integers or floating point numbers)
 */

#define BOB_CORE_RANDOM_MODULE
#include <bob.core/random_api.h>
#include <bob.blitz/cppapi.h>
#include <bob.blitz/cleanup.h>
#include <bob.extension/documentation.h>

#include <boost/make_shared.hpp>
#include <bob.core/random.h>

static auto binomial_doc = bob::extension::ClassDoc(
  BOB_EXT_MODULE_PREFIX ".binomial",
  "Models a random binomial distribution",
  "This distribution produces random numbers :math:`x` distributed with the probability density function\n\n"
  ".. math::\n\n   {{t}\\choose{k}}p^k(1-p)^{t-k}\n\n"
  "where ``t`` and ``p`` are parameters of the distribution.\n\n"
  ".. warning::\n\n"
  "   This distribution requires that :math:`t >= 0` and that :math:`0 <= p <= 1`."
)
.add_constructor(bob::extension::FunctionDoc(
  "binomial",
  "Creates a new binomial distribution object"
)
.add_prototype("dtype, [t], [p]", "")
.add_parameter("dtype", ":py:class:`numpy.dtype`", "The data type for the drawn random numbers; only integral types are supported")
.add_parameter("t", "float", "[Default: ``1.``] The :math:`t` parameter of the binomial distribution")
.add_parameter("p", "float", "[Default: ``0.5``] The :math:`p` parameter of the binomial distribution")
);

/* How to create a new PyBoostBinomialObject */
static PyObject* PyBoostBinomial_New(PyTypeObject* type, PyObject*, PyObject*) {

  /* Allocates the python object itself */
  PyBoostBinomialObject* self = (PyBoostBinomialObject*)type->tp_alloc(type, 0);
  self->type_num = NPY_NOTYPE;
  self->distro.reset();

  return Py_BuildValue("N", self);
}

/* How to delete a PyBoostBinomialObject */
static void PyBoostBinomial_Delete (PyBoostBinomialObject* o) {
  o->distro.reset();
  Py_TYPE(o)->tp_free((PyObject*)o);
}

template <typename T>
boost::shared_ptr<void> make_binomial(PyObject* t, PyObject* p) {
  T ct = 1.;
  if (t) ct = PyBlitzArrayCxx_AsCScalar<T>(t);
  if (ct < 0) {
    PyErr_SetString(PyExc_ValueError, "parameter t must be >= 0");
    return boost::shared_ptr<void>();
  }
  T cp = 0.5;
  if (p) cp = PyBlitzArrayCxx_AsCScalar<T>(p);
  if (cp < 0.0 || cp > 1.0) {
    PyErr_SetString(PyExc_ValueError, "parameter p must lie in the interval [0.0, 1.0]");
    return boost::shared_ptr<void>();
  }
  return boost::make_shared<bob::core::random::binomial_distribution<int64_t,T>>(ct, cp);
}

PyObject* PyBoostBinomial_SimpleNew (int type_num, PyObject* t, PyObject* p) {
BOB_TRY
  PyBoostBinomialObject* retval = (PyBoostBinomialObject*)PyBoostBinomial_New(&PyBoostBinomial_Type, 0, 0);
  if (!retval) return 0;
  auto retval_ = make_safe(retval);

  retval->type_num = type_num;

  switch(type_num) {
    case NPY_FLOAT32:
      retval->distro = make_binomial<float>(t, p);
      break;
    case NPY_FLOAT64:
      retval->distro = make_binomial<double>(t, p);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot create %s(T) with T having an unsupported numpy type number of %d (it only supports numpy.float32 or numpy.float64)", Py_TYPE(retval)->tp_name, retval->type_num);
      return 0;
  }

  if (!retval->distro) { // a problem occurred
    return 0;
  }

  return Py_BuildValue("O", retval);
BOB_CATCH_FUNCTION("SimpleNew", 0)
}

/* Implements the __init__(self) function */
static int PyBoostBinomial_Init(PyBoostBinomialObject* self, PyObject *args, PyObject* kwds) {
BOB_TRY
  char** kwlist = binomial_doc.kwlist();

  PyObject* t = 0;
  PyObject* p = 0;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|OO", kwlist, &PyBlitzArray_TypenumConverter, &self->type_num, &t, &p)) return -1; ///< FAILURE

  switch(self->type_num) {
    case NPY_FLOAT32:
      self->distro = make_binomial<float>(t, p);
      break;
    case NPY_FLOAT64:
      self->distro = make_binomial<double>(t, p);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot create %s(T) with T having an unsupported numpy type number of %d (it only supports numpy.float32 or numpy.float64)", Py_TYPE(self)->tp_name, self->type_num);
      return -1;
  }

  if (!self->distro) { // a problem occurred
    return -1;
  }

  return 0; ///< SUCCESS
BOB_CATCH_MEMBER("constructor", -1)
}

int PyBoostBinomial_Check(PyObject* o) {
  if (!o) return 0;
  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBoostBinomial_Type));
}

int PyBoostBinomial_Converter(PyObject* o, PyBoostBinomialObject** a) {
  if (!PyBoostBinomial_Check(o)) return 0;
  Py_INCREF(o);
  (*a) = reinterpret_cast<PyBoostBinomialObject*>(o);
  return 1;
}

static auto t_doc = bob::extension::VariableDoc(
  "t",
  "float",
  "The parameter ``t`` of the distribution"
);
template <typename T> PyObject* get_t(PyBoostBinomialObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<bob::core::random::binomial_distribution<int64_t,T>>(self->distro)->t());
}

static PyObject* PyBoostBinomial_GetT(PyBoostBinomialObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_FLOAT32:
      return get_t<float>(self);
    case NPY_FLOAT64:
      return get_t<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot get parameter `t` of %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("t", 0)
}

static auto p_doc = bob::extension::VariableDoc(
  "p",
  "float",
  "The parameter ``p`` of the distribution"
);
template <typename T> PyObject* get_p(PyBoostBinomialObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<bob::core::random::binomial_distribution<int64_t,T>>(self->distro)->p());
}

static PyObject* PyBoostBinomial_GetP(PyBoostBinomialObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_FLOAT32:
      return get_p<float>(self);
    case NPY_FLOAT64:
      return get_p<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot get parameter `p` of %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("p", 0)
}

static auto dtype_doc = bob::extension::VariableDoc(
  "dtype",
  ":py:class:`numpy.dtype`",
  "The type of scalars produced by this binomial distribution"
);
static PyObject* PyBoostBinomial_GetDtype(PyBoostBinomialObject* self) {
BOB_TRY
  return reinterpret_cast<PyObject*>(PyArray_DescrFromType(self->type_num));
BOB_CATCH_MEMBER("dtype", 0)
}

static PyGetSetDef PyBoostBinomial_getseters[] = {
    {
      dtype_doc.name(),
      (getter)PyBoostBinomial_GetDtype,
      0,
      dtype_doc.doc(),
      0,
    },
    {
      t_doc.name(),
      (getter)PyBoostBinomial_GetT,
      0,
      t_doc.doc(),
      0,
    },
    {
      p_doc.name(),
      (getter)PyBoostBinomial_GetP,
      0,
      p_doc.doc(),
      0,
    },
    {0}  /* Sentinel */
};

static auto reset_doc = bob::extension::FunctionDoc(
  "reset",
  "Resets this distribution",
  "After calling this method, subsequent uses of the distribution do not depend on values produced by any random number generator prior to invoking reset",
  true
)
.add_prototype("")
;
template <typename T> PyObject* reset(PyBoostBinomialObject* self) {
  boost::static_pointer_cast<bob::core::random::binomial_distribution<int64_t,T>>(self->distro)->reset();
  Py_RETURN_NONE;
}

static PyObject* PyBoostBinomial_Reset(PyBoostBinomialObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_FLOAT32:
      return reset<float>(self);
    case NPY_FLOAT64:
      return reset<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot reset %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("reset", 0)
}

static auto call_doc = bob::extension::FunctionDoc(
  "draw",
  "Draws one random number from this distribution using the given ``rng``",
  ".. note:: The :py:meth:`__call__` function is a synonym for this ``draw``.",
  true
)
.add_prototype("rng", "value")
.add_parameter("rng", ":py:class:`mt19937`", "The random number generator to use")
.add_return("value", "dtype", "A random value that follows the binomial distribution")
;
template <typename T> PyObject* call(PyBoostBinomialObject* self, PyBoostMt19937Object* rng) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<bob::core::random::binomial_distribution<int64_t,T>>(self->distro)->operator()(*rng->rng));
}

static PyObject* PyBoostBinomial_Call(PyBoostBinomialObject* self, PyObject *args, PyObject* kwds) {
BOB_TRY
  char** kwlist = call_doc.kwlist();

  PyBoostMt19937Object* rng;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PyBoostMt19937_Type, &rng)) return 0; ///< FAILURE

  switch(self->type_num) {
    case NPY_FLOAT32:
      return call<float>(self, rng);
      break;
    case NPY_FLOAT64:
      return call<double>(self, rng);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot call %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
  }

  return 0; ///< FAILURE
BOB_CATCH_MEMBER("call", 0)
}

static PyMethodDef PyBoostBinomial_methods[] = {
    {
      call_doc.name(),
      (PyCFunction)PyBoostBinomial_Call,
      METH_VARARGS|METH_KEYWORDS,
      call_doc.doc(),
    },
    {
      reset_doc.name(),
      (PyCFunction)PyBoostBinomial_Reset,
      METH_NOARGS,
      reset_doc.doc(),
    },
    {0}  /* Sentinel */
};

extern PyObject* scalar_to_bytes(PyObject* s);

/**
 * String representation and print out
 */
static PyObject* PyBoostBinomial_Repr(PyBoostBinomialObject* self) {
BOB_TRY
  PyObject* st = scalar_to_bytes(PyBoostBinomial_GetT(self));
  if (!st) return 0;
  auto st_ = make_safe(st);
  PyObject* sp = scalar_to_bytes(PyBoostBinomial_GetP(self));
  if (!sp) return 0;
  auto sp_ = make_safe(sp);

  return
    PyString_FromFormat
      (
       "%s(dtype='%s', t=%s, p=%s)",
       Py_TYPE(self)->tp_name, PyBlitzArray_TypenumAsString(self->type_num),
       PyString_AS_STRING(st), PyString_AS_STRING(sp)
      );
BOB_CATCH_MEMBER("repr", 0)
}

PyTypeObject PyBoostBinomial_Type = {
  PyVarObject_HEAD_INIT(0,0)
  0
};

bool init_BoostBinomial(PyObject* module)
{
  // initialize the type struct
  PyBoostBinomial_Type.tp_name = binomial_doc.name();
  PyBoostBinomial_Type.tp_basicsize = sizeof(PyBoostBinomialObject);
  PyBoostBinomial_Type.tp_flags = Py_TPFLAGS_DEFAULT;
  PyBoostBinomial_Type.tp_doc = binomial_doc.doc();
  PyBoostBinomial_Type.tp_str = reinterpret_cast<reprfunc>(PyBoostBinomial_Repr);
  PyBoostBinomial_Type.tp_repr = reinterpret_cast<reprfunc>(PyBoostBinomial_Repr);

  // set the functions
  PyBoostBinomial_Type.tp_new = PyBoostBinomial_New;
  PyBoostBinomial_Type.tp_init = reinterpret_cast<initproc>(PyBoostBinomial_Init);
  PyBoostBinomial_Type.tp_dealloc = reinterpret_cast<destructor>(PyBoostBinomial_Delete);
  PyBoostBinomial_Type.tp_methods = PyBoostBinomial_methods;
  PyBoostBinomial_Type.tp_getset = PyBoostBinomial_getseters;
  PyBoostBinomial_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBoostBinomial_Call);

  // check that everything is fine
  if (PyType_Ready(&PyBoostBinomial_Type) < 0) return false;

  // add the type to the module
  return PyModule_AddObject(module, "binomial", Py_BuildValue("O", &PyBoostBinomial_Type)) >= 0;
}


#include <boost/test/unit_test.hpp>

#include "example.h"

BOOST_AUTO_TEST_CASE( subtract )
{
  auto res = subtract_numbers(1.0, 2.0);
  BOOST_TEST(res == -1.0);
}


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

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

// Contributed and/or modified by Tinko Bartels,
//   as part of Google Summer of Code 2020 program.

// 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_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_ALMOST_STATIC_FILTER_HPP
#define BOOST_GEOMETRY_EXTENSIONS_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_ALMOST_STATIC_FILTER_HPP

#include <array>
#include <algorithm>
#include <limits>

#include <boost/geometry/extensions/generic_robust_predicates/strategies/cartesian/detail/expression_tree.hpp>

namespace boost { namespace geometry
{

namespace detail { namespace generic_robust_predicates
{

template <typename Filter, std::size_t N, std::size_t End>
struct make_filter_impl
{
    template <typename ExtremaArray, typename ...Reals>
    static Filter apply(const ExtremaArray& extrema, const Reals&... args)
    {
        return make_filter_impl<Filter, N + 1, End>
            ::apply(extrema, args..., extrema[N]);
    }
};

template <typename Filter, std::size_t End>
struct make_filter_impl<Filter, End, End>
{
    template <typename ExtremaArray, typename ...Reals>
    static Filter apply(const ExtremaArray& extrema, const Reals&... args)
    {
        Filter f(args...);
        return f;
    }
};

template
<
    typename Expression,
    typename CalculationType,
    typename StaticFilter
>
class almost_static_filter
{
private:
    using expression_max_argn = max_argn<Expression>;
    using ct = CalculationType;
    using extrema_array = std::array<ct, 2 * expression_max_argn::value>;
    extrema_array m_extrema;
    StaticFilter m_filter;
public:
    const StaticFilter& filter() const { return m_filter; }
    inline almost_static_filter()
    {
        std::fill(m_extrema.begin(),
                  m_extrema.begin() + m_extrema.size() / 2,
                  -std::numeric_limits<ct>::infinity());
        std::fill(m_extrema.begin() + m_extrema.size() / 2,
                  m_extrema.end(),
                  std::numeric_limits<ct>::infinity());
    }
    template <typename ...Reals>
    int apply(const Reals&... args) const
    {
        return m_filter.apply(args...);
    }

    template <typename ...Reals>
    inline void update_extrema(const Reals&... args)
    {
        std::array<ct, sizeof...(Reals)> input {{ static_cast<ct>(args)... }};
        for(int i = 0; i < m_extrema.size() / 2; ++i)
        {
            m_extrema[i] = std::max(m_extrema[i], input[i]);
        }
        for(int i = m_extrema.size() / 2; i < m_extrema.size(); ++i)
        {
            m_extrema[i] = std::min(m_extrema[i], input[i]);
        }
    }

    template <typename ...Reals>
    inline bool update_extrema_check(const Reals&... args)
    {
        bool changed = false;
        std::array<ct, sizeof...(Reals)> input {{ static_cast<ct>(args)... }};
        for(int i = 0; i < m_extrema.size() / 2; ++i)
        {
            if(input[i] > m_extrema[i])
            {
                changed = true;
                m_extrema[i] = input[i];
            }
        }
        for(int i = m_extrema.size() / 2; i < m_extrema.size(); ++i)
        {
            if(input[i] < m_extrema[i])
            {
                changed = true;
                m_extrema[i] = input[i];
            }
        }
        return changed;
    }

    inline void update_filter()
    {
        m_filter = make_filter_impl
                <
                    StaticFilter,
                    0,
                    2 * expression_max_argn::value
                >::apply(m_extrema);
    }
};

}} // namespace detail::generic_robust_predicates

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_ALMOST_STATIC_FILTER_HPP


#include "BlocksEngine/pch.h"
#include "BlocksEngine/Core/Components/Camera.h"

#include <boost/bind.hpp>

#include "BlocksEngine/Core/Actor.h"
#include "BlocksEngine/Core/Transform.h"
#include "BlocksEngine/Main/Game.h"

using namespace BlocksEngine;

Matrix Camera::WorldViewProjection() const noexcept
{
    return wvp_;
}

Matrix Camera::WorldView() const noexcept
{
    const Vector3 position = GetTransform()->GetPosition();
    const Matrix rotation = Matrix::CreateFromQuaternion(GetTransform()->GetOrientation());

    Vector3 target = Vector3<float>::Transform(Vector3<float>::Forward, rotation);
    target.Normalize();

    return Matrix::CreateLookAt(position, position + target, Vector3<float>::Up);
}

Matrix Camera::Projection() const noexcept
{
    return projection_;
}

void Camera::OnWindowResized(const int width, const int height) noexcept
{
    projection_ = std::move(CalculateProjection());
}

void Camera::Update()
{
    worldView_ = WorldView();
    wvp_ = worldView_ * projection_;
}

void Camera::Start()
{
    SetEventTypes(EventType::Update);

    windowResizedConnection_ = GetGame()->Window().AddSignalWindowResized(
        bind(&Camera::OnWindowResized, this, _1, _2)
    );
    projection_ = CalculateProjection();
}

float Camera::ClampAngle(float angle, const float min, const float max) const
{
    if (angle < -360.0f) angle += 360.0f;
    if (angle > 360.0f) angle -= 360.0f;
    return std::clamp(angle, min, max);
}

DirectX::XMMATRIX Camera::CalculateProjection() const noexcept
{
    return DirectX::XMMatrixPerspectiveFovLH(1.0f, GetGame()->Graphics().AspectRatio(), 0.3f, 1000.0f);
}


/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <test/cnumeric.hpp>

#include <boost/hana/assert.hpp>
#include <boost/hana/comparable.hpp>
#include <boost/hana/core/operators.hpp>
#include <boost/hana/functional/curry.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>

#include <test/auto/base.hpp>
#include <test/injection.hpp>

// tested instances
#include <test/auto/constant.hpp>
#include <test/auto/logical.hpp>
using namespace boost::hana;

namespace boost { namespace hana { namespace test {
    template <typename T>
    auto instances<CNumeric<T>> = make<Tuple>(
        type<Constant>,
        type<Logical>
    );

    template <typename T>
    auto objects<CNumeric<T>> = make<Tuple>(
        cnumeric<T, 0>,
        cnumeric<T, 1>,
        cnumeric<T, 2>,
        cnumeric<T, 3>
    );
}}}

namespace boost { namespace hana {
    template <typename T>
    struct operators::of<test::CNumeric<T>>
        : operators::of<Logical>
    { };
}}

int main() {
    test::check_datatype<test::CNumeric<int>>();

    // Constant
    {
        // value
        {
            static_assert(value(test::cnumeric<int, 0>) == 0, "");
            static_assert(value(test::cnumeric<int, 1>) == 1, "");
        }
    }

    // Logical
    {
        struct invalid { };
        constexpr auto true_ = test::cnumeric<bool, true>;
        constexpr auto false_ = test::cnumeric<bool, false>;
        constexpr auto x = test::cnumeric<int, 1>;
        constexpr auto y = test::cnumeric<int, 2>;

        // not_
        {
            BOOST_HANA_CONSTANT_CHECK(equal(not_(true_), false_));
        }

        // and_
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(false_),
                false_
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_, true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_, false_),
                false_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(false_, invalid{}),
                false_
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_, true_, true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_, true_, false_),
                false_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(true_, false_, invalid{}),
                false_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                and_(false_, invalid{}, invalid{}),
                false_
            ));
        }

        // or_
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_),
                false_
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_, false_),
                false_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_, true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(true_, invalid{}),
                true_
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_, false_, false_),
                false_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_, false_, true_),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(false_, true_, invalid{}),
                true_
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                or_(true_, invalid{}, invalid{}),
                true_
            ));
        }

        // if_
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                if_(true_, x, y), x
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                if_(false_, x, y), y
            ));
        }

        // eval_if
        {
            auto t = [=](auto) { return x; };
            auto e = [=](auto) { return y; };

            BOOST_HANA_CONSTANT_CHECK(equal(
                eval_if(true_, t, invalid{}), x
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                eval_if(false_, invalid{}, e), y
            ));
        }

        // while_
        {
            //! @todo implement this
        }

        // until
        {
            using test::x;
            auto equal_to = curry<2>(equal);

            struct { } invalid{};
            auto f = test::injection([]{});

            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(x<0>), x<0>, invalid),
                x<0>
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(f(x<0>)), x<0>, f),
                f(x<0>)
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(f(f(x<0>))), x<0>, f),
                f(f(x<0>))
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(f(f(f(x<0>)))), x<0>, f),
                f(f(f(x<0>)))
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(f(f(f(f(x<0>))))), x<0>, f),
                f(f(f(f(x<0>))))
            ));

            // Make sure it can be called with an lvalue state:
            auto state = x<0>;
            BOOST_HANA_CONSTANT_CHECK(equal(
                until(equal_to(f(f(f(f(x<0>))))), state, f),
                f(f(f(f(x<0>))))
            ));
        }

        // operators
        {
            using namespace boost::hana::operators;
            BOOST_HANA_CONSTANT_CHECK(equal(
                !true_,
                not_(true_)
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                true_ && true_,
                and_(true_, true_)
            ));

            BOOST_HANA_CONSTANT_CHECK(equal(
                false_ || false_,
                or_(false_, false_)
            ));
        }
    }
}


#pragma once

#include <Eigen/Dense>
#include <set>
#include <vector>
#include <map>

// Note: each row is an observation aka feature vector

namespace data
{
	class Dataset
	{
	public:

		// Constructor takes names of features. Features with other names will be ignored when observation is appended
		Dataset(
			const std::vector<std::string>& r_feature_names,
			double init_value = std::numeric_limits<double>::quiet_NaN());

		// Constructor takes file paths to feature CSV file and label CSV file
		Dataset(
			std::string features_file_path,
			std::string labels_file_path,
			double init_value = std::numeric_limits<double>::quiet_NaN());

		// Constructor takes file path to feature CSV file, only. Labels are initialized with NaN by default
		Dataset(
			std::string features_file_path,
			double init_value = std::numeric_limits<double>::quiet_NaN());

		// Only features contained in the map will be set for the observation, the others remain with the init_value. If no label is provided, NaN is stored as label
		// Labels are expected to be 0..num_classes-1.
		void append_observation(const std::map<std::string, double>& r_features, double label = std::numeric_limits<double>::quiet_NaN());

		// Filter features by name. Other features will be discarded. If a name is not found, it is ignored
		void filter_features(const std::vector<std::string>& r_feature_names);

		// Set labels in dataset. Labels are expected to be 0..num_classes-1 and observation-many.
		void set_labels(Eigen::VectorXd labels);

		// Normalize values of feature observations to a range of zero to one
		void normalize();

		// Normalize with given min-max values (e.g., when test dataset should be normalized similar to training dataset)
		void normalize(const std::map<std::string, std::pair<double, double> >& r_min_max);

		// Get min-max values for features. Map consists of feature name as key and min and max as value pair
		std::map<std::string, std::pair<double, double> > get_min_max() const;

		// Get values of feature observations, feature_names defines which features in which order should be provided.
		// Optionally, fill string vector with feature names as provided in the result. E.g., some features requested might be not available and thus not included
		Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
			get_observations_row_wise(const std::vector<std::string>& r_feature_names, std::vector<std::string>* p_result_feature_names = nullptr) const;
		Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>
			get_observations_column_wise(const std::vector<std::string>& r_feature_names, std::vector<std::string>* p_result_feature_names = nullptr) const;

		// Get labels of all observations
		Eigen::VectorXd get_labels() const;

		// Get labels but converted to binary (threshold is used to divide classes into -1 and 1 case, as demanded by shogun. value > threshold is converted to 1, else -1)
		Eigen::VectorXd get_binary_labels(int threshold = 0.0) const;

		// Get feature count
		int get_feature_count() const;

		// Get names of features (though it is a vector, each name is unique)
		std::vector<std::string> get_feature_names() const;

		// Save in CSV files
		void save_as_CSV(std::string features_file_path, std::string labels_file_path) const;
		void save_features_as_CSV(std::string file_path) const;
		void save_labels_as_CSV(std::string file_path) const;

	private:

		// Custom data structure for feature names. Uses ordered vector but guarantess unique names
		class Names
		{
		public:

			Names() {}
			Names(const std::vector<std::string>& r_names, bool force_unique = true)
			{
				if (force_unique)
				{
					for (const auto& r_name : r_names)
					{
						add(r_name);
					}
				}
				else
				{
					_names = r_names;
				}
			}

			void add(const std::string& r_name, bool force_unique = true)
			{
				// Super slow, but very few executions
				if (force_unique)
				{
					for (const auto& r_known_name : _names)
					{
						if (r_name == r_known_name)
						{
							return;
						}
					}
				}
				_names.push_back(r_name);
			}

			const std::vector<std::string>& get() const
			{
				return _names;
			}

			bool is_same(const std::vector<std::string>& names) const
			{
				if (names.size() == _names.size())
				{
					for (int i = 0; i < (int)_names.size(); ++i)
					{
						if (names.at(i) != _names.at(i))
						{
							return false;
						}
					}
				}
				else
				{
					return false;
				}
				return true;
			}

		private:
			std::vector<std::string> _names;
		};

		// Get index-based min-max values
		std::vector<std::pair<double, double> > get_min_max_internal() const;

		// Members
		Names _names; // count is same as columns in _values
		Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> _values; // stored row-wise
		Eigen::VectorXd _labels; // count same as rows in _values
		const double _init_value; // defined by constructor
	};
}


/////////////////////////////////////////////////////////////////////////////////
// distribution::toolkit::distributions::chi_squared::os.hpp 				   //
//                                                                             //
//  (C) Copyright 2009 Erwann Rogard                                           //
//  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)           //
/////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_CHI_SQUARED_OS_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_CHI_SQUARED_OS_HPP_ER_2009
#include <boost/statistics/detail/distribution_toolkit/distributions/chi_squared/description.hpp>

namespace boost{
namespace math{

    template<typename T,typename P>
	std::ostream&
    operator<<(
    	std::ostream& os,
        const boost::math::chi_squared_distribution<T,P>& dist
    )
    {
        return (os << description(dist));
    }

}// math
}// boost

#endif


/*
Copyright (c) 2017 Theodoros Chondrogiannis
*/

#ifndef GRAPH_HPP
#define GRAPH_HPP

#include <fstream>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>

#include <boost/functional/hash.hpp>

using namespace std;

typedef unsigned int NodeID;
typedef pair<NodeID, NodeID> Edge;

// the value 'int' is the edge weight
typedef unordered_map<NodeID, int> EdgeList;

class RoadNetwork {
public:
  unsigned int numNodes;
  unsigned int numEdges;
  vector<EdgeList> adjListOut;
  vector<EdgeList> adjListInc;

  RoadNetwork(const char *filename);
  int getEdgeWeight(NodeID lnode, NodeID rnode);
  void print();
  RoadNetwork(){};
  ~RoadNetwork();

  EdgeList ougoingEdgesOf(NodeID);
  EdgeList incomingEdgesOf(NodeID);
};

// This is to ensure that edges are considered in a bidirectional fashion for
// the computation of the overlap.
bool operator==(const Edge &le, const Edge &re);

class Path {
public:
  vector<NodeID> nodes;
  unordered_set<Edge, boost::hash<Edge>> edges;
  int length;

  Path() { length = -1; }

  bool containsEdge(Edge e);
  double overlap_ratio(RoadNetwork *rN, Path &path2);
  friend ostream &operator<<(ostream &os, const Path &path);

  vector<Edge> getEdges() const;
};

#endif


#define BOOST_TEST_MODULE reduce

#include <boost/test/included/unit_test.hpp>
#include <mpl/mpl.hpp>

template<typename T>
class add {
public:
  T operator()(const T &a, const T &b) { return a + b; }
};

template<typename T>
bool reduce_func_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  if (comm_world.rank() == 0) {
    T y{};
    comm_world.reduce(add<T>(), 0, x, y);
    return y == T((N * N + N) / 2);
  } else {
    comm_world.reduce(add<T>(), 0, x);
  }
  return true;
}

template<typename T>
bool reduce_op_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  if (comm_world.rank() == 0) {
    T y{};
    comm_world.reduce(mpl::plus<T>(), 0, x, y);
    return y == T((N * N + N) / 2);
  } else {
    comm_world.reduce(mpl::plus<T>(), 0, x);
  }
  return true;
}

template<typename T>
bool reduce_lambda_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  if (comm_world.rank() == 0) {
    T y{};
    comm_world.reduce([](T a, T b) { return a + b; }, 0, x, y);
    return y == T((N * N + N) / 2);
  } else {
    comm_world.reduce([](T a, T b) { return a + b; }, 0, x);
  }
  return true;
}

template<typename T>
bool reduce_inplace_func_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.reduce(add<T>{}, 0, x);
  return x == T((N * N + N) / 2) or comm_world.rank() > 0;
}

template<typename T>
bool reduce_inplace_op_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.reduce(mpl::plus<T>(), 0, x);
  return x == T((N * N + N) / 2) or comm_world.rank() > 0;
}

template<typename T>
bool reduce_inplace_lambda_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.reduce([](T a, T b) { return a + b; }, 0, x);
  return x == T((N * N + N) / 2) or comm_world.rank() > 0;
}

template<typename T>
bool allreduce_func_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)}, y{};
  comm_world.allreduce(add<T>(), x, y);
  return y == T((N * N + N) / 2);
}

template<typename T>
bool allreduce_op_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)}, y{};
  comm_world.allreduce(mpl::plus<T>(), x, y);
  return y == T((N * N + N) / 2);
}

template<typename T>
bool allreduce_lambda_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)}, y{};
  comm_world.allreduce([](T a, T b) { return a + b; }, x, y);
  return y == T((N * N + N) / 2);
}

template<typename T>
bool allreduce_inplace_func_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.allreduce(add<T>(), x);
  return x == T((N * N + N) / 2);
}

template<typename T>
bool allreduce_inplace_op_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.allreduce(mpl::plus<T>(), x);
  return x == T((N * N + N) / 2);
}

template<typename T>
bool allreduce_inplace_lambda_test() {
  const mpl::communicator &comm_world = mpl::environment::comm_world();
  int N = comm_world.size();
  T x{T(comm_world.rank() + 1)};
  comm_world.allreduce([](T a, T b) { return a + b; }, x);
  return x == T((N * N + N) / 2);
}

BOOST_AUTO_TEST_CASE(reduce) {
  BOOST_TEST(reduce_func_test<double>());
  BOOST_TEST(reduce_op_test<double>());
  BOOST_TEST(reduce_lambda_test<double>());
  BOOST_TEST(reduce_inplace_func_test<double>());
  BOOST_TEST(reduce_inplace_op_test<double>());
  BOOST_TEST(reduce_inplace_lambda_test<double>());
  BOOST_TEST(allreduce_func_test<double>());
  BOOST_TEST(allreduce_op_test<double>());
  BOOST_TEST(allreduce_lambda_test<double>());
  BOOST_TEST(allreduce_inplace_func_test<double>());
  BOOST_TEST(allreduce_inplace_op_test<double>());
  BOOST_TEST(allreduce_inplace_lambda_test<double>());
}


#include <iostream>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
// Include the headers of MyLib

namespace np = boost::python::numpy;
namespace p = boost::python;

BOOST_PYTHON_MODULE(boost_numpy)
{
    Py_Initialize();
    np::initialize();

    // Write the bindings here

p::tuple shape = p::make_tuple(3, 3);
np::dtype dtype = np::dtype::get_builtin<float>();
np::ndarray a = np::zeros(shape, dtype);
np::ndarray b = np::empty(shape,dtype);

  std::cout << "Original array:\n" << p::extract<char const *>(p::str(a)) << std::endl;

  // Reshape the array into a 1D array
  a = a.reshape(p::make_tuple(9));

 for(int i=0; i<9; i++) a[i] = i;

  // Print it again.
  std::cout << "Reshaped array:\n" << p::extract<char const *>(p::str(a)) << std::endl;

}


// tableau.cpp
#include "tableau.hpp"
#include <boost/python.hpp>

namespace py = boost::python;

/* ------------------------------------------------------------------------- */
// Constructors, destructors
/* ------------------------------------------------------------------------- */
Tableau::Tableau() {}; // default constructor

Tableau::Tableau(vec2D& tableauArg) {
	setTableau(tableauArg);
	setStages();
	setNodes();
	setrkMat();
	setWeights();
} // end constructor taking a 2D vector tableau

Tableau::Tableau(py::list& tableauArg) {
	setTableau(tableauArg);
	setStages();
	setNodes();
	setrkMat();
	setWeights();
} // end constructor taking a 2D vector tableau

Tableau::Tableau(const Tableau& tableauArg) :
	tableau_(tableauArg.tableau_) {
	reset(tableau_);
}

Tableau::~Tableau() {};

void Tableau::reset(vec2D& tableauArg) {
	setTableau(tableauArg);
	setStages();
	setNodes();
	setrkMat();
	setWeights();
} // end reset

void Tableau::reset(py::list& tableauArg) {
	setTableau(tableauArg);
	setStages();
	setNodes();
	setrkMat();
	setWeights();
} // end reset
/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
// Tableau
/* ------------------------------------------------------------------------- */
void Tableau::setTableau(vec2D& btToSet) {
	tableau_.resize(btToSet.size());
	tableau_ = btToSet;
} // end setTableau

void Tableau::setTableau(py::list& tableau) {
	int listLen = py::len(tableau);
	tableau_.resize(listLen);
	for (int i = 0; i < listLen; i++) {
		for (int j = 0; j < listLen; j++) {
			tableau_[i].push_back(py::extract<double>(tableau[i][j]));
		}
	}
} // end setTableau

vec2D Tableau::getTableau() {
	return tableau_;
} // end getTableau
/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
// Stages
/* ------------------------------------------------------------------------- */
void Tableau::setStages() {
	stages_ = tableau_.size() - 1;
} // end setStages

int Tableau::getStages() {
	return stages_;
} // end getStages
/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
// Nodes
/* ------------------------------------------------------------------------- */
void Tableau::setNodes() {
	nodes_.resize(stages_);
	for (int i=0; i < stages_; i++) {
		nodes_[i] = tableau_[i][0];
	}
} // end setNodes

vec1D Tableau::getNodes() {
	return nodes_;
} // end getNodes
/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
// k Coefficients
/* ------------------------------------------------------------------------- */
void Tableau::setrkMat() {
	for (int i = 0; i < stages_; i++)
		rk_mat_.push_back(std::vector<double>(tableau_[i].begin() + 1, tableau_[i].end()));
} // end setrkMat

vec2D Tableau::getrkMat() {
	return rk_mat_;
} // end getrkMat
/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
// Weights
/* ------------------------------------------------------------------------- */
void Tableau::setWeights() {
	for (int i = 1; i <= stages_; i++) {
		weights_.push_back(tableau_.back()[i]);
	}
} // end setWeights

vec1D Tableau::getWeights() {
	return weights_;
} // end getWeights
/* ------------------------------------------------------------------------- */


/* Copyright (C) 2012,2013 IBM Corp.
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/* Test_matmul.cpp - Testing the functionality of multiplying an encrypted
 * vector by a plaintext matrix, either over the extension- or the
 * base-field/ring.
 */

#include <cassert>
#include <NTL/lzz_pXFactoring.h>
#include "timing.h"
#include "matrix.h"

template<class type>
class RandomMatrix : public  PlaintextMatrixInterface<type> {
public:
  PA_INJECT(type)

private:
  const EncryptedArray& ea;

  vector< vector< RX > > data;

public:
  ~RandomMatrix() { /*cout << "destructor: random matrix\n";*/ }

  RandomMatrix(const EncryptedArray& _ea) : ea(_ea) {
    long n = ea.size();
    long d = ea.getDegree();

    long bnd = 2*n; // non-zero with probability 1/bnd

    RBak bak; bak.save(); ea.getContext().alMod.restoreContext();

    data.resize(n);
    for (long i = 0; i < n; i++) {
      data[i].resize(n);
      for (long j = 0; j < n; j++) {
        bool zEntry = (RandomBnd(bnd) > 0);

        if (zEntry)
          clear(data[i][j]);
        else
          random(data[i][j], d);
      }
    }
  }

  virtual const EncryptedArray& getEA() const {
    return ea;
  }

  virtual bool get(RX& out, long i, long j) const {
    assert(i >= 0 && i < ea.size());
    assert(j >= 0 && j < ea.size());
    if (IsZero(data[i][j])) return true;
    out = data[i][j];
    return false;
  }
};

PlaintextMatrixBaseInterface *
buildRandomMatrix(const EncryptedArray& ea)
{
  switch (ea.getContext().alMod.getTag()) {
    case PA_GF2_tag: {
      return new RandomMatrix<PA_GF2>(ea);
    }

    case PA_zz_p_tag: {
      return new RandomMatrix<PA_zz_p>(ea);
    }

    default: return 0;
  }
}

template<class type>
class RandomBlockMatrix : public  PlaintextBlockMatrixInterface<type> {
public:
  PA_INJECT(type)

private:
  const EncryptedArray& ea;

  vector< vector< mat_R > > data;

public:
  ~RandomBlockMatrix() { /*cout << "destructor: random block matrix\n";*/ }
  RandomBlockMatrix(const EncryptedArray& _ea) : ea(_ea) {
    long n = ea.size();
    long d = ea.getDegree();

    long bnd = 2*n; // non-zero with probability 1/bnd

    RBak bak; bak.save(); ea.getContext().alMod.restoreContext();

    data.resize(n);
    for (long i = 0; i < n; i++) {
      data[i].resize(n);
      for (long j = 0; j < n; j++) {
        data[i][j].SetDims(d, d);

        bool zEntry = (RandomBnd(bnd) > 0);

        for (long u = 0; u < d; u++)
          for (long v = 0; v < d; v++)
            if (zEntry)
              clear(data[i][j][u][v]);
            else
              random(data[i][j][u][v]);
      }
    }
  }

  virtual const EncryptedArray& getEA() const {
    return ea;
  }

  virtual bool get(mat_R& out, long i, long j) const {
    assert(i >= 0 && i < ea.size());
    assert(j >= 0 && j < ea.size());
    if (IsZero(data[i][j])) return true;
    out = data[i][j];
    return false;
  }
};

PlaintextBlockMatrixBaseInterface *
buildRandomBlockMatrix(const EncryptedArray& ea)
{
  switch (ea.getContext().alMod.getTag()) {
    case PA_GF2_tag: {
      return new RandomBlockMatrix<PA_GF2>(ea);
    }

    case PA_zz_p_tag: {
      return new RandomBlockMatrix<PA_zz_p>(ea);
    }

    default: return 0;
  }
}

void  TestIt(long m, long p, long r, long d, long L)
{
  cout << "*** TestIt: m=" << m
       << ", p=" << p
       << ", r=" << r
       << ", d=" << d
       << ", L=" << L
       << endl;

  FHEcontext context(m, p, r);
  buildModChain(context, L, /*c=*/3);

  context.zMStar.printout();
  cout << endl;

  FHESecKey secretKey(context);
  const FHEPubKey& publicKey = secretKey;
  secretKey.GenSecKey(/*w=*/64); // A Hamming-weight-w secret key

  ZZX G;
  if (d == 0)
    G = context.alMod.getFactorsOverZZ()[0];
  else
    G = makeIrredPoly(p, d);

  cout << "G = " << G << "\n";
  cout << "generating key-switching matrices... ";
  addSome1DMatrices(secretKey); // compute key-switching matrices that we need
  addFrbMatrices(secretKey); // compute key-switching matrices that we need
  cout << "done\n";

  cout << "computing masks and tables for rotation...";
  EncryptedArray ea(context, G);
  cout << "done\n";

  // Test a "normal" matrix over the extension field
  {
    // choose a random plaintext square matrix
    shared_ptr<PlaintextMatrixBaseInterface> ptr(buildRandomMatrix(ea));

    // choose a random plaintext vector
    PlaintextArray v(ea);
    v.random();

    // encrypt the random vector
    Ctxt ctxt(publicKey);
    ea.encrypt(ctxt, publicKey, v);
    Ctxt ctxt2 = ctxt;

    cout << " Multiplying with PlaintextMatrixBaseInterface... " << std::flush;
    v.mat_mul(*ptr);         // multiply the plaintext vector
    free_mat_mul_dense(ea, ctxt2, *ptr);  // multiply the ciphertext vector

    PlaintextArray v1(ea);
    ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector

    if (v.equals(v1))        // check that we've got the right answer
      cout << "Nice!!\n";
    else
      cout << "Grrr@*\n";

    // Test cached verions of the mult-by-matrix operation
    {
      CachedPtxtMatrix zzxMat;
      free_compMat(ea, zzxMat, *ptr);
      ctxt2 = ctxt;
      cout << " Multiplying with CachedPtxtMatrix... " << std::flush;
      mat_mul(ctxt2, zzxMat, ea);
      ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector
      if (v.equals(v1))        // check that we've got the right answer
	cout << "Nice!!\n" << std::flush;
      else
	cout << "Grrr@*\n" << std::flush;
    }
    {
      CachedDCRTPtxtMatrix dcrtMat;
      free_compMat(ea, dcrtMat, *ptr);
      ctxt2 = ctxt;
      cout << " Multiplying with CachedDCRTPtxtMatrix... " << std::flush;
      mat_mul(ctxt2, dcrtMat, ea);
      ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector
      if (v.equals(v1))        // check that we've got the right answer
	cout << "Nice!!\n\n";
      else
	cout << "Grrr@*\n\n";
    }
  }

  // Test a "block matrix" over the base field
  {
    // choose a random plaintext square matrix
    shared_ptr<PlaintextBlockMatrixBaseInterface>
      ptr(buildRandomBlockMatrix(ea));

    // choose a random plaintext vector
    PlaintextArray v(ea);
    v.random();

    // encrypt the random vector
    Ctxt ctxt(publicKey);
    ea.encrypt(ctxt, publicKey, v);
    Ctxt ctxt2 = ctxt;

    v.mat_mul(*ptr);         // multiply the plaintext vector
    cout << " Multiplying with PlaintextBlockMatrixBaseInterface... "
	 << std::flush;
    free_mat_mul(ea, ctxt2, *ptr);  // multiply the ciphertext vector

    PlaintextArray v1(ea);
    ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector

    if (v.equals(v1))        // check that we've got the right answer
      cout << "Nice!!\n";
    else
      cout << "Grrr...\n";

    // Test cached verion sof the mult-by-block-matrix operation
    {
      CachedPtxtBlockMatrix zzxMat;
      free_compMat(ea, zzxMat, *ptr);
      ctxt2 = ctxt;
      cout << " Multiplying with CachedPtxtBlockMatrix... " << std::flush;
      mat_mul(ctxt2, zzxMat, ea);
      ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector
      if (v.equals(v1))        // check that we've got the right answer
	cout << "Nice!!\n" << std::flush;
      else
	cout << "Grrr@*\n" << std::flush;
    }
    {
      CachedDCRTPtxtBlockMatrix dcrtMat;
      free_compMat(ea, dcrtMat, *ptr);
      ctxt2 = ctxt;
      cout << " Multiplying with CachedDCRTPtxtBlockMatrix... " << std::flush;
      mat_mul(ctxt2, dcrtMat, ea);
      ea.decrypt(ctxt2, secretKey, v1); // decrypt the ciphertext vector
      if (v.equals(v1))        // check that we've got the right answer
	cout << "Nice!!\n";
      else
	cout << "Grrr@*\n";
    }
  }
}

void usage(char *prog)
{
  cout << "Usage: "<<prog<<" [ optional parameters ]...\n";
  cout << "  optional parameters have the form 'attr1=val1 attr2=val2 ...'\n";
  cout << "  e.g, 'm=2047 p=2 L=4'\n\n";
  cout << "  m defines the cyclotomic polynomial Phi_m(X)\n";
  cout << "  p is the plaintext base [default=2]" << endl;
  cout << "  r is the lifting [default=1]" << endl;
  cout << "  d is the degree of the field extension [default==1]\n";
  cout << "    (d == 0 => factors[0] defined the extension)\n";
  cout << "  L is the # of primes in the modulus chain [default=4]\n";
  exit(0);
}

/* Testing the functionality of multiplying an encrypted vector by a plaintext
 * matrix, either over the extension- or the base-field/ring.
 */
int main(int argc, char *argv[])
{
  argmap_t argmap;
  argmap["m"] = "2047";
  argmap["p"] = "2";
  argmap["r"] = "1";
  argmap["d"] = "1";
  argmap["L"] = "4";

  // get parameters from the command line
  if (!parseArgs(argc, argv, argmap)) usage(argv[0]);

  long m = atoi(argmap["m"]);
  long p = atoi(argmap["p"]);
  long r = atoi(argmap["r"]);
  long d = atoi(argmap["d"]);
  long L = atoi(argmap["L"]);

  //  setTimersOn();
  setTimersOn();
  TestIt(m, p, r, d, L);
  cout << endl;
  printAllTimers();
  cout << endl;

}

/********************************************************************/
/************               UNUSED CODE                   ***********/
/********************************************************************/
#if 0
template<class type>
class RunningSumMatrix : public  PlaintextMatrixInterface<type> {
public:
  PA_INJECT(type)

private:
  const EncryptedArray& ea;

public:
  ~RunningSumMatrix() { /* cout << "destructor: running sum matrix\n";*/ }

  RunningSumMatrix(const EncryptedArray& _ea) : ea(_ea) { }

  virtual const EncryptedArray& getEA() const {
    return ea;
  }

  virtual bool get(RX& out, long i, long j) const {
    assert(i >= 0 && i < ea.size());
    assert(j >= 0 && j < ea.size());
    if (j >= i)
      out = 1;
    else
      out = 0;
    return false;
  }
};

PlaintextMatrixBaseInterface *
buildRunningSumMatrix(const EncryptedArray& ea)
{
  switch (ea.getContext().alMod.getTag()) {
    case PA_GF2_tag: {
      return new RunningSumMatrix<PA_GF2>(ea);
    }

    case PA_zz_p_tag: {
      return new RunningSumMatrix<PA_zz_p>(ea);
    }

    default: return 0;
  }
}

template<class type>
class TotalSumMatrix : public  PlaintextMatrixInterface<type> {
public:
  PA_INJECT(type)

private:
  const EncryptedArray& ea;

public:
  ~TotalSumMatrix() { cout << "destructor: total sum matrix\n"; }

  TotalSumMatrix(const EncryptedArray& _ea) : ea(_ea) { }

  virtual const EncryptedArray& getEA() const {
    return ea;
  }

  virtual bool get(RX& out, long i, long j) const {
    assert(i >= 0 && i < ea.size());
    assert(j >= 0 && j < ea.size());
    out = 1;
    return false;
  }
};

PlaintextMatrixBaseInterface *
buildTotalSumMatrix(const EncryptedArray& ea)
{
  switch (ea.getContext().alMod.getTag()) {
    case PA_GF2_tag: {
      return new TotalSumMatrix<PA_GF2>(ea);
    }

    case PA_zz_p_tag: {
      return new TotalSumMatrix<PA_zz_p>(ea);
    }

    default: return 0;
  }
}

template<class type>
class PolyBlockMatrix : public  PlaintextBlockMatrixInterface<type> {
public:
  PA_INJECT(type)

private:
  const EncryptedArray& ea;

  vector< vector< mat_R > > data;

public:

  PolyBlockMatrix(const PlaintextMatrixBaseInterface& MM) : ea(MM.getEA()) {
    RBak bak; bak.save(); ea.getContext().alMod.restoreContext();

    const PlaintextMatrixInterface<type>& M =
      dynamic_cast< const PlaintextMatrixInterface<type>& >(MM);

    long n = ea.size();
    const RX& G = ea.getDerived(type()).getG();

    data.resize(n);
    for (long i = 0; i < n; i++) {
      data[i].resize(n);
      for (long j = 0; j < n; j++) {
        RX tmp;
        if (M.get(tmp, i, j)) clear(tmp);
        matrixOfPoly(data[i][j], tmp, G);
      }
    }
  }

  virtual const EncryptedArray& getEA() const {
    return ea;
  }

  virtual bool get(mat_R& out, long i, long j) const {
    assert(i >= 0 && i < ea.size());
    assert(j >= 0 && j < ea.size());
    out = data[i][j];
    return false;
  }
};

PlaintextBlockMatrixBaseInterface *
buildPolyBlockMatrix(const PlaintextMatrixBaseInterface& M)
{
  switch (M.getEA().getContext().alMod.getTag()) {
    case PA_GF2_tag: {
      return new PolyBlockMatrix<PA_GF2>(M);
    }

    case PA_zz_p_tag: {
      return new PolyBlockMatrix<PA_zz_p>(M);
    }

    default: return 0;
  }
}

static void matrixOfPoly(mat_GF2& m, const GF2X& a, const GF2X& f)
{
  long d = deg(f);
  m.SetDims(d, d);
  GF2X b = a;

  for (long i = 0; i < d; i++) {
    VectorCopy(m[i], b, d);
    MulByXMod(b, b, f);
  }
}

static void matrixOfPoly(mat_zz_p& m, const zz_pX& a, const zz_pX& f)
{
  long d = deg(f);
  m.SetDims(d, d);
  zz_pX b = a;

  for (long i = 0; i < d; i++) {
    VectorCopy(m[i], b, d);
    MulByXMod(b, b, f);
  }
}
#endif


#include "EigenColumnVector.h"
#include <Eigen/Dense>

typedef Eigen::Matrix<double, Eigen::Dynamic, 1> ColumnVector;
typedef Eigen::Matrix<double, 1, Eigen::Dynamic> RowVector;
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Matrix;

void* EigenColumnVector_CreateVector(int rows)
{
	auto v = new ColumnVector(rows, 1);
	for (int i = 0; i < rows; i++)
		(*v)(i) = 0;

	return v;
}

void EigenColumnVector_Release(void* ptr)
{
	auto obj = static_cast<ColumnVector*>(ptr);
	if (obj != nullptr)
	{
		delete obj;
		obj = nullptr;
	}
}

ColumnVector* CastToColumnVector(void* ptr)
{
	return static_cast<ColumnVector*>(ptr);
}

ColumnVector* NewColumnVector(const ColumnVector& v)
{
	return new ColumnVector(v.rows(), 1);
}

ColumnVector* NewColumnVector()
{
	return new ColumnVector();
}

RowVector* NewRowVector();

int EigenColumnVector_Dimension(void* ptr)
{
	auto m = CastToColumnVector(ptr);
	return (int)m->rows();
}

double EigenColumnVector_Get(void* ptr, int x)
{
	auto m = CastToColumnVector(ptr);
	return (*m)(x);
}

void EigenColumnVector_Set(void* ptr, int x, double value)
{
	auto m = CastToColumnVector(ptr);
	(*m)(x) = value;
}

double EigenColumnVector_Dot(void* ptr1, void* ptr2)
{
	auto v1 = CastToColumnVector(ptr1);
	auto v2 = CastToColumnVector(ptr2);

	return v1->dot(*v2);
}

void* EigenColumnVector_Normalized(void* ptr)
{
	auto v = CastToColumnVector(ptr);
	auto n = NewColumnVector(*v);

	(*n) = v->normalized();
	return n;
}

void EigenColumnVector_Normalize(void* ptr)
{
	auto v = CastToColumnVector(ptr);
	v->normalize();
}

double EigenColumnVector_Norm(void* ptr)
{
	auto v = CastToColumnVector(ptr);
	return v->norm();
}

void* EigenColumnVector_Transpose(void* ptr)
{
	auto v1 = CastToColumnVector(ptr);
	auto v2 = NewRowVector();
	(*v2) = v1->transpose();
	return v2;
}

void* EigenColumnVector_Adjoint(void* ptr)
{
	auto v1 = CastToColumnVector(ptr);
	auto v2 = NewColumnVector();
	(*v2) = v1->adjoint();
	return v2;
}

void* EigenColumnVector_Conjugate(void* ptr)
{
	auto v1 = CastToColumnVector(ptr);
	auto v2 = NewColumnVector();
	(*v2) = v1->conjugate();
	return v2;
}

void EigenColumnVector_Resize(void* ptr, int dimension)
{
	auto v = CastToColumnVector(ptr);
	v->resize(dimension);
}


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

// Copyright (c) 2014-2015, Oracle and/or its affiliates.

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

#include <iostream>

#ifndef BOOST_TEST_MODULE
#define BOOST_TEST_MODULE test_difference_pointlike_pointlike
#endif

#ifdef BOOST_GEOMETRY_TEST_DEBUG
#define BOOST_GEOMETRY_DEBUG_TURNS
#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
#endif

#include <boost/test/included/unit_test.hpp>

#include "../test_set_ops_pl_pl.hpp"

#include <boost/geometry/geometries/multi_point.hpp>

typedef bg::model::point<double,2,bg::cs::cartesian>  point_type;
typedef bg::model::multi_point<point_type>  multi_point_type;

//===========================================================================
//===========================================================================
//===========================================================================

BOOST_AUTO_TEST_CASE( test_difference_point_point )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** POINT / POINT DIFFERENCE ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef point_type P;
    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            P, P, MP, bg::overlay_difference
        > tester;

    tester::apply
        (from_wkt<P>("POINT(0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 1)"),
         "ppdf01");

    tester::apply
        (from_wkt<P>("POINT(0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "ppdf02");
}

BOOST_AUTO_TEST_CASE( test_difference_multipoint_point )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** MULTIPOINT / POINT DIFFERENCE ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef point_type P;
    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            MP, P, MP, bg::overlay_difference
        > tester;

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 1)"),
         "mppdf01");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mppdf02");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<MP>("MULTIPOINT(1 1)"),
         "mppdf03");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mppdf04");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<MP>("MULTIPOINT(1 1)"),
         "mppdf05");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(1 0)"),
         from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mppdf06");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mppdf07");

    tester::apply
        (from_wkt<MP>("MULTIPOINT()"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(0 0)"),
         "mppdf08");
}

BOOST_AUTO_TEST_CASE( test_difference_point_multipoint )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** POINT / MULTIPOINT DIFFERENCE ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef point_type P;
    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            P, MP, MP, bg::overlay_difference
        > tester;

    tester::apply
        (from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1,1 1)"),
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1,1 1)"),
         "pmpdf01");

    tester::apply
        (from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1)"),
         "pmpdf02");

    tester::apply
        (from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "pmpdf03");
}

BOOST_AUTO_TEST_CASE( test_difference_multipoint_multipoint )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** MULTIPOINT / MULTIPOINT DIFFERENCE ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            MP, MP, MP, bg::overlay_difference
        > tester;

    tester::apply
        (from_wkt<MP>("MULTIPOINT(2 2,3 3,0 0,0 0,2 2,1 1,1 1,1 0,1 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1,1 1,4 4)"),
         from_wkt<MP>("MULTIPOINT(2 2,3 3,0 0,0 0,2 2)"),
         from_wkt<MP>("MULTIPOINT(4 4)"),
         "mpmpdf01");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mpmpdf02");

    tester::apply
        (from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         "mpmpdf03");

    tester::apply
        (from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT()"),
         "mpmpdf04");

    tester::apply
        (from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT()"),
         "mpmpdf05");
}


///////////////////////////////////////////////////////////////////////////////
// statistics::survival::data::data::include.hpp                             //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_SURVIVAL_DATA_DATA_INCLUDE_HPP_ER_2009
#define BOOST_STATISTICS_SURVIVAL_DATA_DATA_INCLUDE_HPP_ER_2009

#include <boost/statistics/survival/data/data/event.hpp>
#include <boost/statistics/survival/data/data/mean_event.hpp>
#include <boost/statistics/survival/data/data/mngr/include.hpp>
#include <boost/statistics/survival/data/data/record.hpp>

#endif


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

#include "CommonIncludes.h"
#include "CalculatePIBoost.h"
#include "ProgressIndicatorInterface.h"

//#include <boost/math/constants/info.hpp>
#include <boost/math/constants/constants.hpp>
//#include <boost/multiprecision/detail/functions/trig.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

const wxString CalculatePIBoost::algoName_ = wxT("PI: boost");
const wxString CalculatePIBoost::algoDescr_ = wxT("Calculates PI using the boost::multiprecision library.");
const wxString CalculatePIBoost::copyrightText_ = wxT("Boost Software License - Version 1.0 - August 17th, 2003\n") \
	wxT("\n") \
	wxT("Permission is hereby granted, free of charge, to any person or organization\n") \
	wxT("obtaining a copy of the software and accompanying documentation covered by\n") \
	wxT("this license (the \"Software\") to use, reproduce, display, distribute,\n") \
	wxT("execute, and transmit the Software, and to prepare derivative works of the\n") \
	wxT("Software, and to permit third-parties to whom the Software is furnished to\n") \
	wxT("do so, all subject to the following:\n") \
	wxT("\n") \
	wxT("The copyright notices in the Software and this entire statement, including\n") \
	wxT("the above license grant, this restriction and the following disclaimer,\n") \
	wxT("must be included in all copies of the Software, in whole or in part, and\n") \
	wxT("all derivative works of the Software, unless such copies or derivative\n") \
	wxT("works are solely in the form of machine-executable object code generated by\n") \
	wxT("a source language processor.\n") \
	wxT("\n") \
	wxT("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n") \
	wxT("IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n") \
	wxT("FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\n") \
	wxT("SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\n") \
	wxT("FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\n") \
	wxT("ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n") \
	wxT("DEALINGS IN THE SOFTWARE.");
const std::string CalculatePIBoost::maxNumberOfDigits_ = "1000000";

CalculatePIBoost::CalculatePIBoost() :
	pProgressIndicatorInterface_(NULL)
{
}

CalculatePIBoost::~CalculatePIBoost()
{
}

bool CalculatePIBoost::isMulticoreCapable()
{
	return false;
}

bool CalculatePIBoost::enableProgressBar()
{
	return false;
}

const std::string &CalculatePIBoost::getMaxNumberOfDigits()
{
	return maxNumberOfDigits_;
}

const wxString &CalculatePIBoost::getAlgorithmName()
{
	return algoName_;
}

const wxString &CalculatePIBoost::getAlgorithmDescription()
{
	return algoDescr_;
}

const wxString &CalculatePIBoost::getCopyrightText()
{
	return copyrightText_;
}

void CalculatePIBoost::setDigits(const wxString &digits)
{
	digits_ = digits;
}

void CalculatePIBoost::setCores(int c)
{
	// Don't do anything: This Calculator is not multithreaded
}

#define VERSION_1

void CalculatePIBoost::calculate(ProgressIndicatorInterface *pProgressIndicatorInterface)
{
	// The major drawback of boost::multiprecision::cpp_dec_float is that the number of digits needs
	// to be known at compile time
	const int digits_compile_time = 100000;
	long long int d;
	digits_.ToLongLong(&d);
	if(d > digits_compile_time)
		d = digits_compile_time;	// ... or else calc_pi does not terminate
	long long int d_bits = 0;	// The number of bits required for d digits
	double digits_bits_factor = std::log( 10.0 ) / std::log( 2.0 );	// This is log2(10), approx. 3.32
	d_bits = static_cast<long long int>(std::ceil( digits_bits_factor * static_cast<double>(d) ));

	//typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<digits_compile_time, boost::int32_t, std::allocator<unsigned int> > > calc_pi_mptype;
	//typedef boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<digits_compile_time> > calc_pi_mptype;
	typedef boost::multiprecision::number<boost::multiprecision::backends::gmp_float<digits_compile_time> > calc_pi_mptype;
	calc_pi_mptype pi_boost;

#if defined(VERSION_1)
	// Version 1 uses boost::math::constants
//	boost::math::constants::print_info_on_type<mptype>();
	pi_boost = boost::math::constants::pi<calc_pi_mptype>();
#else
	// Version 2 uses an internal and undocumented method of boost::multiprecision which may or may not be present in the future
	boost::multiprecision::default_ops::calc_pi(pi_boost.backend(), d_bits);
#endif

	std::ostringstream ostr;
	ostr << std::setprecision(d+2);
	ostr << pi_boost;
	result_ = ostr.str();
}


#include <fstream>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>

#include <boost/lexical_cast.hpp>

using namespace std;
using namespace boost;

typedef set<size_t> Vertices;
typedef map<size_t, Vertices> Edges;

Edges edges, redges;
Vertices vertices;

int main(int argc, char ** argv)
{
    if (argc != 4)
    {
        cerr << "usage: extract_neighbourhood <vertex> <radius> <max-degre> < in_graph > out_graph\n";
        return 1;
    }
    size_t v = lexical_cast<size_t>(argv[1]);
    size_t r = lexical_cast<size_t>(argv[2]);
    size_t max_degree = lexical_cast<size_t>(argv[3]);
    size_t edges_num = 0;
    while (1)
    {
        size_t from, to;
        if (!(cin >> from >> to))
            break;

        ++edges_num;

        edges[from].insert(to);
        redges[to].insert(from);
        vertices.insert(from);
        vertices.insert(to);
    }

    cerr << edges_num << " edges read\n";

    Vertices extracted_vertices;

    queue< pair<size_t, size_t> > q;

    q.push(make_pair(v, 0));

    while (!q.empty())
    {
        size_t u = q.front().first;
        size_t dist = q.front().second;
        q.pop();
        if (dist > r)
            continue;

        if ((edges[u].size() > max_degree) || (redges[u].size() > max_degree))
            continue;

        if (extracted_vertices.insert(u).second == false)
            continue;

        for (Vertices::const_iterator it = edges[u].begin(), end = edges[u].end();
                it != end; ++it)
        {
            q.push(make_pair(*it, dist + 1));
        }

        for (Vertices::const_iterator it = redges[u].begin(), end = redges[u].end();
                it != end; ++it)
        {
            q.push(make_pair(*it, dist + 1));
        }
    }

    cerr << extracted_vertices.size() << " vertices extracted\n";

    size_t extracted_edges_num = 0;
    for (Vertices::const_iterator it = extracted_vertices.begin(), end = extracted_vertices.end();
            it != end; ++it)
    {
        size_t u = *it;
        for (Vertices::const_iterator it2 = edges[u].begin(), end2 = edges[u].end();
                it2 != end2; ++it2)
        {
            if (extracted_vertices.count(*it2))
            {
                cout << u << " " << *it2 << "\n";
                ++extracted_edges_num;
            }
        }

    }

    cerr << extracted_edges_num << " edges extracted\n";

}


#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include <boost/tokenizer.hpp>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <cstdlib>
#include <set>

#include "HCStream.cpp"

struct TestContext {
	typedef struct {
		std::vector<std::vector<double>> points;
		std::vector<int> labels;
	} dataset;

	dataset readDataset(std::string filename) {
		dataset ds;

		//std::vector<std::vector<double>> points;

		std::string data(filename);
		std::ifstream in(data.c_str());
		if (!in.is_open())
			return ds;

		std::string line;
		while (std::getline(in, line)) {
			boost::char_separator<char> sep(" ");
			boost::tokenizer<boost::char_separator<char> > tok(line, sep);

			// Tokenizer tok(line);
			std::vector<double> vals;

			std::transform(tok.begin(), tok.end(), std::back_inserter(vals),
					[](const std::string& str)
					{	return std::stold(str);});

			int label = vals.back();
			vals.pop_back();

			ds.points.push_back(vals);
			ds.labels.push_back(label);

		}

		return ds;
	}

	std::string set2Str(std::set<int> vec) {
		std::ostringstream oss;
		std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(oss, ","));
		return oss.str();
	}
};

BOOST_FIXTURE_TEST_CASE( HCStream_ParseEntry_SmileyDataset, TestContext ) {
	HCStream* hcStream = new HCStream(1000, 0.2, 5, 0.01, 0.01);

	dataset ds = readDataset("./src/datasets/smiley.csv");
	for (size_t pos = 0; pos < ds.points.size(); pos++) {
		hcStream->parseEntry(pos, ds.points[pos]);
	}

	std::set<int> setLabels;
	for (auto &point : ds.points) {
		int label = hcStream->getLabel(point);
		setLabels.insert(label);
	}

	//BOOST_CHECK_EQUAL(4, setLabels.size());

	std::cout << "smiley labels: " << set2Str(setLabels) << std::endl;

	/*
	 sort(actualLabels.begin(), actualLabels.end());
	 auto uniqueLabels = std::unique(actualLabels.begin(), actualLabels.end());
	 int clusterNo = std::distance(uniqueLabels, actualLabels.begin());
	*/
}


//
// Created by Hippolyte Barraud on 06/01/2018.
//

#include <string>
#include <iostream>
#include <vector>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include "io.hpp"
#include "seam_carver.hpp"

int main(int ac, char **av) {
    std::ios_base::sync_with_stdio (false);
    std::cin.tie(nullptr);

    if (ac < 2) {
        std::cout << "Usage: " << av[0] << " input_file_path > output_file" << std::endl;
        return EXIT_FAILURE;
    }

    boost::iostreams::mapped_file mmap_file(av[1], boost::iostreams::mapped_file::readonly);
    if (!mmap_file.is_open()) {
        std::cout << "Failed to open file" << std::endl;
        return EXIT_FAILURE;
    }
    auto file_buffer = mmap_file.const_data();
    int width, height, target;

    sscanf(file_buffer, "%d %d %d", &height, &width, &target);
    assert(width > 0 && height > 0 && target >= 0 && (width - target) > 0);

    matrix<uint_fast16_t> energy(height, width);
    read_input(energy, file_buffer, mmap_file.size());

    matrix<uint_fast16_t> workset(height, width);
    auto current = width;
    while (current > (width - target)) {
        workset.width = --current;
        remove_seam_simded(energy, workset);
        energy.width = workset.width;
    }

    print_matrix(energy);
    return EXIT_SUCCESS;
}


// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/at.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/tuple.hpp>
namespace hana = boost::hana;

constexpr auto xs = hana::make_tuple(0, '1', 2.0);

static_assert(hana::at(xs, hana::size_t<0>{}) == 0, "");
static_assert(hana::at(xs, hana::size_t<1>{}) == '1', "");
static_assert(hana::at(xs, hana::size_t<2>{}) == 2.0, "");

int main() { }


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 * @file testSignature
 * @brief Tests focusing on the details of Signatures to evaluate boost
 * compliance
 * @author Alex Cunningham
 * @date Sept 19th 2011
 */

#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/Testable.h>
#include <gtsam/discrete/Signature.h>

#include <boost/assign/std/vector.hpp>
#include <vector>

using namespace std;
using namespace gtsam;
using namespace boost::assign;

DiscreteKey X(0, 2), Y(1, 3), Z(2, 2);

/* ************************************************************************* */
TEST(testSignature, simple_conditional) {
  Signature sig(X, {Y}, "1/1 2/3 1/4");
  CHECK(sig.table());
  Signature::Table table = *sig.table();
  vector<double> row[3]{{0.5, 0.5}, {0.4, 0.6}, {0.2, 0.8}};
  LONGS_EQUAL(3, table.size());
  CHECK(row[0] == table[0]);
  CHECK(row[1] == table[1]);
  CHECK(row[2] == table[2]);

  CHECK(sig.key() == X);

  DiscreteKeys keys = sig.discreteKeys();
  LONGS_EQUAL(2, keys.size());
  CHECK(keys[0] == X);
  CHECK(keys[1] == Y);

  DiscreteKeys parents = sig.parents();
  LONGS_EQUAL(1, parents.size());
  CHECK(parents[0] == Y);

  EXPECT_LONGS_EQUAL(6, sig.cpt().size());
}

/* ************************************************************************* */
TEST(testSignature, simple_conditional_nonparser) {
  Signature::Table table;
  Signature::Row row1, row2, row3;
  row1 += 1.0, 1.0;
  row2 += 2.0, 3.0;
  row3 += 1.0, 4.0;
  table += row1, row2, row3;

  Signature sig(X | Y = table);
  CHECK(sig.key() == X);

  DiscreteKeys keys = sig.discreteKeys();
  LONGS_EQUAL(2, keys.size());
  CHECK(keys[0] == X);
  CHECK(keys[1] == Y);

  DiscreteKeys parents = sig.parents();
  LONGS_EQUAL(1, parents.size());
  CHECK(parents[0] == Y);

  EXPECT_LONGS_EQUAL(6, sig.cpt().size());
}

/* ************************************************************************* */
DiscreteKey A(0, 2), S(1, 2), T(2, 2), L(3, 2), B(4, 2), E(5, 2), D(7, 2);

// Make sure we can create all signatures for Asia network with constructor.
TEST(testSignature, all_examples) {
  DiscreteKey X(6, 2);
  Signature a(A, {}, "99/1");
  Signature s(S, {}, "50/50");
  Signature t(T, {A}, "99/1  95/5");
  Signature l(L, {S}, "99/1  90/10");
  Signature b(B, {S}, "70/30  40/60");
  Signature e(E, {T, L}, "F F F 1");
  Signature x(X, {E}, "95/5 2/98");
}

// Make sure we can create all signatures for Asia network with operator magic.
TEST(testSignature, all_examples_magic) {
  DiscreteKey X(6, 2);
  Signature a(A % "99/1");
  Signature s(S % "50/50");
  Signature t(T | A = "99/1  95/5");
  Signature l(L | S = "99/1  90/10");
  Signature b(B | S = "70/30  40/60");
  Signature e((E | T, L) = "F F F 1");
  Signature x(X | E = "95/5 2/98");
}

// Check example from docs.
TEST(testSignature, doxygen_example) {
  Signature::Table table{{0.9, 0.1}, {0.2, 0.8}, {0.3, 0.7}, {0.1, 0.9}};
  Signature d1(D, {E, B}, table);
  Signature d2((D | E, B) = "9/1 2/8 3/7 1/9");
  Signature d3(D, {E, B}, "9/1 2/8 3/7 1/9");
  EXPECT(*(d1.table()) == table);
  EXPECT(*(d2.table()) == table);
  EXPECT(*(d3.table()) == table);
}

/* ************************************************************************* */
int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */


//
// Created by tao on 7/21/21.
//

#include "encoder_converter.h"

#include <rosbag/bag.h>
#include <irp_sen_msgs/encoder.h>
#include <sensor_msgs/JointState.h>
#include <boost/filesystem.hpp>

namespace kaist2bag {

EncoderConverter::EncoderConverter(const std::string &dataset_dir, const std::string &save_dir,
                                   const std::string &irp_topic, const std::string &raw_topic)
                                   : Converter(dataset_dir, save_dir), irp_topic_(irp_topic), raw_topic_(raw_topic) {
    irp_bag_name_ = FilterSlash(irp_topic_) + ".bag";
    raw_bag_name_ = FilterSlash(raw_topic_) + ".bag";
}

int EncoderConverter::Convert() {
    CheckAndCreateSaveDir();

    boost::filesystem::path irp_bag_file = boost::filesystem::path(save_dir_) / irp_bag_name_;
    boost::filesystem::path raw_bag_file = boost::filesystem::path(save_dir_) / raw_bag_name_;
    rosbag::Bag irp_bag(irp_bag_file.string(), rosbag::bagmode::Write);
    rosbag::Bag raw_bag(raw_bag_file.string(), rosbag::bagmode::Write);
    ROS_INFO("saving %s", irp_bag_file.c_str());
    ROS_INFO("saving %s", raw_bag_file.c_str());

    sensor_msgs::JointState joint_data;
    joint_data.name.push_back("front_left_wheel_joint");
    joint_data.name.push_back("front_right_wheel_joint");
    joint_data.name.push_back("back_left_wheel_joint");
    joint_data.name.push_back("back_right_wheel_joint");
    joint_data.position.push_back(0.0);
    joint_data.position.push_back(0.0);
    joint_data.position.push_back(0.0);
    joint_data.position.push_back(0.0);
    joint_data.velocity.push_back(0.0);
    joint_data.velocity.push_back(0.0);
    joint_data.velocity.push_back(0.0);
    joint_data.velocity.push_back(0.0);
    bool has_previous = false;
    int64_t pre_stamp, pre_left_count, pre_right_count;

    int encoder_resolution_ = 0;
    double encoder_left_diameter_ = 0.0;
    double encoder_right_diameter_ = 0.0;
    double encoder_wheel_base_ = 0.0;
    const std::string calib_file = dataset_dir_ + "/" + default_calib_file;
    std::ifstream file(calib_file.c_str());
    std::string str;
    while (std::getline(file, str)) {
        std::vector<std::string> strs;
        boost::split(strs, str, boost::is_any_of(" "));
        if(!strs[1].compare("resolution:")){
            encoder_resolution_ = std::stoi(strs[2]);
        }
        if(!strs[1].compare("left")){
            encoder_left_diameter_ = std::stod(strs[4]);
        }
        if(!strs[1].compare("right")){
            encoder_right_diameter_ = std::stod(strs[4]);
        }
        if(!strs[1].compare("wheel")){
            encoder_wheel_base_ = std::stod(strs[3]);
        }
    }

    const std::string data_file = dataset_dir_ + "/" + default_data_file;
    FILE* fp = fopen(data_file.c_str(), "r");
    irp_sen_msgs::encoder encoder_data;
    int64_t stamp, left_count, right_count;
    while (fscanf(fp, "%ld,%ld,%ld\n", &stamp, &left_count, &right_count) == 3) {
        encoder_data.header.stamp.fromNSec(stamp);
        encoder_data.header.frame_id = "encoder";
        encoder_data.left_count = left_count;
        encoder_data.right_count = right_count;
        irp_bag.write(irp_topic_, encoder_data.header.stamp, encoder_data);

        if(has_previous) {

            double stamp_diff = 1e-9 * static_cast<double>(stamp - pre_stamp);
            int64_t d_left_cnt = left_count - pre_left_count;
            int64_t d_right_cnt = right_count - pre_right_count;
            double degl = ((double)d_left_cnt/(double)encoder_resolution_) * 2 * M_PI;
            double degr = ((double)d_right_cnt/(double)encoder_resolution_) * 2 * M_PI;
            double wl = degl / stamp_diff;
            double wr = degr / stamp_diff;

            joint_data.header.stamp.fromNSec(stamp);
            joint_data.header.frame_id = "encoder";
            joint_data.position[0] += degl;
            joint_data.position[1] += degr;
            joint_data.position[2] += degl;
            joint_data.position[3] += degr;
            joint_data.velocity[0] = wl;
            joint_data.velocity[1] = wr;
            joint_data.velocity[2] = wl;
            joint_data.velocity[3] = wr;
            raw_bag.write(raw_topic_, joint_data.header.stamp, joint_data);

        }
        has_previous = true;
        pre_stamp = stamp;
        pre_left_count = left_count;
        pre_right_count = right_count;

    }
    irp_bag.close();
    raw_bag.close();
    fclose(fp);
    ROS_INFO("done saving %s", irp_bag_file.c_str());
    ROS_INFO("done saving %s", raw_bag_file.c_str());

    return 0;
}
} // namespace kaist2bag