#pragma once

#include <defines.h>

#include <stdint.h>
#include <string>
#include <vector>
#include <iostream>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/QR>
#include <unsupported/Eigen/MatrixFunctions>

// CUDA runtime
#include <cuda_runtime.h>
// Utilities and system includes
#include <nvidia/helper_cuda.h>
#include <timer.hpp>

using namespace Eigen;
using namespace std;

#ifdef WEIGHTED
extern "C" void robustSquaredAngleCostFctGPU(float *h_cost, float *d_cost,
    float *d_x, float* d_weights, uint16_t *d_z, float *d_mu, float sigma_sq,
    int w, int h);

extern "C" void robustSquaredAngleCostFctAssignmentGPU(float *h_cost,
    float *d_cost,uint32_t *h_N, uint32_t *d_N, float *d_x, float* d_weights,
    float * d_errs,
    uint16_t *d_z, float *d_mu, float sigma_sq, int w, int h);

extern "C" void robustSquaredAngleCostFctJacobianGPU(float *h_J, float *d_J,
    float *d_x, float *d_weights, uint16_t *d_z, float *d_mu, float sigma_sq,
    int w, int h);
#else
extern "C" void robustSquaredAngleCostFctGPU(float *h_cost, float *d_cost,
    float *d_x, uint16_t *d_z, float *d_mu, float sigma_sq, int w, int h);

extern "C" void robustSquaredAngleCostFctAssignmentGPU(float *h_cost,
    float *d_cost, uint32_t *h_N, uint32_t *d_N, float *d_x, uint16_t *d_z, float *d_mu,
    float sigma_sq, int w, int h);

extern "C" void robustSquaredAngleCostFctJacobianGPU(float *h_J, float *d_J,
    float *d_x, uint16_t *d_z, float *d_mu, float sigma_sq, int w, int h);
#endif

extern "C" void meanInTpS2GPU(float *h_p, float *d_p, float *h_mu_karch,
    float *d_mu_karch, float *d_q, uint16_t *d_z, float* d_weights,int w, int h);

extern "C" void sufficientStatisticsOnTpS2GPU(float *h_p, float *d_p,
  float *h_Rnorths, float *d_Rnorths, float *d_q, uint16_t *d_z ,int w, int h,
  float *h_SSs, float *d_SSs);

extern "C" void loadRGBvaluesForMFaxes();

class OptSO3
{
protected:
  const int w_,h_;
  float *d_cost, *d_J, *d_mu_;
  float *h_errs_;
  uint32_t *d_N_;
  float t_max_, dt_;

public:
  float *d_errs_;
  float *d_q_, *d_weights_;
  const float sigma_sq_;
  uint16_t *d_z, *h_z;

  float dtPrep_; // time for preparation (here assignemnts)
  float dtCG_; // time for cunjugate gradient

  uint32_t t_; // timestep
  Matrix3f Rprev_; // previous rotation

  OptSO3(float sigma, float *d_q, int w, int h, float *d_weights =NULL):
    w_(w),h_(h),t_max_(1.0f),dt_(0.1f),
    d_q_(d_q),d_weights_(d_weights),
    sigma_sq_(sigma*sigma), dtPrep_(0.0f), dtCG_(0.0f),
    t_(0), Rprev_(Matrix3f::Identity())
  {init();};

  ~OptSO3();

  double D_KL_axisUnif();

  float *getErrs(int w, int h);

  virtual double conjugateGradientCUDA(Matrix3f& R, uint32_t maxIter=100)
  {
    Timer t0;
    uint32_t N = 0;
    if( fabs(R.determinant()-1.0) > 1e-6)
    {
      cout<<" == renormalizing rotation to get back to det(R) = 1"<<endl;
      R.col(0) = R.col(0)/R.col(0).norm();
      R.col(1) = R.col(1)/R.col(1).norm();
      R.col(2) = R.col(0).cross(R.col(1));
    }
    float res0 = conjugateGradientPreparation_impl(R,N);
    dtPrep_ = t0.toctic("--- association ");
    float resEnd = conjugateGradientCUDA_impl(R,res0, N, maxIter);
    conjugateGradientPostparation_impl(R);
    dtCG_ = t0.toctic("--- conjugateGradientCUDA ");
    t_++; // keep track of timesteps
    Rprev_ = R; // keep track of previous rotation estimate for rot. velocity computation
    return resEnd;
  };

  /* return a skew symmetric matrix from A */
  Matrix3f enforceSkewSymmetry(const Matrix3f &A) const
  {return 0.5*(A-A.transpose());};

  float dtPrep(void) const { return dtPrep_;};
  float dtCG(void) const { return dtCG_;};
protected:
  virtual float conjugateGradientPreparation_impl(Matrix3f& R, uint32_t& N);
  virtual float conjugateGradientCUDA_impl(Matrix3f& R, float res0, uint32_t N, uint32_t maxIter=100);
  virtual void conjugateGradientPostparation_impl(Matrix3f& R){;};
  /* evaluate cost function for a given assignment of npormals to axes */
  virtual float evalCostFunction(Matrix3f& R);
  /* compute Jacobian */
  virtual void computeJacobian(Matrix3f&J, Matrix3f& R, float N);
  /* recompute assignment based on rotation R and return residual as well */
  virtual float computeAssignment(Matrix3f& R, uint32_t& N);
  /* updates G and H from rotation R and jacobian J
   */
  virtual void updateGandH(Matrix3f& G, Matrix3f& G_prev, Matrix3f& H,
      const Matrix3f& R, const Matrix3f& J, const Matrix3f& M_t_min,
      bool resetH);
  /* performs line search starting at R in direction of H
   * returns min of cost function and updates R, and M_t_min
   */
  virtual float linesearch(Matrix3f& R, Matrix3f& M_t_min, const Matrix3f& H,
    float N, float t_max=1.0f, float dt=0.1f);
  /* mainly init GPU arrays */
  void init();
  /* copy rotation to device */
  void Rot2Device(Matrix3f& R);
  /* convert a Rotation matrix R to a MF representaiton of the axes */
  void Rot2M(Matrix3f& R, float *mu);
  //deprecated
  void rectifyRotation(Matrix3f& R);
};


#define BOOST_TEST_MODULE ChainDataTestSuite
#define BOOST_TEST_DYN_LINK

#include <boost/test/included/unit_test.hpp>
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
#include <memory>

#include <hops/hops.hpp>

BOOST_AUTO_TEST_SUITE(ChainDataTestSuite)
    BOOST_AUTO_TEST_CASE(InitializeAndRead) {
        Eigen::MatrixXd A(4, 2);
        Eigen::VectorXd b(4);
        Eigen::VectorXd x(2);

        A <<  1,  0,
              0,  1,
             -1,  0,
              0, -1;
        b << 1, 1, 0, 0;
        x << 0.5, 0.5;

        hops::ChainData chainData;
        long numberOfSamples = 1000;

        if (true) {
            hops::RandomNumberGenerator randomNumberGenerator;
            std::unique_ptr<hops::MarkovChain> mc = hops::MarkovChainFactory::createMarkovChain(hops::MarkovChainType::HitAndRun, A, b, x);
            mc->installDataObject(chainData);

            mc->draw(randomNumberGenerator, numberOfSamples);
        }

        BOOST_CHECK_EQUAL(chainData.getAcceptanceRates().size(), numberOfSamples);
        BOOST_CHECK_EQUAL(chainData.getStates().size(), numberOfSamples);
        BOOST_CHECK_EQUAL(chainData.getTimestamps().size(), numberOfSamples);
    }
BOOST_AUTO_TEST_SUITE_END()


#include <boost/geometry/formulas/unit_spheroid.hpp>


// -*- C++ -*-
//
// Copyright Sylvain Bougerel 2009 - 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file COPYING or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_TEST_DYN_LINK
#define SPATIAL_ENABLE_ASSERT // detect interal issues that should not occur

#include <boost/test/unit_test.hpp>
#include "spatial_test_fixtures.hpp"

BOOST_AUTO_TEST_CASE( test_loose_balancing )
{
  details::Dynamic_rank rank(2);
  loose_balancing test;
  // A leaf node is always balanced!
  BOOST_CHECK_EQUAL(test(rank, 0, 0), false);
  // rebalance even if no right.
  BOOST_CHECK_EQUAL(test(rank, 4, 0), true);
  // reblance even if no left.
  BOOST_CHECK_EQUAL(test(rank, 0, 4), true);
  // should fail cause we are right under loose balancing conditions
  BOOST_CHECK_EQUAL(test(rank, 7, 2), false);
  // should fail cause we are right under loose balancing conditions
  BOOST_CHECK_EQUAL(test(rank, 9, 3), false);
  // should pass cause we are above loose balancing conditions
  BOOST_CHECK_EQUAL(test(rank, 2, 8), true);
  // should pass cause we are above loose balancing conditions
  BOOST_CHECK_EQUAL(test(rank, 8, 2), true);
}

BOOST_AUTO_TEST_CASE( test_tight_balancing )
{
  {
    details::Dynamic_rank rank(2);
    tight_balancing test;
    // A leaf node is always balanced!
    BOOST_CHECK_EQUAL(test(rank, 0, 0), false);
    // rebalance even if no right.
    BOOST_CHECK_EQUAL(test(rank, 9, 0), true);
    // rebalance even if no left.
    BOOST_CHECK_EQUAL(test(rank, 0, 9), true);
    // should fail cause we are right under tight balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 4, 2), false);
    // should fail cause we are right under tight balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 2, 4), false);
    // should pass cause we are above tight balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 3, 6), true);
    // should pass cause we are above tight balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 6, 3), true);
  }
  {
    details::Dynamic_rank rank(9);
    tight_balancing test;
    // high dimension prevent rebalance.
    BOOST_CHECK_EQUAL(test(rank, 1, 8), false);
    BOOST_CHECK_EQUAL(test(rank, 8, 1), false);
  }
}

BOOST_AUTO_TEST_CASE( test_perfect_balancing )
{
  {
    details::Dynamic_rank rank(2);
    perfect_balancing test;
    // A leaf node is always balanced!
    BOOST_CHECK_EQUAL(test(rank, 0, 0), false);
    // rebalance even if no right.
    BOOST_CHECK_EQUAL(test(rank, 4, 1), true);
    // rebalance even if no left.
    BOOST_CHECK_EQUAL(test(rank, 2, 5), true);
    // should fail cause we are right under perfect balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 2, 0), false);
    // should fail cause we are right under perfect balancing conditions
    BOOST_CHECK_EQUAL(test(rank, 0, 2), false);
  }
  {
    details::Dynamic_rank rank(9);
    perfect_balancing test;
    // high dimension does not change balancing
    BOOST_CHECK_EQUAL(test(rank, 0, 2), false);
    BOOST_CHECK_EQUAL(test(rank, 2, 0), false);
    BOOST_CHECK_EQUAL(test(rank, 0, 3), true);
    BOOST_CHECK_EQUAL(test(rank, 3, 0), true);
  }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_ctor )
{
  typedef details::Relaxed_kdtree
    <details::Dynamic_rank, int2, int2, bracket_less<int2>,
     tight_balancing, std::allocator<int2> > kdtree_type;
  kdtree_type instance_one;
  kdtree_type instance_two(details::Dynamic_rank(2));
  kdtree_type instance_three(details::Dynamic_rank(2), bracket_less<int2>());
  kdtree_type instance_four(details::Dynamic_rank(2), bracket_less<int2>(),
                            tight_balancing());
  kdtree_type instance_five(details::Dynamic_rank(2), bracket_less<int2>(),
                            tight_balancing(), std::allocator<int2>());
  // Trees should be empty.
  BOOST_CHECK(instance_one.empty());
  BOOST_CHECK(instance_two.empty());
  BOOST_CHECK(instance_three.empty());
  BOOST_CHECK(instance_four.empty());
  BOOST_CHECK(instance_five.empty());
  // That also mean that beginning equal end for each of them
  BOOST_CHECK(instance_one.begin() == instance_one.end());
  BOOST_CHECK(instance_two.begin() == instance_two.end());
  BOOST_CHECK(instance_three.begin() == instance_three.end());
  BOOST_CHECK(instance_four.begin() == instance_four.end());
  BOOST_CHECK(instance_five.begin() == instance_five.end());
  // And that also mean that have a size of 0
  BOOST_CHECK_EQUAL(instance_one.size(), 0);
  BOOST_CHECK_EQUAL(instance_two.size(), 0);
  BOOST_CHECK_EQUAL(instance_three.size(), 0);
  BOOST_CHECK_EQUAL(instance_four.size(), 0);
  BOOST_CHECK_EQUAL(instance_five.size(), 0);
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_tight )
{
  typedef details::Relaxed_kdtree
    <details::Static_rank<2>, int2, int2, bracket_less<int2>,
     tight_balancing, std::allocator<int2> > kdtree_type;
  kdtree_type tree;
  kdtree_type::const_iterator it;
  BOOST_CHECK_NO_THROW(it = tree.insert(zeros));
  BOOST_CHECK(*it == zeros);
  BOOST_CHECK(!tree.empty());
  BOOST_CHECK(tree.begin() != tree.end());
  BOOST_CHECK_EQUAL(tree.size(), 1);
  BOOST_CHECK(zeros == *tree.begin());
  BOOST_CHECK(++tree.begin() == tree.end());
  BOOST_CHECK(tree.begin() == --tree.end());
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_perfect )
{
  typedef details::Relaxed_kdtree
    <details::Static_rank<2>, int2, int2, bracket_less<int2>,
     perfect_balancing, std::allocator<int2> > kdtree_type;
  kdtree_type tree;
  kdtree_type::const_iterator it;
  BOOST_CHECK_NO_THROW(it = tree.insert(zeros));
  BOOST_CHECK(*it == zeros);
  BOOST_CHECK(!tree.empty());
  BOOST_CHECK(tree.begin() != tree.end());
  BOOST_CHECK_EQUAL(tree.size(), 1);
  BOOST_CHECK(zeros == *tree.begin());
  BOOST_CHECK(++tree.begin() == tree.end());
  BOOST_CHECK(tree.begin() == --tree.end());
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_loose )
{
  typedef details::Relaxed_kdtree
    <details::Static_rank<2>, int2, int2, bracket_less<int2>,
     loose_balancing, std::allocator<int2> > kdtree_type;
  kdtree_type tree;
  kdtree_type::const_iterator it;
  BOOST_CHECK_NO_THROW(it = tree.insert(zeros));
  BOOST_CHECK(*it == zeros);
  BOOST_CHECK(!tree.empty());
  BOOST_CHECK(tree.begin() != tree.end());
  BOOST_CHECK_EQUAL(tree.size(), 1);
  BOOST_CHECK(zeros == *tree.begin());
  BOOST_CHECK(++tree.begin() == tree.end());
  BOOST_CHECK(tree.begin() == --tree.end());
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_growing )
{
  tight_pointset_fix<int2> fix(100, increase());
  // because the tree is ordered on all dimensions and nodes are all sequential
  // values, iteration should be ordered as well.
  int i = 0;
  for (tight_pointset_fix<int2>::container_type::iterator it
         = fix.container.begin(); it != fix.container.end(); ++it, ++i)
    {
      BOOST_CHECK_EQUAL((*it)[0], i);
      BOOST_CHECK_EQUAL((*it)[1], i);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_same )
{
  perfect_pointset_fix<int2> fix(100, increase());
  // because the tree is ordered on all dimensions and nodes are all sequential
  // values, iteration should be ordered as well.
  int i = 0;
  for (perfect_pointset_fix<int2>::container_type::iterator it
         = fix.container.begin(); it != fix.container.end(); ++it, ++i)
    {
      BOOST_CHECK_EQUAL((*it)[0], i);
      BOOST_CHECK_EQUAL((*it)[1], i);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_reduce )
{
  pointset_fix<int2> fix(100, decrease());
  // because the tree is ordered on all dimensions and nodes are all sequential
  // values, iteration should be ordered as well.
  int i = 1;
  for (pointset_fix<int2>::container_type::iterator it
         = fix.container.begin(); it != fix.container.end(); ++it, ++i)
    {
      BOOST_CHECK_EQUAL((*it)[0], i);
      BOOST_CHECK_EQUAL((*it)[1], i);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_insert_lots )
{
  // build repeatitively a lot of them, should not fail.
  // This tree has multiple similar node, by construction
  for (int i=0; i<100; ++i)
    {
      pointset_fix<int2> fix(100, randomize(-4, 4));
      int count = 0;
      for (pointset_fix<int2>::container_type::iterator it
             = fix.container.begin(); it != fix.container.end(); ++it)
        {
          if ((*it)[0] >= -4 && (*it)[0] < 4
              && (*it)[1] >= -4 && (*it)[1] < 4) ++count;
        }
      BOOST_CHECK_EQUAL(count, 100);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_copy )
{
  // Copy a kdtree of quad and iterate over it. Both tree should have leaf nodes
  // at the same location
  runtime_pointset_fix<quad> fix(40, randomize(-10, 10));
  runtime_pointset_fix<quad>::container_type copy(fix.container);
  BOOST_CHECK_EQUAL(fix.container.size(), copy.size());
  typedef runtime_pointset_fix<quad>::container_type::iterator iterator_type;
  iterator_type orig_begin = fix.container.begin();
  iterator_type orig_end = fix.container.end();
  iterator_type copy_begin = copy.begin();
  iterator_type copy_end = copy.end();
  for (; orig_begin != orig_end && copy_begin != copy_end;
       ++copy_begin, ++orig_begin)
    {
      BOOST_CHECK(*orig_begin == *copy_begin);
      if (orig_begin.node->left == 0) BOOST_CHECK(copy_begin.node->left == 0);
      if (orig_begin.node->right == 0) BOOST_CHECK(copy_begin.node->right == 0);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_assignment )
{
  // Copy an 3d kdtree and iterate over it. Both iterators should yield same
  // results and exhibit same properties.
  runtime_pointset_fix<quad> fix(40, randomize(-10, 10));
  runtime_pointset_fix<quad> copy(40, randomize(-10, 10));
  BOOST_CHECK_EQUAL(fix.container.size(), copy.container.size());
  copy.container = fix.container;
  typedef runtime_pointset_fix<quad>::container_type::iterator iterator_type;
  iterator_type orig_begin = fix.container.begin();
  iterator_type orig_end = fix.container.end();
  iterator_type copy_begin = copy.container.begin();
  iterator_type copy_end = copy.container.end();
  for (; orig_begin != orig_end && copy_begin != copy_end;
       ++copy_begin, ++orig_begin)
    {
      BOOST_CHECK(*orig_begin == *copy_begin);
      if (orig_begin.node->left == 0) BOOST_CHECK(copy_begin.node->left == 0);
      if (orig_begin.node->right == 0) BOOST_CHECK(copy_begin.node->right == 0);
    }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_bulk_insert )
{
  // reuse test_kdtree_insert_100_iterate_forward
  typedef details::Relaxed_kdtree
    <details::Static_rank<2>, int2, int2, bracket_less<int2>,
     loose_balancing, std::allocator<int2> > kdtree_type;
  kdtree_type kdtree;
  spatial::import::array<int2, 100> points;
  for(spatial::import::array<int2, 100>::iterator i
        = points.begin(); i != points.end(); ++i)
    {
      (*i)[0] = std::rand() % 20;
      (*i)[1] = std::rand() % 20;
    }
  BOOST_REQUIRE_NO_THROW(kdtree.insert(points.begin(), points.end()));
  BOOST_CHECK(kdtree.empty() == false);
  BOOST_REQUIRE_EQUAL(kdtree.size(), 100);
  BOOST_CHECK_EQUAL(kdtree.count(), 100);
  int count = 0;
  for(kdtree_type::iterator i = kdtree.begin(); i != kdtree.end(); ++i)
    {
      spatial::import::array<int2, 100>::iterator match;
      BOOST_REQUIRE((match = std::find(points.begin(), points.end(), *i))
                    != points.end());
      (*match)[0] = -1; // Prevent the same point from being found twice.
      (*match)[1] = -1;
      BOOST_REQUIRE_LE(++count, 100);
    }
  BOOST_CHECK_EQUAL(count, 100);
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_erase_key )
{
  {
    // Erase on empty tree should return 0.
    pointset_fix<int2> fix;
    int2 some_value(0, 0);
    size_type count = fix.container.erase(some_value);
    BOOST_CHECK_EQUAL(count, 0);
    BOOST_CHECK(fix.container.end() == fix.container.begin());
    BOOST_CHECK(fix.container.empty());
  }
  {
    // Erase one value of a tree should return 1 and leave an empty tree.
    tight_pointset_fix<int2> fix;
    int2 some_value(0, 0);
    fix.container.insert(some_value);
    BOOST_CHECK(!fix.container.empty());
    size_type count = fix.container.erase(some_value);
    BOOST_CHECK_EQUAL(count, 1);
    BOOST_CHECK(fix.container.end() == fix.container.begin());
    BOOST_CHECK(fix.container.empty());
  }
  {
    // Erase a value not in the tree should leave the tree untouched.
    runtime_pointset_fix<int2> fix;
    int2 some_value(0, 0);
    int2 other_value(1, 1);
    fix.container.insert(some_value);
    runtime_pointset_fix<int2>::container_type::iterator
      begin_before = fix.container.begin();
    runtime_pointset_fix<int2>::container_type::iterator
      end_before = fix.container.end();
    size_type count = fix.container.erase(other_value);
    BOOST_CHECK_EQUAL(count, 0);
    BOOST_CHECK(!fix.container.empty());
    BOOST_CHECK(fix.container.end() != fix.container.begin());
    BOOST_CHECK(fix.container.end() == end_before);
    BOOST_CHECK(fix.container.begin() == begin_before);
  }
  {
    // Should be able to erase multiple values
    typedef details::Relaxed_kdtree
      <details::Dynamic_rank, int2, int2, bracket_less<int2>,
       tight_balancing, std::allocator<int2> >
      kdtree_type;
    // 1D tree where we store 2d objects...
    kdtree_type tree(details::Dynamic_rank(1));
    int2 one(1, 0);
    int2 two(2, 2);
    int2 four(4, 0);
    tree.insert(one);
    tree.insert(two);
    tree.insert(two);
    tree.insert(four);
    // ... And equal should take the second dimension into account.
    BOOST_REQUIRE_EQUAL(tree.count(), 4);
    size_type count = tree.erase(two);
    BOOST_CHECK_EQUAL(count, 2);
    BOOST_REQUIRE_EQUAL(tree.count(), 2);
    BOOST_CHECK(tree.find(one) != tree.end());
    BOOST_CHECK(tree.find(four) != tree.end());
    BOOST_CHECK(tree.find(two) == tree.end());
    kdtree_type::iterator iter = tree.begin();
    BOOST_REQUIRE(iter != tree.end());
    BOOST_REQUIRE_NO_THROW(++iter);
    BOOST_REQUIRE(iter != tree.end());
    BOOST_REQUIRE_NO_THROW(++iter);
    BOOST_REQUIRE(iter == tree.end());
  }
}

BOOST_AUTO_TEST_CASE( test_relaxed_kdtree_erase_iterator )
{
  // check that erase at edge preserve basic iterators
  {
    point_multimap_fix<quad, std::string> fix(20, randomize(0, 1));
    typedef point_multimap_fix<quad, std::string>::container_type
      ::iterator iter_type;
    size_type track_size = fix.container.size();
    while (fix.container.size() != 0)
      {
        iter_type iter = fix.container.begin();
        BOOST_REQUIRE_NO_THROW(fix.container.erase(iter));
        BOOST_CHECK_EQUAL(fix.container.size(), --track_size);
        BOOST_CHECK(iter != fix.container.begin());
        int count = 0;
        for(iter_type i = fix.container.begin();
            i != fix.container.end(); ++i, ++count);
        BOOST_CHECK_EQUAL(count, track_size);
      }
  }
  {
    point_multimap_fix<quad, std::string> fix(20, randomize(0, 1));
    typedef point_multimap_fix<quad, std::string>::container_type
      ::iterator iter_type;
    typedef point_multimap_fix<quad, std::string>::container_type
      ::reverse_iterator riter_type;
    size_type track_size = fix.container.size();
    while (fix.container.size() != 0)
      {
        iter_type iter = --fix.container.end();
        BOOST_REQUIRE_NO_THROW(fix.container.erase(iter));
        BOOST_CHECK_EQUAL(fix.container.size(), --track_size);
        BOOST_CHECK(iter != (--fix.container.end()));
        int count = 0;
        for(riter_type i = fix.container.rbegin();
            i != fix.container.rend(); ++i, ++count);
        BOOST_CHECK_EQUAL(count, track_size);
      }
  }
  // erase all and check that total ordering is preserved.
  {
    typedef pointset_fix<double6>::container_type::iterator iterator;
    typedef ordered_iterator<pointset_fix<double6>::container_type>
      ordered_iterator;
    pointset_fix<double6> fix(100, randomize(0, 1));
    size_type track_size = fix.container.size();
    while (fix.container.size() != 0)
      {
        iterator eraser = fix.container.begin();
        std::advance(eraser, static_cast<ptrdiff_t>
                     (static_cast<size_t>(rand()) % fix.container.size()));
        ordered_iterator begin = ordered_begin(fix.container);
        ordered_iterator end = ordered_end(fix.container);
        size_type count = 0;
        for(ordered_iterator i = begin; i != end; ++i, ++count);
        BOOST_REQUIRE_EQUAL(count, track_size);
        count = 0;
        for(ordered_iterator i = end; i != begin; --i, ++count);
        BOOST_REQUIRE_EQUAL(count, track_size);
        BOOST_REQUIRE_NO_THROW(fix.container.erase(eraser));
        BOOST_CHECK_EQUAL(fix.container.size(), --track_size);
      }
  }
}

BOOST_AUTO_TEST_CASE (test_relaxed_kdtree_equal)
{
  {
    // These 2 trees should be equal (contents are equivalent)
    boxmap_fix<quad, std::string> one;
    one.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    boxmap_fix<quad, std::string> two;
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    BOOST_CHECK(one.container == two.container);
  }
  {
    // These 2 trees should be different.
    boxmap_fix<quad, std::string> one;
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    boxmap_fix<quad, std::string> two;
    two.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    BOOST_CHECK(one.container != two.container);
  }
}

BOOST_AUTO_TEST_CASE (test_relaxed_kdtree_lexicographic)
{
  {
    // These 2 trees should be equal (contents are equivalent)
    boxmap_fix<quad, std::string> one;
    one.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    boxmap_fix<quad, std::string> two;
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    BOOST_CHECK(!(one.container < two.container
                  || one.container > two.container));
  }
  {
    // These one is a "prefix" of two lexicographically
    boxmap_fix<quad, std::string> one;
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    one.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    boxmap_fix<quad, std::string> two;
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(1, 2, 1, 1), ""));
    two.container.insert(std::make_pair(quad(2, 2, 1, 1), ""));
    BOOST_CHECK(one.container < two.container);
  }
}


#include <iostream>
#include <frovedis.hpp>
#include <frovedis/ml/w2v/word2vec.hpp>
#include <boost/program_options.hpp>

using namespace frovedis;
using namespace boost::program_options;

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

  use_frovedis use(argc, argv);

  std::string weight_path, vocab_path, output_path;
  int  min_count;
  bool binary_output;

  options_description opt("option");
  opt.add_options()
    ("help,h", "print help")
    ("weight,w", value<std::string>(&weight_path)->required(), "path for input binary file of trained weight matrix")
    ("vocab,v", value<std::string>(&vocab_path)->required(), "path for input binary file of vocabulary.")
    ("output,o", value<std::string>(&output_path)->required(), "path for output file of word vectors whose format is same to original.")
    ("min-count", value<int>(&min_count)->default_value(5, "5"), "This will discard words that appear less than the times")
    ("binary", bool_switch(&binary_output)->default_value(false, "false"),
     "save output as binary")
    ;

  variables_map argmap;
  store(command_line_parser(argc,argv).options(opt).allow_unregistered().run(), argmap);
  if(argmap.count("help")){
    std::cerr << opt << std::endl;
    exit(1);
  }

  notify(argmap);

  w2v::save_model(weight_path, vocab_path, output_path, min_count, binary_output);

  return 0;
}


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2009 Matthias Christian Schabel
// Copyright (C) 2007-2009 Steven Watanabe
//
// 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)

/**
\file

\brief test_constants.cpp

\details
Test all combinations of operators with the constants.

**/

#include <boost/units/systems/detail/constants.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/pow.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/time.hpp>

using boost::units::quantity;
using boost::units::si::length;
using boost::units::si::meters;
using boost::units::si::seconds;
using boost::units::static_rational;
using boost::units::pow;
using boost::units::root;

BOOST_UNITS_PHYSICAL_CONSTANT(length_constant, quantity<length>, 2.0 * meters, 0.5 * meters);

template<class T>
void check_same(const T&, const T&);

template<class T>
typename T::value_type unwrap(const boost::units::constant<T>&);

template<class T>
T unwrap(const T&);

#define BOOST_UNITS_CHECK_RESULT(arg1, op, arg2) check_same((arg1) op (arg2), unwrap(arg1) op unwrap(arg2));

void test_add() {
    BOOST_UNITS_CHECK_RESULT(length_constant, +, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, +, 1.0 * meters);
    BOOST_UNITS_CHECK_RESULT(1.0* meters, +, length_constant);
}

void test_subtract() {
    BOOST_UNITS_CHECK_RESULT(length_constant, -, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, -, 1.0 * meters);
    BOOST_UNITS_CHECK_RESULT(1.0* meters, -, length_constant);
}

void test_multiply() {
    BOOST_UNITS_CHECK_RESULT(length_constant, *, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, *, 1.0 * seconds);
    BOOST_UNITS_CHECK_RESULT(1.0 * seconds, *, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, *, 1.0);
    BOOST_UNITS_CHECK_RESULT(1.0, *, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, *, seconds);
    BOOST_UNITS_CHECK_RESULT(seconds, *, length_constant);
}

void test_divide() {
    BOOST_UNITS_CHECK_RESULT(length_constant, /, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, /, 1.0 * seconds);
    BOOST_UNITS_CHECK_RESULT(1.0 * seconds, /, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, /, 1.0);
    BOOST_UNITS_CHECK_RESULT(1.0, /, length_constant);
    BOOST_UNITS_CHECK_RESULT(length_constant, /, seconds);
    BOOST_UNITS_CHECK_RESULT(seconds, /, length_constant);
}

void test_pow() {
    check_same(pow<2>(length_constant), pow<2>(unwrap(length_constant)));
    check_same(root<2>(length_constant), root<2>(unwrap(length_constant)));
    check_same(pow<5>(length_constant), pow<5>(unwrap(length_constant)));
    check_same(root<5>(length_constant), root<5>(unwrap(length_constant)));
    check_same(pow<static_rational<2, 3> >(length_constant), pow<static_rational<2, 3> >(unwrap(length_constant)));
    check_same(root<static_rational<2, 3> >(length_constant), root<static_rational<2, 3> >(unwrap(length_constant)));
}


#ifndef SKYLARK_GEMM_HPP
#define SKYLARK_GEMM_HPP

#include <boost/mpi.hpp>
#include "exception.hpp"
#include "sparse_matrix.hpp"
#include "computed_matrix.hpp"
#include "../utility/typer.hpp"

// Defines a generic Gemm function that receives both dense and sparse matrices.

namespace skylark { namespace base {

/**
 * Rename the Elemental Gemm function, so that we have unified access.
 */

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::Matrix<T>& A, const El::Matrix<T>& B,
    T beta, El::Matrix<T>& C) {
    El::Gemm(oA, oB, alpha, A, B, beta, C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::Matrix<T>& A, const El::Matrix<T>& B,
    El::Matrix<T>& C) {
    El::Gemm(oA, oB, alpha, A, B, C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::STAR, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    T beta, El::DistMatrix<T, El::STAR, El::STAR>& C) {
    El::Gemm(oA, oB, alpha, A.LockedMatrix(), B.LockedMatrix(), beta, C.Matrix());
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::STAR, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    El::DistMatrix<T, El::STAR, El::STAR>& C) {
    El::Gemm(oA, oB, alpha, A.LockedMatrix(), B.LockedMatrix(), C.Matrix());
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::CIRC, El::CIRC>& A,
    const El::DistMatrix<T, El::CIRC, El::CIRC>& B,
    T beta, El::DistMatrix<T, El::CIRC, El::CIRC>& C) {
    El::Gemm(oA, oB, alpha, A.LockedMatrix(), B.LockedMatrix(), beta, C.Matrix());
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::CIRC, El::CIRC>& A,
    const El::DistMatrix<T, El::CIRC, El::CIRC>& B,
    El::DistMatrix<T, El::CIRC, El::CIRC>& C) {
    El::Gemm(oA, oB, alpha, A.LockedMatrix(), B.LockedMatrix(), C.Matrix());
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T>& A, const El::DistMatrix<T>& B,
    T beta, El::DistMatrix<T>& C) {
    El::Gemm(oA, oB, alpha, A, B, beta, C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T>& A, const El::DistMatrix<T>& B,
    El::DistMatrix<T>& C) {
    El::Gemm(oA, oB, alpha, A, B, C);
}

/**
 * The following combinations is not offered by Elemental, but are useful for us.
 * We implement them partially.
 */

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VC, El::STAR>& A,
    const El::DistMatrix<T, El::VC, El::STAR>& B,
    T beta, El::DistMatrix<T, El::STAR, El::STAR>& C) {
    // TODO verify sizes etc.

    if ((oA == El::TRANSPOSE || oA == El::ADJOINT) && oB == El::NORMAL) {
        boost::mpi::communicator comm(C.Grid().Comm().comm,
            boost::mpi::comm_attach);
        El::Matrix<T> Clocal(C.Matrix());
        El::Gemm(oA, El::NORMAL,
            alpha, A.LockedMatrix(), B.LockedMatrix(),
            beta / T(comm.size()), Clocal);
        boost::mpi::all_reduce(comm,
            Clocal.Buffer(), Clocal.MemorySize(), C.Matrix().Buffer(),
            std::plus<T>());
    } else {
        SKYLARK_THROW_EXCEPTION(base::unsupported_base_operation());
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VC, El::STAR>& A,
    const El::DistMatrix<T, El::VC, El::STAR>& B,
    El::DistMatrix<T, El::STAR, El::STAR>& C) {

    int C_height = (oA == El::NORMAL ? A.Height() : A.Width());
    int C_width = (oB == El::NORMAL ? B.Width() : B.Height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VC, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    T beta, El::DistMatrix<T, El::VC, El::STAR>& C) {
    // TODO verify sizes etc.

    if (oA == El::NORMAL && oB == El::NORMAL) {
        El::Gemm(El::NORMAL, El::NORMAL,
            alpha, A.LockedMatrix(), B.LockedMatrix(),
            beta, C.Matrix());
    } else {
        SKYLARK_THROW_EXCEPTION(base::unsupported_base_operation());
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VC, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    El::DistMatrix<T, El::VC, El::STAR>& C) {

    int C_height = (oA == El::NORMAL ? A.Height() : A.Width());
    int C_width = (oB == El::NORMAL ? B.Width() : B.Height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VR, El::STAR>& A,
    const El::DistMatrix<T, El::VR, El::STAR>& B,
    T beta, El::DistMatrix<T, El::STAR, El::STAR>& C) {
    // TODO verify sizes etc.

    if ((oA == El::TRANSPOSE || oA == El::ADJOINT) && oB == El::NORMAL) {
        boost::mpi::communicator comm(C.Grid().Comm(), boost::mpi::comm_attach);
        El::Matrix<T> Clocal(C.Matrix());
        El::Gemm(oA, El::NORMAL,
            alpha, A.LockedMatrix(), B.LockedMatrix(),
            beta / T(comm.size()), Clocal);
        boost::mpi::all_reduce(comm,
            Clocal.Buffer(), Clocal.MemorySize(), C.Matrix().Buffer(),
            std::plus<T>());
    } else {
        SKYLARK_THROW_EXCEPTION(base::unsupported_base_operation());
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VR, El::STAR>& A,
    const El::DistMatrix<T, El::VR, El::STAR>& B,
    El::DistMatrix<T, El::STAR, El::STAR>& C) {

    int C_height = (oA == El::NORMAL ? A.Height() : A.Width());
    int C_width = (oB == El::NORMAL ? B.Width() : B.Height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VR, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    T beta, El::DistMatrix<T, El::VR, El::STAR>& C) {
    // TODO verify sizes etc.

    if (oA == El::NORMAL && oB == El::NORMAL) {
        El::Gemm(El::NORMAL, El::NORMAL,
            alpha, A.LockedMatrix(), B.LockedMatrix(),
            beta, C.Matrix());
    } else {
        SKYLARK_THROW_EXCEPTION(base::unsupported_base_operation());
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::DistMatrix<T, El::VR, El::STAR>& A,
    const El::DistMatrix<T, El::STAR, El::STAR>& B,
    El::DistMatrix<T, El::VR, El::STAR>& C) {

    int C_height = (oA == El::NORMAL ? A.Height() : A.Width());
    int C_width = (oB == El::NORMAL ? B.Width() : B.Height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

/**
 * Gemm between mixed Elemental, sparse input. Output is dense Elemental.
 */

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::Matrix<T>& A, const sparse_matrix_t<T>& B,
    T beta, El::Matrix<T>& C) {
    // TODO verify sizes etc.

    const int* indptr = B.indptr();
    const int* indices = B.indices();
    const T *values = B.locked_values();

    if (oA == El::ADJOINT && std::is_same<T, El::Base<T> >::value)
        oA = El::TRANSPOSE;

    if (oB == El::ADJOINT && std::is_same<T, El::Base<T> >::value)
        oB = El::TRANSPOSE;

    if (oA == El::ADJOINT || oB == El::ADJOINT)
        SKYLARK_THROW_EXCEPTION(base::unsupported_base_operation());

    // NN
    if (oA == El::NORMAL && oB == El::NORMAL) {
        int k = A.Width();
        int n = B.width();
        int m = A.Height();

        El::Scale(beta, C);

        El::Matrix<T> Ac;
        El::Matrix<T> Cc;

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for private(Cc, Ac)
#       endif
        for(int col = 0; col < n; col++) {
            El::View(Cc, C, 0, col, m, 1);
            for (int j = indptr[col]; j < indptr[col + 1]; j++) {
                int row = indices[j];
                T val = values[j];
                El::LockedView(Ac, A, 0, row, m, 1);
                El::Axpy(alpha * val, Ac, Cc);
            }
        }
    }

    // NT
    if (oA == El::NORMAL && oB == El::TRANSPOSE) {
        int k = A.Width();
        int n = B.width();
        int m = A.Height();

        El::Scale(beta, C);

        El::Matrix<T> Ac;
        El::Matrix<T> Cc;

        // Now, we simply think of B has being in CSR mode...
        int row = 0;
        for(int row = 0; row < n; row++) {
            El::LockedView(Ac, A, 0, row, m, 1);
#           if SKYLARK_HAVE_OPENMP
#           pragma omp parallel for private(Cc)
#           endif
            for (int j = indptr[row]; j < indptr[row + 1]; j++) {
                int col = indices[j];
                T val = values[j];
                El::View(Cc, C, 0, col, m, 1);
                El::Axpy(alpha * val, Ac, Cc);
            }
        }
    }

    // TN - TODO: Not tested!
    if (oA == El::TRANSPOSE && oB == El::NORMAL) {
        int k = A.Height();
        int n = B.width();
        int m = A.Width();

        T *c = C.Buffer();
        int ldc = C.LDim();

        const T *a = A.LockedBuffer();
        int lda = A.LDim();

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for collapse(2)
#       endif
        for (int j = 0; j < n; j++)
            for(int row = 0; row < m; row++) {
                c[j * ldc + row] *= beta;
                 for (int l = indptr[j]; l < indptr[j + 1]; l++) {
                     int rr = indices[l];
                     T val = values[l];
                     c[j * ldc + row] += val * a[row * lda + rr];
                 }
            }
    }

    // TT - TODO: Not tested!
    if (oA == El::TRANSPOSE && oB == El::TRANSPOSE) {
        int k = A.Height();
        int n = B.width();
        int m = A.Width();

        El::Scale(beta, C);

        T *c = C.Buffer();
        int ldc = C.LDim();

        const T *a = A.LockedBuffer();
        int lda = A.LDim();

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for
#       endif
        for(int row = 0; row < k; row++)
            for(int rb = 0; rb < n; rb++)
                for (int l = indptr[rb]; l < indptr[rb + 1]; l++) {
                    int col = indices[l];
                    c[col * ldc + row] += values[l] * a[row * lda + rb];
                }
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const sparse_matrix_t<T>& A, const El::Matrix<T>& B,
    T beta, El::Matrix<T>& C) {
    // TODO verify sizes etc.

    const int* indptr = A.indptr();
    const int* indices = A.indices();
    const T *values = A.locked_values();

    int k = A.width();
    int n = B.Width();
    int m = B.Height();

    if (oA == El::ADJOINT && std::is_same<T, El::Base<T> >::value)
        oA = El::TRANSPOSE;

    if (oB == El::ADJOINT && std::is_same<T, El::Base<T> >::value)
        oB = El::TRANSPOSE;

    // NN
    if (oA == El::NORMAL && oB == El::NORMAL) {

        El::Scale(beta, C);

        T *c = C.Buffer();
        int ldc = C.LDim();

        const T *b = B.LockedBuffer();
        int ldb = B.LDim();

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for
#       endif
        for(int i = 0; i < n; i++)
            for(int col = 0; col < k; col++)
                 for (int j = indptr[col]; j < indptr[col + 1]; j++) {
                     int row = indices[j];
                     T val = values[j];
                     c[i * ldc + row] += alpha * val * b[i * ldb + col];
                 }
    }

    // NT
    if (oA == El::NORMAL && (oB == El::TRANSPOSE || oB == El::ADJOINT)) {

        El::Scale(beta, C);

        El::Matrix<T> Bc;
        El::Matrix<T> BTr;
        El::Matrix<T> Cr;

        for(int col = 0; col < k; col++) {
            El::LockedView(Bc, B, 0, col, m, 1);
            El::Transpose(Bc, BTr, oB == El::ADJOINT);
#           if SKYLARK_HAVE_OPENMP
#           pragma omp parallel for private(Cr)
#           endif
            for (int j = indptr[col]; j < indptr[col + 1]; j++) {
                int row = indices[j];
                T val = values[j];
                El::View(Cr, C, row, 0, 1, m);
                El::Axpy(alpha * val, BTr, Cr);
            }
        }
    }

    // TN - TODO: Not tested!
    if (oA == El::TRANSPOSE && oB == El::NORMAL) {
        T *c = C.Buffer();
        int ldc = C.LDim();

        const T *b = B.LockedBuffer();
        int ldb = B.LDim();

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for collapse(2)
#       endif
        for (int j = 0; j < n; j++)
            for(int row = 0; row < k; row++) {
                c[j * ldc + row] *= beta;
                 for (int l = indptr[row]; l < indptr[row + 1]; l++) {
                     int col = indices[l];
                     T val = values[l];
                     c[j * ldc + row] += val * b[j * ldb + col];
                 }
            }
    }

    // AN - TODO: Not tested!
    if (oA == El::ADJOINT && oB == El::NORMAL) {
        T *c = C.Buffer();
        int ldc = C.LDim();

        const T *b = B.LockedBuffer();
        int ldb = B.LDim();

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for collapse(2)
#       endif
        for (int j = 0; j < n; j++)
            for(int row = 0; row < k; row++) {
                c[j * ldc + row] *= beta;
                 for (int l = indptr[row]; l < indptr[row + 1]; l++) {
                     int col = indices[l];
                     T val = El::Conj(values[l]);
                     c[j * ldc + row] += val * b[j * ldb + col];
                 }
            }
    }

    // TT - TODO: Not tested!
    if (oA == El::TRANSPOSE && (oB == El::TRANSPOSE || oB == El::ADJOINT)) {

        El::Scale(beta, C);

        El::Matrix<T> Bc;
        El::Matrix<T> BTr;
        El::Matrix<T> Cr;

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for private(Cr, Bc, BTr)
#       endif
        for(int row = 0; row < k; row++) {
            El::View(Cr, C, row, 0, 1, m);
            for (int l = indptr[row]; l < indptr[row + 1]; l++) {
                int col = indices[l];
                T val = values[l];
                El::LockedView(Bc, B, 0, col, m, 1);
                El::Transpose(Bc, BTr, oB == El::ADJOINT);
                El::Axpy(alpha * val, BTr, Cr);
            }
        }
    }

    // AT - TODO: Not tested!
    if (oA == El::ADJOINT && (oB == El::TRANSPOSE || oB == El::ADJOINT)) {

        El::Scale(beta, C);

        El::Matrix<T> Bc;
        El::Matrix<T> BTr;
        El::Matrix<T> Cr;

#       if SKYLARK_HAVE_OPENMP
#       pragma omp parallel for private(Cr, Bc, BTr)
#       endif
        for(int row = 0; row < k; row++) {
            El::View(Cr, C, row, 0, 1, m);
            for (int l = indptr[row]; l < indptr[row + 1]; l++) {
                int col = indices[l];
                T val = El::Conj(values[l]);
                El::LockedView(Bc, B, 0, col, m, 1);
                El::Transpose(Bc, BTr, oB == El::ADJOINT);
                El::Axpy(alpha * val, BTr, Cr);
            }
        }
    }
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const sparse_matrix_t<T>& A, const El::Matrix<T>& B,
    El::Matrix<T>& C) {
    int C_height = (oA == El::NORMAL ? A.height() : A.width());
    int C_width = (oB == El::NORMAL ? B.Width() : B.Height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

template<typename T>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    T alpha, const El::Matrix<T>& A, const sparse_matrix_t<T>& B,
    El::Matrix<T>& C) {
    int C_height = (oA == El::NORMAL ? A.Height() : A.Width());
    int C_width = (oB == El::NORMAL ? B.width() : B.height());
    El::Zeros(C, C_height, C_width);
    base::Gemm(oA, oB, alpha, A, B, T(0), C);
}

template<typename value_type>
void Gemm(El::Orientation oA, El::Orientation oB, value_type alpha,
          const El::DistMatrix<value_type, El::VC, El::STAR> &A,
          const El::DistMatrix<value_type, El::VC, El::STAR> &B,
          value_type beta, El::DistMatrix<value_type, El::VC, El::STAR> &C) {

    //XXX: Just forward to Elemental for now
    El::Gemm(oA, oB, alpha, A, B, beta, C);
}

template<typename value_type>
void Gemm(El::Orientation oA, El::Orientation oB, value_type alpha,
          const El::DistMatrix<value_type, El::VC, El::STAR> &A,
          const El::DistMatrix<value_type, El::VC, El::STAR> &B,
          El::DistMatrix<value_type, El::VC, El::STAR> &C) {

    Gemm(oA, oB, alpha, A, B, static_cast<value_type>(0.0), C);
}

/* All combinations with computed matrix */

template<typename CT, typename RT, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const computed_matrix_t<CT>& A,
    const RT& B, typename utility::typer_t<OT>::value_type beta, OT& C) {
    base::Gemm(oA, oB, alpha, A.materialize(), B, beta, C);
}

template<typename CT, typename RT, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const computed_matrix_t<CT>& A,
    const RT& B, OT& C) {
    base::Gemm(oA, oB, alpha, A.materialize(), B, C);
}

template<typename CT, typename RT, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const RT& A,
    const computed_matrix_t<CT>& B,
    typename utility::typer_t<OT>::value_type beta, OT& C) {
    base::Gemm(oA, oB, alpha, A, B.materialize(), beta, C);
}

template<typename CT, typename RT, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const RT& A,
    const computed_matrix_t<CT>& B, OT& C) {
    base::Gemm(oA, oB, alpha, A, B.materialize(), C);
}

template<typename CT1, typename CT2, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const computed_matrix_t<CT1>& A,
    const computed_matrix_t<CT2>& B, typename utility::typer_t<OT>::value_type beta,
    OT& C) {
    base::Gemm(oA, oB, alpha, A.materialize(), B.materialize(), beta, C);
}

template<typename CT1, typename CT2, typename OT>
inline void Gemm(El::Orientation oA, El::Orientation oB,
    typename utility::typer_t<OT>::value_type alpha, const computed_matrix_t<CT1>& A,
    const computed_matrix_t<CT2>& B, OT& C) {
    base::Gemm(oA, oB, alpha, A.materialize(), B.materialize(), C);
}

} } // namespace skylark::base

// Additional implementations
#include "detail/dist_mixed_gemm.hpp"
#include "detail/combblas_mixed_gemm.hpp"

#endif // SKYLARK_GEMM_HPP


#pragma once
#include "../../API/Evaluator.hpp"
#include <metaSMT/tags/Cardinality.hpp>
#include "object.hpp"
#include "adder_impl.hpp"
#include "bdd_impl.hpp"

#include <boost/optional.hpp>

namespace metaSMT {
  namespace cardinality {
    namespace cardtags = metaSMT::logic::cardinality::tag;
    template < typename Context, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality<cardtags::eq_tag, Boolean> const &c) {
      std::vector<Boolean> const &ps = c.ps;
      unsigned const nps = ps.size();
      unsigned const cardinality = c.cardinality;
      typename Context::result_type res = evaluate(ctx, logic::True);
      if (cardinality == 0) {
        for (unsigned u = 0; u < nps; ++u) {
          res = evaluate(ctx, logic::And(res, logic::Not(ps[u])));
          }
        return boost::optional< typename Context::result_type >(res);
      }
     if (cardinality == nps) {
       for (unsigned u = 0; u < nps; ++u) {
         res = evaluate(ctx, logic::And(res,ps[u]));
       }
        return boost::optional< typename Context::result_type >(res);
     }

     if (cardinality == 1 && nps == 2) {
		return boost::optional< typename Context::result_type >(evaluate(ctx, logic::Xor(ps[0],ps[1])));
     }

     if (cardinality > nps) {
        return boost::optional< typename Context::result_type >(evaluate(ctx,logic::False));
     }

      return boost::optional< typename Context::result_type >();
    }
    template < typename Context, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality< cardtags::gt_tag, Boolean> const &c) {

      std::vector<Boolean> const &ps = c.ps;
      unsigned const nps = ps.size();
      unsigned const cardinality = c.cardinality;
      typename Context::result_type res = evaluate(ctx, logic::True);

        if (nps  <= cardinality) {
          return boost::optional< typename Context::result_type >(evaluate(ctx, logic::False));
        }

        if (nps == cardinality+1) {
          for (unsigned u = 0; u < nps; ++u)
            res = evaluate(ctx, logic::And(res, ps[u]));
          return boost::optional< typename Context::result_type >(res);
        }

      return boost::optional< typename Context::result_type >();
    }

    template < typename Context, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality< cardtags::ge_tag, Boolean> const &c) {

      std::vector<Boolean> const &ps = c.ps;
      unsigned const nps = ps.size();
      unsigned const cardinality = c.cardinality;
      typename Context::result_type res = evaluate(ctx, logic::True);

        if (nps  < cardinality) {
          return boost::optional< typename Context::result_type >(evaluate(ctx, logic::False));
        }

        if (cardinality == 0) {
          return boost::optional< typename Context::result_type >(res);
        }

        if (nps == cardinality) {
          for (unsigned u = 0; u < nps; ++u)
            res = evaluate(ctx, logic::And(res, ps[u]));
          return boost::optional< typename Context::result_type >(res);
        }

      return boost::optional< typename Context::result_type >();
    }

    template < typename Context, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality< cardtags::lt_tag, Boolean> const &c) {

      std::vector<Boolean> const &ps = c.ps;
      unsigned const nps = ps.size();
      unsigned const cardinality = c.cardinality;
      typename Context::result_type res = evaluate(ctx, logic::True);

	  if (nps < cardinality) {
		return boost::optional< typename Context::result_type >(res);
	  }
	  if (cardinality == 0) {
		return boost::optional< typename Context::result_type >(evaluate(ctx, logic::False));
	  }
	  if (cardinality == 1) {
          for (unsigned u = 0; u < nps; ++u) {
            res = evaluate(ctx, logic::And(res, logic::Not(ps[u])));
		  }
          return boost::optional< typename Context::result_type >(res);
	  }

      return boost::optional< typename Context::result_type >();
    }

    template < typename Context, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality< cardtags::le_tag, Boolean> const &c) {

      std::vector<Boolean> const &ps = c.ps;
      unsigned const nps = ps.size();
      unsigned const cardinality = c.cardinality;

          typename Context::result_type res = evaluate(ctx, logic::True);
	  if (nps <= cardinality) {
		return boost::optional< typename Context::result_type >(res);
	  }

	  if (cardinality == 0) {
          for (unsigned u = 0; u < nps; ++u) {
            res = evaluate(ctx, logic::And(res, logic::Not(ps[u])));
		  }
          return boost::optional< typename Context::result_type >(res);
	  }

      return boost::optional< typename Context::result_type >();
    }

    // Empty Template to match. Just returns an empty optional
    template < typename Context, typename Tag, typename Boolean >
    boost::optional< typename Context::result_type >
    cardinality_simplify(Context &ctx, cardinality::Cardinality<Tag , Boolean> const &c) {
      return boost::optional< typename Context::result_type >();
    }
  } // cardinality

  template < typename Tag, typename Boolean >
  struct Evaluator< cardinality::Cardinality<Tag, Boolean> > : public boost::mpl::true_ {
    template < typename Context >
    static typename Context::result_type
    eval(Context &ctx, cardinality::Cardinality<Tag, Boolean> const &c) {
      boost::optional< typename Context::result_type > r = cardinality_simplify(ctx, c);
      if ( r ) {
        return *r;
      }

      std::string enc = c.encoding;
      if ( enc == "" ) {
        enc = get_option(ctx, "cardinality", /* default = */ "bdd");
      }
      if ( enc == "adder" ) {
        return cardinality::adder::cardinality(ctx, c);
      }
      else if ( enc == "bdd" ) {
        return cardinality::bdd::cardinality(ctx, c);
      }
      else {
        assert( false && "Unknown cardinality implementation" );
        throw std::exception();
      }
    }
  }; // Evaluator
} // metaSMT


#ifndef INPUT_TYPES_HPP
#define INPUT_TYPES_HPP

#include <string>
#include <vector>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <functional>
#include <boost/bimap.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>

#include "units.hpp"

namespace Input {

  class DescriptionBase {
  private:

    std::string description;
    bool is_defined;

  public:

    DescriptionBase()
      : description()
      , is_defined{ false }
    {}

    // setters
    void SetDescription( std::string const& description_) {
      description = description_;
      is_defined = true;
    }

    // getters
    std::string const& Description() const {
      return description;
    }

    bool DescriptionIsDefined() const {
      return is_defined;
    }

  };

  class Mechanism : public DescriptionBase {
  private:

    std::string path;

  public:

    // constructor: can be omitted, default ctor is OK
    Mechanism()
      : path()
    {}

    // setters
    void SetPath( std::string const& path_) {
      path = path_;
    }

    // getters
    std::string const& Path() const {
      return path;
    }

  };

  class RealScalar {
  private:

    units::precise_unit inp_unit;
    units::precise_unit unit;
    std::string unit_str;
    double value;

  public:

    // constructor
    RealScalar( units::precise_unit const unit_ )
      : inp_unit{ units::precise::invalid }
      , unit{ unit_ }
      , value{ std::numeric_limits<double>::signaling_NaN() }
    {
      if( !units::is_valid( unit ) ) {
        throw( std::invalid_argument( "Invalid unit." ) );
      }
      unit_str = units::to_string( unit );
    }

    // setters
    void Set( double const inp_value
              , std::string const& inp_unit_str ) {
      inp_unit = units::unit_from_string( inp_unit_str );
      if( !units::is_valid( inp_unit ) ) {
        throw( std::invalid_argument( inp_unit_str
                                      + " is not a valid unit." ) );
      }
      if( !inp_unit.is_convertible( unit ) ) {
        throw( std::invalid_argument( inp_unit_str
                                      + " is not convertible to "
                                      + unit_str + '.' ) );
      }
      value = units::convert( inp_value
                              , inp_unit
                              , unit );
    }

    // getters
    double Value() const {
      return value;
    }

    units::precise_unit const Unit() const {
      return unit;
    }

    units::precise_unit const InputUnit() const {
      return inp_unit;
    }

  };

  using CompositionPairs = std::vector<std::pair<std::string, double>>;

  enum class CompositionInputType {
    Invalid
    , Concentration
    , MassFraction
    , MoleFraction
  };

  class Composition {
  private:

    CompositionInputType type;
    units::precise_unit inp_unit;
    units::precise_unit unit;
    // maybe change this to 2 vectors: names and values?
    CompositionPairs composition_pairs;

  public:

    Composition()
      : type{ CompositionInputType::Invalid }
      , inp_unit{ units::precise::invalid }
      , unit{ units::precise::invalid }
      , composition_pairs()
    {}

    void Set( CompositionPairs const& composition_pairs_
              , std::string const& type_str_
              , std::string const& inp_unit_str_ = "" ) {
      composition_pairs = composition_pairs_;
      std::string const type_str = boost::algorithm::to_lower_copy( type_str_ );
      if( "concentration" == type_str ) {
        // only dimensional case
        type = CompositionInputType::Concentration;

        inp_unit = units::unit_from_string( inp_unit_str_ );
        if( !units::is_valid( inp_unit ) ) {
          throw( std::invalid_argument( inp_unit_str_
                                        + " is not a valid unit" ) );
        }

        unit = units::precise_unit( units::mol/units::m.pow(3) );
        if( !inp_unit.is_convertible( unit ) ) {
          throw( std::invalid_argument( inp_unit_str_
                                        + " is not convertible to "
                                        + units::to_string( unit ) ) );
        }

        // convert to internal units
        double const conv_factor = units::convert( 1.0, inp_unit, unit );
        for( auto& pair : composition_pairs ) {
          pair.second *= conv_factor;
        }

      } else if( "mass fraction" == type_str
                 || "mole fraction" == type_str ) {
        // dimensionless cases: ratio, no conv needed
        type = ("mass fraction" == type_str)
          ? CompositionInputType::MassFraction
          : CompositionInputType::MoleFraction;
        unit = inp_unit = units::precise::ratio;
      } else {
        throw( std::invalid_argument( "Composition type is set to \""
                                      + type_str
                                      + "\". The valid types are: \"concentration\", "
                                      "\"mass fraction\" or \"mole fraction\".") );
      }
    }

    CompositionPairs const& Pairs() const {
      return composition_pairs;
    }

    std::vector<std::string> const Species() const {
      std::vector<std::string> species;
      for( auto const& pair : composition_pairs ) {
        species.push_back( pair.first );
      }
      return species;
    }

    std::vector<double> const Values() const {
      std::vector<double> values;
      for( auto const& pair : composition_pairs ) {
        values.push_back( pair.second );
      }
      return values;
    }

    units::precise_unit const Unit() const {
      return unit;
    }

    units::precise_unit const InputUnit() const {
      return inp_unit;
    }

    CompositionInputType Type() const {
      return type;
    }
  };

  class Case : public DescriptionBase {
  public:

    RealScalar pressure;
    RealScalar temperature;
    Composition composition;
    RealScalar time;

    // constructor
    Case()
      : pressure( units::precise::pascal )
      , temperature( units::precise::Kelvin )
      , composition()
      , time{ units::precise::second }
    {}

  };

  class CaseSetBase : public DescriptionBase {
  private:

    Mechanism mechanism;

  public:

    // constructor: can be omitted, default ctor is OK
    CaseSetBase()
      : mechanism()
    {}

    // setters
    void SetMechanism( Mechanism const& mechanism_ ) {
      mechanism = mechanism_;
    }

    // getters
    Mechanism const& GetMechanism() const {
      return mechanism;
    }
  };

  class CaseSet : public CaseSetBase {
  private:

    std::vector<Case> cases;

  public:

    // constructor: can be omitted, default ctor is OK
    CaseSet()
      : CaseSetBase()
      , cases()
    {}

    // setters
    void AddCase( Case const& cas ) {
      cases.push_back( cas );
    }

    // getters
    std::vector<Case> const& Cases() const {
      return cases;
    }

    Case const& GetCase( size_t i ) const {
      return cases[i];
    }

    Case const& operator [] ( size_t i ) {
      return cases[i];
    }
  };

  class Reactor {
  public:
    std::vector<Input::CaseSet> const_P;  // const pressure
    std::vector<Input::CaseSet> const_V;  // const volume
    std::vector<Input::CaseSet> const_TP; // const temperature and pressure
    std::vector<Input::CaseSet> const_TV; // const temperature and volume

    Reactor()
      : const_P()
      , const_V()
      , const_TP()
      , const_TV()
    {}
  };

  class Parameters {
  public:

    Reactor reactor;

    Parameters()
      : reactor()
    {}
  };

} // namespace Input

#endif // INPUT_TYPES_HPP


// Copyright (c) 2019 Hans Dembinski
//
// 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)

// This test is inspired by the corresponding boost/beast test of detail_variant.

#include <cassert>
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/ostream.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/serialization.hpp>
#include "throw_exception.hpp"
#include "utility_axis.hpp"
#include "utility_serialization.hpp"

using namespace boost::histogram::axis;

int main(int argc, char** argv) {
  assert(argc == 2);

  const auto filename = join(argv[1], "axis_variant_serialization_test.xml");

  using R = regular<>;
  using I = integer<>;

  variant<I, R> a(I(0, 3));
  variant<I, R> b(R(1, 0, 1));
  print_xml(filename, b);
  BOOST_TEST_NE(a, b);
  load_xml(filename, a);
  BOOST_TEST_EQ(a, b);

  variant<I> c; // load incompatible version
  BOOST_TEST_THROWS(load_xml(filename, c), std::runtime_error);

  return boost::report_errors();
}


//   Copyright 2015 Patrick Putnam
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   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.
#ifndef CLOTHO_WEIGHT_PARAMETER_HPP_
#define CLOTHO_WEIGHT_PARAMETER_HPP_

#include <boost/property_tree/ptree.hpp>
#include "clotho/utility/clotho_strings.hpp"

#include "clotho/data_spaces/generators/weight_distribution_helper.hpp"

namespace clotho {
namespace genetics {

template < class WeightType >
struct weight_parameter {
    typedef weight_parameter< WeightType >  self_type;

    typedef WeightType  weight_type;
    typedef  weight_distribution_helper< weight_type > dist_helper_type;
    typedef typename dist_helper_type::type distribution_type;

    distribution_type   m_dist;

    weight_parameter( boost::property_tree::ptree & config ) {
        boost::property_tree::ptree lconfig;
        lconfig = config.get_child( TRAIT_BLOCK_K, lconfig );
        m_dist.param( dist_helper_type::makeParameter( lconfig) );

        config.put_child( TRAIT_BLOCK_K, lconfig );
    }

    weight_parameter( const self_type & other ) :
        m_dist( other.m_dist.param() )
    {}

    void write_parameter( boost::property_tree::ptree & l ) {
        boost::property_tree::ptree c;
        dist_helper_type::makeParameter( c );
        l.put_child( TRAIT_BLOCK_K, c );
    }

    virtual ~weight_parameter() {}
};

}   // namespace genetics
}   // namespace clotho

#endif  // CLOTHO_WEIGHT_PARAMETER_HPP_


/* Copyright (C) 2012-2020 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 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. See accompanying LICENSE file.
 */

#if defined(__unix__) || defined(__unix) || defined(unix)
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include <NTL/BasicThreadPool.h>
#include <helib/FHE.h>
#include <helib/EncryptedArray.h>
#include <helib/matmul.h>
#include <helib/debugging.h>
#include <helib/fhe_stats.h>

#include "gtest/gtest.h"
#include "test_common.h"

namespace {

/********************************************************************
 ********************************************************************/
struct Parameters
{
  const long p; // plaintext base
  const long r; // exponent
  // p^r is the plaintext-space modulus
  const long c;        // number of columns in the key-switching matrices
  const long bits;     // # of bits in the modulus chain
  const long skHwt;    // Hamming weight of recryption secret key
  const long nthreads; // number of threads
  const long seed;     // random number seed
  const int useCache;  // 0: zzX cache, 1: DCRT cache
  const int force_bsgs;
  const int force_hoist;
  // int disable_intFactor // fhe_disable_intFactor (disabled by Victor)
  const int chen_han;
  const bool debug; // generate debugging output
  const int scale;  // scale parameter
  const NTL::Vec<long> global_gens;
  const NTL::Vec<long> global_ords;
  const NTL::Vec<long> global_mvec;
  const int c_m; // = 100;
  const long outer_rep;
  const long inner_rep;

  Parameters(long p,
             long r,
             long c,
             long bits,
             long skHwt,
             long nthreads,
             long seed,
             long useCache,
             int c_m,
             int force_bsgs,
             int force_hoist,
             int chen_han,
             bool debug,
             int scale,
             const std::vector<long>& global_gens,
             const std::vector<long>& global_ords,
             const std::vector<long>& global_mvec,
             long outer_rep,
             long inner_rep) :
      p(p),
      r(r),
      c(c),
      bits(bits),
      skHwt(skHwt),
      nthreads(nthreads),
      seed(seed),
      useCache(useCache),
      force_bsgs(force_bsgs),
      force_hoist(force_hoist),
      chen_han(chen_han),
      debug(debug),
      scale(scale),
      global_gens(helib::convert<NTL::Vec<long>>(global_gens)),
      global_ords(helib::convert<NTL::Vec<long>>(global_ords)),
      global_mvec(helib::convert<NTL::Vec<long>>(global_mvec)),
      c_m(c_m),
      outer_rep(outer_rep),
      inner_rep(inner_rep)
  {
    if (global_gens.empty() || global_ords.empty() || global_mvec.empty())
      throw helib::LogicError("gens, ords, and mvec must be non-empty");
  };

  friend std::ostream& operator<<(std::ostream& os, const Parameters& params)
  {
    return os << "{"
              << "p" << params.p << ","
              << "r" << params.r << ","
              << "c" << params.c << ","
              << "bits" << params.bits << ","
              << "skHwt" << params.skHwt << ","
              << "nthreads" << params.nthreads << ","
              << "seed" << params.seed << ","
              << "useCache" << params.useCache << ","
              << "force_bsgs" << params.force_bsgs << ","
              << "force_hoist" << params.force_hoist << ","
              << "chen_han" << params.chen_han << ","
              << "debug" << params.debug << ","
              << "scale" << params.scale << ","
              << "global_gens" << params.global_gens << ","
              << "global_ords" << params.global_ords << ","
              << "global_mvec" << params.global_mvec << ","
              << "c_m" << params.c_m << "}";
  }
};

class GTestFatboot : public ::testing::TestWithParam<Parameters>
{
private:
  void preContextSetup()
  {

    if (!helib_test::noPrint)
      helib::fhe_stats = true;

    if (!helib_test::noPrint) {
      std::cout << "*** GTestFatboot";
      if (helib::isDryRun())
        std::cout << " (dry run)";
      std::cout << ": p=" << p << ", r=" << r << ", bits=" << bits
                << ", t=" << skHwt << ", c=" << c << ", m=" << m
                << " mvec=" << mvec << ", gens=" << helib::vecToStr(gens)
                << ", ords=" << helib::vecToStr(ords) << std::endl;
      std::cout << "Computing key-independent tables..." << std::flush;
    }
    helib::setTimersOn();
    helib::setDryRun(
        false); // Need to get a "real context" to test bootstrapping
    time = -NTL::GetTime();
  }

  void postContextSetup()
  {
    if (scale) {
      context.scale = scale;
    }

    context.zMStar.set_cM(c_m / 100.0);
  }

  static void setGlobals(int force_bsgs, int force_hoist, int chen_han)
  {
    helib::fhe_test_force_bsgs = force_bsgs;
    helib::fhe_test_force_hoist = force_hoist;
    helib::fhe_force_chen_han = chen_han;
  };

  void cleanupBootstrappingGlobals()
  {
    helib::fhe_test_force_bsgs = old_fhe_test_force_bsgs;
    helib::fhe_test_force_hoist = old_fhe_test_force_hoist;
    helib::fhe_force_chen_han = old_fhe_force_chen_han;
  }

  static void setSeedIfNeeded(const long seed)
  {
    if (seed)
      SetSeed(NTL::ZZ(seed));
  };

  static void checkPM(const long p, const long m)
  {
    helib::assertTrue(NTL::GCD(p, m) == 1, "GCD(p, m) == 1");
  }

protected:
  const int old_fhe_test_force_bsgs;
  const int old_fhe_test_force_hoist;
  const int old_fhe_force_chen_han;
  const long p;
  const long r;
  const long c;
  const long bits;
  const long skHwt;
  const long nthreads;
  const long seed;
  const long useCache;
  const int force_bsgs;
  const int force_hoist;
  const int chen_han;
  const bool debug;
  const int scale;
  const NTL::Vec<long> mvec;
  const std::vector<long> gens;
  const std::vector<long> ords;
  const int c_m; // = 100;
  const long outer_rep;
  const long inner_rep;

  const long m, phim;
  double time;
  helib::Context context;

  GTestFatboot() :
      old_fhe_test_force_bsgs(helib::fhe_test_force_bsgs),
      old_fhe_test_force_hoist(helib::fhe_test_force_hoist),
      old_fhe_force_chen_han(helib::fhe_force_chen_han),
      p((setGlobals(GetParam().force_bsgs,
                    GetParam().force_hoist,
                    GetParam().chen_han),
         GetParam().p)),
      r(GetParam().r),
      c(GetParam().c),
      bits(GetParam().bits),
      skHwt(GetParam().skHwt),
      nthreads((NTL::SetNumThreads(GetParam().nthreads), GetParam().nthreads)),
      seed((setSeedIfNeeded(GetParam().seed), GetParam().seed)),
      useCache(GetParam().useCache),
      force_bsgs(GetParam().force_bsgs),
      force_hoist(GetParam().force_hoist),
      chen_han(GetParam().chen_han),
      debug(GetParam().debug),
      scale(GetParam().scale),
      mvec(GetParam().global_mvec),
      gens(helib::convert<std::vector<long>>(GetParam().global_gens)),
      ords(helib::convert<std::vector<long>>(GetParam().global_ords)),
      c_m(GetParam().c_m),
      outer_rep(GetParam().outer_rep),
      inner_rep(GetParam().inner_rep),
      m(helib::computeProd(mvec)),
      phim((checkPM(p, m), helib::phi_N(m))),
      time(0),
      context((preContextSetup(), m), p, r, gens, ords)
  {
    postContextSetup();
  }

  void TearDown() override
  {
    if (!helib_test::noPrint) {
      helib::printAllTimers();
    }
    if (helib::fhe_stats)
      helib::print_stats(std::cout);

    cleanupBootstrappingGlobals();
    helib::cleanupDebugGlobals();
  }
};

TEST_P(GTestFatboot, correctlyPerformsFatboot)
{
  helib::buildModChain(context,
                       bits,
                       c,
                       /*willBeBootstrappable=*/true,
                       /*t=*/skHwt);

  if (!helib_test::noPrint) {
    std::cout << "security=" << context.securityLevel() << std::endl;
    std::cout << "# small primes = " << context.smallPrimes.card() << std::endl;
    std::cout << "# ctxt primes = " << context.ctxtPrimes.card() << std::endl;
    std::cout << "# bits in ctxt primes = "
              << long(context.logOfProduct(context.ctxtPrimes) / log(2.0) + 0.5)
              << std::endl;
    std::cout << "# special primes = " << context.specialPrimes.card()
              << std::endl;
    std::cout << "# bits in special primes = "
              << long(context.logOfProduct(context.specialPrimes) / log(2.0) +
                      0.5)
              << std::endl;
    std::cout << "scale=" << context.scale << std::endl;
  }

  context.makeBootstrappable(mvec, /*t=*/skHwt, useCache);
  time += NTL::GetTime();

  if (!helib_test::noPrint) {
    std::cout << " done in " << time << " seconds" << std::endl;
    std::cout << "  e=" << context.rcData.e << ", e'=" << context.rcData.ePrime
              << ", t=" << context.rcData.skHwt << std::endl
              << "  ";
    context.zMStar.printout();
  }
  helib::setDryRun(
      helib_test::dry); // Now we can set the dry-run flag if desired

  long p2r = context.alMod.getPPowR();

  for (long numkey = 0; numkey < outer_rep; numkey++) { // test with 3 keys

    time = -NTL::GetTime();
    if (!helib_test::noPrint)
      std::cout << "Generating keys, " << std::flush;
    helib::SecKey secretKey(context);
    helib::PubKey& publicKey = secretKey;
    secretKey.GenSecKey(skHwt); // A +-1/0 secret key
    helib::addSome1DMatrices(
        secretKey); // compute key-switching matrices that we need
    helib::addFrbMatrices(secretKey);
    if (!helib_test::noPrint)
      std::cout << "computing key-dependent tables..." << std::flush;
    secretKey.genRecryptData();
    time += NTL::GetTime();
    if (!helib_test::noPrint)
      std::cout << " done in " << time << " seconds\n";

    NTL::zz_p::init(p2r);
    NTL::zz_pX poly_p = NTL::random_zz_pX(context.zMStar.getPhiM());
    helib::zzX poly_p1 = helib::balanced_zzX(poly_p);
    NTL::ZZX ptxt_poly = helib::convert<NTL::ZZX>(poly_p1);
    NTL::ZZX ptxt_poly1;
    helib::PolyRed(ptxt_poly1, ptxt_poly, p2r, true);
    // this is the format produced by decryption

    helib::setupDebugGlobals(&secretKey, context.ea);

    NTL::ZZX poly2;
    helib::Ctxt c1(publicKey);

    secretKey.Encrypt(c1, ptxt_poly, p2r);

    helib::resetAllTimers();
    for (long num = 0; num < inner_rep; num++) { // multiple tests with same key
      publicKey.reCrypt(c1);
      secretKey.Decrypt(poly2, c1);

      EXPECT_EQ(ptxt_poly1, poly2);
    }
  }
  if (!helib_test::noPrint)
    helib::printAllTimers();
#if (defined(__unix__) || defined(__unix) || defined(unix))
  struct rusage rusage;
  getrusage(RUSAGE_SELF, &rusage);
  if (!helib_test::noPrint)
    std::cout << "  rusage.ru_maxrss=" << rusage.ru_maxrss << std::endl;
#endif
  if (helib::fhe_stats)
    helib::print_stats(std::cout);
}

// LEGACY TEST DEFAULT PARAMETERS:
// long p=2;
// long r=1;
// long c=3;
// long bits=600;
// long t=64;
// long nthreads=1;
// long seed=0;
// long useCache=1;
// int c_m = 100;

// clang-format off
INSTANTIATE_TEST_SUITE_P(typicalParameters, GTestFatboot, ::testing::Values(
    //SLOW
    Parameters( 2, 1, 3, 600, 64, 1, 0, 1, 100, 0, 0, 0, 0, 0, {1026,  249}, {30, -2}, {  31, 41}, 1, 1),
    Parameters(17, 1, 3, 600, 64, 1, 0, 1, 100, 0, 0, 0, 0, 0, { 556, 1037}, { 6,  4}, {7, 5, 37}, 1, 1)
    //FAST
    // Parameters( 2, 1, 3, 600, 64, 1, 0, 1, 100, 0, 0, 0, 0, 0, {1026,  249}, {30, -2}, {  31, 41}, 1, 1)
    ));
// clang-format on
} // namespace


/******************************************************************************
 * Copyright 2018 The Apollo 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 obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *****************************************************************************/

#include "cyber/cyber.h"
#include "modules/drivers/proto/pointcloud.pb.h"
#include <iostream>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/voxel_grid.h>
#include <boost/thread/thread.hpp>

using namespace std;

pcl::visualization::CloudViewer viewer("point cloud viewer");
//boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("viewer test"));

//init para
float filterRes = 0.4;

void MessageCallback(
		const std::shared_ptr<apollo::drivers::PointCloud>& msg) {
  cout << "-----------------------" << msg->point_size() << endl;
//init viewer
//  viewer->initCameraParameters();
//  int v1(0);
//  int v2(0);
//  viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
//  viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);

  pcl::PointCloud<pcl::PointXYZ>::Ptr pcloud(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>& cloud = *pcloud;
//  pcl::PointCloud<pcl::PointXYZ> cloud;
  cloud.width = msg->point_size();
  cloud.height = 1;
  cloud.is_dense = false;
  cloud.points.resize(cloud.width * cloud.height);
  for (size_t i = 0; i < cloud.points.size(); ++i) {
    cloud.points[i].x = msg->point(i).x();
    cloud.points[i].y = msg->point(i).y();
    cloud.points[i].z = msg->point(i).z();
  }

//  save point_cloud to pcd file
//  pcl::io::savePCDFileASCII("/apollo/write_pcd_test.pcd", cloud);

// visualize point_cloud
//  viewer.showCloud(pcloud);

//voxel frid filter
//  pcl::PointCloud<pcl::PointXYZ>::Ptr cloudFiltered(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::VoxelGrid<pcl::PointXYZ> vg;
  vg.setInputCloud(pcloud);
  vg.setLeafSize(filterRes, filterRes, filterRes);
  vg.filter(*pcloud);
  viewer.showCloud(pcloud);

//  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> pcloud_handler(pcloud, 250, 0, 0);
//  viewer->addPointCloud(pcloud, "window1", v1);
//  viewer->addPointCloud(pcloud, "cloud", v1);
//  viewer->addPointCloud(pcloud, pcloud_handler, "window2", v2);
//  viewer->spinOnce();
}

int main(int argc, char* argv[]) {
  // init cyber framework
  apollo::cyber::Init(argv[0]);
  // create listener node
  auto listener_node = apollo::cyber::CreateNode("listener");
  // create listener
  auto listener = listener_node->CreateReader<apollo::drivers::PointCloud>(
		  "/diamond/sensor/lidar/front/PointCloud2", MessageCallback);

  apollo::cyber::WaitForShutdown();
  return 0;
}


#pragma once
#include <Eigen/Geometry>

#include "pipelinemodule.hpp"
#include "types.h"
#include "pointcloud.h"

namespace PoseEstimation
{
    /**
     * @brief Abstract module for registering two point clouds.
     */
    template<typename PointT>
    class PoseRefiner : public PipelineModule
    {
    public:
        PoseRefiner() : PipelineModule(PipelineModuleType::PoseRefiner)
        {}

        /**
         * @brief Align the source point cloud to the target point cloud.
         * @param source Point cloud to be aligned.
         * @param target Point cloud to be aligned to.
         * @param out Transformed input point cloud.
         * @param transformation Final rigid transformation that aligns source cloud to target.
         * @return Whether the refinement process has converged.
         */
        virtual bool refine(PC<PointT> &source, PC<PointT> &target,
                            PC<PointT> &out, Eigen::Matrix4f &transformation) = 0;
    };
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<int, 3, 5> test_int_3_5(const Eigen::Matrix<int, 3, 5> & M)
{
	return M;
}
void export_int_3_5()
{
	boost::python::def("test_int_3_5",test_int_3_5);
}


#define BOOST_TEST_MODULE communicator_gatherv

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

template<typename T>
bool gatherv_test(const T &val) {
  const mpl::communicator &comm_world{mpl::environment::comm_world()};
  const int N{(comm_world.size() * comm_world.size() + comm_world.size()) / 2};
  std::vector<T> v_gather(N);
  std::vector<T> v_send(comm_world.rank() + 1);
  std::vector<T> v_expected(N);
  std::iota(begin(v_expected), end(v_expected), val);
  mpl::layouts<T> layouts;
  for (int i{0}, i_end{comm_world.size()}, offset{0}; i < i_end; ++i) {
    layouts.push_back(mpl::indexed_layout<T>({{i + 1, offset}}));
    offset += i + 1;
  }
  T t_val{val};
  for (int i{0}, i_end{(comm_world.rank() * comm_world.rank() + comm_world.rank()) / 2};
       i < i_end; ++i)
    ++t_val;
  std::iota(begin(v_send), end(v_send), t_val);
  mpl::vector_layout<T> layout(comm_world.rank() + 1);
  if (comm_world.rank() == 0) {
    comm_world.gatherv(0, v_send.data(), layout, v_gather.data(), layouts);
    return v_gather == v_expected;
  } else {
    comm_world.gatherv(0, v_send.data(), layout);
    return true;
  }
}

template<typename T>
bool igatherv_test(const T &val) {
  const mpl::communicator &comm_world{mpl::environment::comm_world()};
  const int N{(comm_world.size() * comm_world.size() + comm_world.size()) / 2};
  std::vector<T> v_gather(N);
  std::vector<T> v_send(comm_world.rank() + 1);
  std::vector<T> v_expected(N);
  std::iota(begin(v_expected), end(v_expected), val);
  mpl::layouts<T> layouts;
  for (int i{0}, i_end{comm_world.size()}, offset{0}; i < i_end; ++i) {
    layouts.push_back(mpl::indexed_layout<T>({{i + 1, offset}}));
    offset += i + 1;
  }
  T t_val{val};
  for (int i{0}, i_end{(comm_world.rank() * comm_world.rank() + comm_world.rank()) / 2};
       i < i_end; ++i)
    ++t_val;
  std::iota(begin(v_send), end(v_send), t_val);
  mpl::vector_layout<T> layout(comm_world.rank() + 1);
  if (comm_world.rank() == 0) {
    auto r{comm_world.igatherv(0, v_send.data(), layout, v_gather.data(), layouts)};
    r.wait();
    return v_gather == v_expected;
  } else {
    auto r{comm_world.igatherv(0, v_send.data(), layout)};
    r.wait();
    return true;
  }
}

BOOST_AUTO_TEST_CASE(gatherv) {
  BOOST_TEST(gatherv_test(1.0));
  BOOST_TEST(gatherv_test(tuple{1, 2.0}));

  BOOST_TEST(igatherv_test(1.0));
  BOOST_TEST(igatherv_test(tuple{1, 2.0}));
}


#define BOOST_TEST_MODULE woat_triangle_problem_test
#include <boost/test/unit_test.hpp>
#include "../src/Triangle.h"

BOOST_AUTO_TEST_CASE(equilateral_test)
{
    std::vector<unsigned long long> sides(3, 3);
    Triangle triangle;
    BOOST_TEST_REQUIRE(triangle.Initialize(sides) == SUCCESS);
    BOOST_TEST(triangle.IsEquilateral());
}

BOOST_AUTO_TEST_CASE(ordinary_test)
{
    std::vector<unsigned long long> sides;
    sides.push_back(2);
    sides.push_back(3);
    sides.push_back(4);
    Triangle triangle;
    BOOST_TEST_REQUIRE(triangle.Initialize(sides) == SUCCESS);
    BOOST_TEST(!triangle.IsEquilateral());
    BOOST_TEST(!triangle.IsIsosceles());
}

BOOST_AUTO_TEST_CASE(isosceles_test)
{
    std::vector<unsigned long long> sides(3);
    Triangle triangle;

    sides[0] = 2;
    sides[1] = 2;
    sides[2] = 3;

    BOOST_TEST_REQUIRE(triangle.Initialize(sides) == SUCCESS);
    BOOST_TEST(triangle.IsIsosceles());

    sides[0] = 2;
    sides[1] = 3;
    sides[2] = 2;

    BOOST_TEST_REQUIRE(triangle.Initialize(sides) == SUCCESS);
    BOOST_TEST(triangle.IsIsosceles());

    sides[0] = 3;
    sides[1] = 2;
    sides[2] = 2;

    BOOST_TEST_REQUIRE(triangle.Initialize(sides) == SUCCESS);
    BOOST_TEST(triangle.IsIsosceles());
}

BOOST_AUTO_TEST_CASE(zero_side_test)
{
    std::vector<unsigned long long> sides(3);
    Triangle triangle;

    sides[0] = 0;
    sides[1] = 1;
    sides[2] = 1;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 1;
    sides[1] = 0;
    sides[2] = 1;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 1;
    sides[1] = 1;
    sides[2] = 0;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 0;
    sides[1] = 0;
    sides[2] = 1;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 0;
    sides[1] = 1;
    sides[2] = 0;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 1;
    sides[1] = 0;
    sides[2] = 0;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 0;
    sides[1] = 0;
    sides[2] = 0;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);
}

BOOST_AUTO_TEST_CASE(sum_of_two_equals_third_test)
{
    std::vector<unsigned long long> sides(3);
    Triangle triangle;

    sides[0] = 1;
    sides[1] = 3;
    sides[2] = 2;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 3;
    sides[1] = 1;
    sides[2] = 2;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 1;
    sides[1] = 2;
    sides[2] = 3;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);
}

BOOST_AUTO_TEST_CASE(sum_of_two_less_than_third_test)
{
    std::vector<unsigned long long> sides(3);
    Triangle triangle;

    sides[0] = 1;
    sides[1] = 4;
    sides[2] = 2;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 4;
    sides[1] = 1;
    sides[2] = 2;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides[0] = 1;
    sides[1] = 2;
    sides[2] = 4;

    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);
}

BOOST_AUTO_TEST_CASE(wrong_sides_count_test)
{
    std::vector<unsigned long long> sides;
    Triangle triangle;

    sides.push_back(1);
    sides.push_back(1);
    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);

    sides.push_back(1);
    sides.push_back(1);
    BOOST_TEST(triangle.Initialize(sides) == NOT_A_TRIANGLE);
}

BOOST_AUTO_TEST_CASE(too_large_sides_test)
{
    unsigned long long maxSide = (unsigned long long)0 - 1, halfMaxSide = maxSide / 2;

    std::vector<unsigned long long> sides(3);
    Triangle triangle;

    sides[0] = halfMaxSide;
    sides[1] = halfMaxSide;
    sides[2] = halfMaxSide;

    BOOST_TEST(triangle.Initialize(sides) == SUCCESS);

    sides[0] = halfMaxSide + 1;
    sides[1] = halfMaxSide;
    sides[2] = halfMaxSide;

    BOOST_TEST(triangle.Initialize(sides) == SUCCESS);

    sides[0] = halfMaxSide;
    sides[1] = halfMaxSide + 1;
    sides[2] = halfMaxSide;

    BOOST_TEST(triangle.Initialize(sides) == SUCCESS);

    sides[0] = halfMaxSide;
    sides[1] = halfMaxSide;
    sides[2] = halfMaxSide + 1;

    BOOST_TEST(triangle.Initialize(sides) == SUCCESS);

    sides[0] = halfMaxSide + 1;
    sides[1] = halfMaxSide + 1;
    sides[2] = halfMaxSide;

    BOOST_TEST(triangle.Initialize(sides) != SUCCESS);

    sides[0] = halfMaxSide + 1;
    sides[1] = halfMaxSide;
    sides[2] = halfMaxSide + 1;

    BOOST_TEST(triangle.Initialize(sides) != SUCCESS);

    sides[0] = halfMaxSide;
    sides[1] = halfMaxSide + 1;
    sides[2] = halfMaxSide + 1;

    BOOST_TEST(triangle.Initialize(sides) != SUCCESS);

    sides[0] = halfMaxSide + 1;
    sides[1] = halfMaxSide + 1;
    sides[2] = halfMaxSide + 1;

    BOOST_TEST(triangle.Initialize(sides) != SUCCESS);
}


#include<bits/stdc++.h>
using namespace std;
#include <iostream>                  // for std::cout
#include <utility>                   // for std::pair
#include <algorithm>                 // for std::for_each
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

using namespace boost;
#include "boost/Advanced-Intrusive-master/HLD/hld_ds.hpp"
using namespace boost::graph;
int main()
{
    int i,n,a,b;
    scanf("%d",&n);
    //Graph type
    typedef adjacency_list<vecS, vecS, undirectedS> Graph_list;
    //Initialisation of Graph object
    Graph_list graph_list(n);
    for(i=1;i<n;i++)
    {
        scanf("%d %d",&a,&b);
        add_edge(a,b,graph_list);
    }
    //Decomposition of input graph by HLD
    HLD<Graph_list> decomposed_tree(0,n,graph_list);
    //getting head of chain to which a node belongs to
    std::cout << decomposed_tree.get_chain_head(0) << "\n";
    //getting size of chain to which node belongs to
    std::cout << decomposed_tree.get_chain_size(0) << "\n";
    //returns the heavy child of input node
    std::cout << decomposed_tree.get_heavy_child(0) << "\n";

    return 0;

}