/* boost random_test.cpp various tests
 *
 * Copyright Jens Maurer 2000
 * 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)
 *
 * $Id: random_test.cpp,v 1.57 2005/06/16 09:56:21 johnmaddock Exp $
 */

#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
#pragma warning( disable : 4786 )
#endif

#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <iterator>
#include <vector>
#include <boost/random.hpp>
#include <boost/config.hpp>

#include <boost/test/test_tools.hpp>
#include <boost/test/included/test_exec_monitor.hpp>

#ifdef BOOST_NO_STDC_NAMESPACE
  namespace std { using ::abs; using ::fabs; using ::pow; }
#endif

/*
 * General portability note:
 * MSVC mis-compiles explicit function template instantiations.
 * For example, f<A>() and f<B>() are both compiled to call f<A>().
 * BCC is unable to implicitly convert a "const char *" to a std::string
 * when using explicit function template instantiations.
 *
 * Therefore, avoid explicit function template instantiations.
 */

/*
 * Validate correct implementation
 */

// own run
bool check(unsigned long x, const boost::mt11213b&) { return x == 3809585648U; }

// validation by experiment from mt19937.c
bool check(unsigned long x, const boost::mt19937&) { return x == 4123659995U; }

// validation values from the publications
bool check(int x, const boost::minstd_rand0&) { return x == 1043618065; }

// validation values from the publications
bool check(int x, const boost::minstd_rand&) { return x == 399268537; }

#if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T)
// by experiment from lrand48()
bool check(unsigned long x, const boost::rand48&) { return x == 1993516219; }
#endif

// ????
bool check(unsigned long x, const boost::taus88&) { return x == 3535848941U; }

// ????
bool check(int x, const boost::ecuyer1988&) { return x == 2060321752; }

// validation by experiment from Harry Erwin's generator.h (private e-mail)
bool check(unsigned int x, const boost::kreutzer1986&) { return x == 139726; }

bool check(double x, const boost::lagged_fibonacci607&) { return std::abs(x-0.401269) < 1e-5; }

// principal operation validated with CLHEP, values by experiment
bool check(unsigned long x, const boost::ranlux3&) { return x == 5957620; }
bool check(unsigned long x, const boost::ranlux4&) { return x == 8587295; }

bool check(float x, const boost::ranlux3_01&)
{ return std::abs(x-5957620/std::pow(2.0f,24)) < 1e-6; }
bool check(float x, const boost::ranlux4_01&)
{ return std::abs(x-8587295/std::pow(2.0f,24)) < 1e-6; }

bool check(double x, const boost::ranlux64_3_01&)
{ return std::abs(x-0.838413) < 1e-6; }
bool check(double x, const boost::ranlux64_4_01&)
{ return std::abs(x-0.59839) < 1e-6; }

template<class PRNG>
void validate(const std::string & name, const PRNG &)
{
  std::cout << "validating " << name << ": ";
  PRNG rng;  // default ctor
  for(int i = 0; i < 9999; i++)
    rng();
  typename PRNG::result_type val = rng();
  // make sure the validation function is a static member
  bool result = check(val, rng);

  // allow for a simple eyeball check for MSVC instantiation brokenness
  // (if the numbers for all generators are the same, it's obviously broken)
  std::cout << val << std::endl;
  BOOST_CHECK(result);
}

void validate_all()
{
  using namespace boost;
#if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T)
  validate("rand48", rand48());
#endif
  validate("minstd_rand", minstd_rand());
  validate("minstd_rand0", minstd_rand0());
  validate("ecuyer combined", ecuyer1988());
  validate("mt11213b", mt11213b());
  validate("mt19937", mt19937());
  validate("kreutzer1986", kreutzer1986());
  validate("ranlux3", ranlux3());
  validate("ranlux4", ranlux4());
  validate("ranlux3_01", ranlux3_01());
  validate("ranlux4_01", ranlux4_01());
  validate("ranlux64_3_01", ranlux64_3_01());
  validate("ranlux64_4_01", ranlux64_4_01());
  validate("taus88", taus88());
  validate("lagged_fibonacci607", lagged_fibonacci607());
}

/*
 * Check function signatures
 */

#if BOOST_WORKAROUND( __BORLANDC__, BOOST_TESTED_AT( 0x570) )
#pragma warn -par
#endif
template<class URNG, class Dist>
void instantiate_dist(URNG& urng, const char * name, const Dist& dist)
{
  // this makes a copy of urng
  boost::variate_generator<URNG, Dist> gen(urng, dist);

  // this keeps a reference to urng
  boost::variate_generator<URNG&, Dist> genref(urng, dist);

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
  // and here is a pointer to (a copy of) the urng
  URNG copy = urng;
  boost::variate_generator<URNG*, Dist> genptr(&copy, dist);
#endif

  for(int i = 0; i < 1000; ++i) {
    (void) gen();
    (void) genref();
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
    (void) genptr();
#endif
  }
  typename Dist::result_type g = gen();
  BOOST_CHECK(std::abs(g - genref()) < 1e-6);
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
  BOOST_CHECK(std::abs(g - genptr()) < 1e-6);
#endif

  (void) gen.engine();
  gen.distribution().reset();

  Dist d = dist;            // copy ctor
  d = dist;                 // copy assignment

#if !defined(BOOST_NO_OPERATORS_IN_NAMESPACE) && !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
  {
    std::ostringstream file;
    file << urng << std::endl;
    file << d;
    std::istringstream input(file.str());
    // std::cout << file.str() << std::endl;
    URNG restored_engine;
    input >> restored_engine;
    input >> std::ws;
    Dist restored_dist;
    input >> restored_dist;
#if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // MSVC brokenness
    boost::variate_generator<URNG, Dist> old(urng, d);
    boost::variate_generator<URNG, Dist> restored(restored_engine, restored_dist);
    // advance some more so that state is exercised
    for(int i = 0; i < 1000; ++i) {
      (void) old();
      (void) restored();
    }
    BOOST_CHECK_MESSAGE((std::abs(old()-restored()) < 0.0001),
                        (std::string(name) + " old == restored_dist"));
#endif // BOOST_MSVC
  }
#endif // BOOST_NO_OPERATORS_IN_NAMESPACE
}

template<class URNG, class RealType>
void instantiate_real_dist(URNG& urng, RealType /* ignored */)
{
  instantiate_dist(urng, "uniform_real",
                   boost::uniform_real<RealType>(0, 2.1));
  instantiate_dist(urng, "triangle_distribution",
                   boost::triangle_distribution<RealType>(1, 1.5, 7));
  instantiate_dist(urng, "exponential_distribution",
                   boost::exponential_distribution<RealType>(5));
  instantiate_dist(urng, "normal_distribution",
                   boost::normal_distribution<RealType>());
  instantiate_dist(urng, "lognormal_distribution",
                   boost::lognormal_distribution<RealType>(1, 1));
  instantiate_dist(urng, "cauchy_distribution",
                   boost::cauchy_distribution<RealType>(1));
  instantiate_dist(urng, "gamma_distribution",
                   boost::gamma_distribution<RealType>(1));
}

template<class URNG, class ResultType>
void instantiate_urng(const std::string & s, const URNG &, const ResultType &)
{
  std::cout << "Basic tests for " << s;
  URNG urng;
  urng.seed();                                  // seed() member function
  int a[URNG::has_fixed_range ? 5 : 10];        // compile-time constant
  (void) a;   // avoid "unused" warning
  typename URNG::result_type x1 = urng();
  ResultType x2 = x1;
  (void) &x2;           // avoid "unused" warning

  URNG urng2 = urng;             // copy constructor
#if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // MSVC brokenness
  BOOST_CHECK(urng == urng2);     // operator==
  BOOST_CHECK(!(urng != urng2));  // operator!=
  urng();
  urng2 = urng;                  // copy assignment
  BOOST_CHECK(urng == urng2);
#endif // BOOST_MSVC

  const std::vector<int> v(9999u, 0x41);
  std::vector<int>::const_iterator it = v.begin();
  std::vector<int>::const_iterator it_end = v.end();
  URNG urng3(it, it_end);
  BOOST_CHECK(it != v.begin());
  std::cout << "; seeding uses " << (it - v.begin()) << " words" << std::endl;

  bool have_exception = false;
  try {
    // now check that exceptions are thrown
    it = v.end();
    urng3.seed(it, it_end);
  } catch(std::invalid_argument& x) {
    have_exception = true;
  }
  BOOST_CHECK(have_exception);

  // check for min/max members
  ResultType min = (urng3.min)();
  (void) &min;
  ResultType max = (urng3.max)();
  (void) &max;

#if !defined(BOOST_NO_OPERATORS_IN_NAMESPACE) && !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
  // Streamable concept not supported for broken compilers

  // advance a little so that state is relatively arbitrary
  for(int i = 0; i < 9307; ++i)
    urng();
  urng2 = urng;

  {
    // narrow stream first
    std::ostringstream file;
    file << urng;
    // move forward
    urng();
    // restore old state
    std::istringstream input(file.str());
    input >> urng;
    // std::cout << file.str() << std::endl;
#if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // MSVC brokenness
    // advance some more so that state is exercised
    for(int i = 0; i < 10000; ++i) {
      urng();
      urng2();
    }
    BOOST_CHECK(urng == urng2);
#endif // BOOST_MSVC
  }

  urng2 = urng;
#if !defined(BOOST_NO_STD_WSTREAMBUF) && !defined(BOOST_NO_STD_WSTRING)
  {
    // then wide stream
    std::wostringstream file;
    file << urng;
    // move forward
    urng();
    std::wistringstream input(file.str());
    input >> urng;
#if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // MSVC brokenness
    // advance some more so that state is exercised
    for(int i = 0; i < 10000; ++i) {
      urng();
      urng2();
    }
    BOOST_CHECK(urng == urng2);
#endif // BOOST_MSVC
  }
#endif // BOOST_NO_STD_WSTREAMBUF, BOOST_NO_STD_WSTRING
#endif // BOOST_NO_OPERATORS_IN_NAMESPACE etc.

  // instantiate various distributions with this URNG
  // instantiate_dist(urng, "uniform_smallint", boost::uniform_smallint(0, 11));
  instantiate_dist(urng, "uniform_int", boost::uniform_int<>(-200, 20000));
  instantiate_dist(urng, "bernoulli_distribution",
                   boost::bernoulli_distribution<>(0.2));
  instantiate_dist(urng, "binomial_distribution",
                   boost::binomial_distribution<>(4, 0.2));
  instantiate_dist(urng, "geometric_distribution",
                   boost::geometric_distribution<>(0.8));
  instantiate_dist(urng, "poisson_distribution",
                   boost::poisson_distribution<>(1));

  instantiate_real_dist(urng, 1.0f);
  instantiate_real_dist(urng, 1.0);
  instantiate_real_dist(urng, 1.0l);

#if 0
  // We cannot compare the outcomes before/after save with std::abs(x-y)
  instantiate_dist("uniform_on_sphere",
                   boost::uniform_on_sphere<URNG>(urng, 2));
#endif
}

void instantiate_all()
{
  using namespace boost;

#if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T)
  instantiate_urng("rand48", rand48(), 0);
  rand48 rnd(boost::int32_t(5));
  rand48 rnd2(boost::uint64_t(0x80000000) * 42);
  rnd.seed(boost::int32_t(17));
  rnd2.seed(boost::uint64_t(0x80000000) * 49);
#endif

  instantiate_urng("minstd_rand0", minstd_rand0(), 0);
  instantiate_urng("minstd_rand", minstd_rand(), 0);
  minstd_rand mstd(42);
  mstd.seed(17);

  instantiate_urng("ecuyer1988", ecuyer1988(), 0);
  instantiate_urng("kreutzer1986", kreutzer1986(), 0);
  instantiate_urng("hellekalek1995", hellekalek1995(), 0);

  instantiate_urng("mt11213b", mt11213b(), 0u);
  instantiate_urng("mt19937", mt19937(), 0u);

  mt19937 mt(boost::uint32_t(17));  // needs to be an exact type match for MSVC
  int i = 42;
  mt.seed(boost::uint32_t(i));
  mt19937 mt2(mstd);
  mt2.seed(mstd);

  random_number_generator<mt19937> std_rng(mt2);
  (void) std_rng(10);

  instantiate_urng("lagged_fibonacci",
                   boost::random::lagged_fibonacci<boost::uint32_t, 24, 607, 273>(),
                   0u);
  instantiate_urng("lagged_fibonacci607", lagged_fibonacci607(), 0.0);

  instantiate_urng("ranlux3", ranlux3(), 0u);
  instantiate_urng("ranlux4", ranlux4(), 0u);

  instantiate_urng("ranlux3_01", ranlux3_01(), 0.0f);
  instantiate_urng("ranlux4_01", ranlux4_01(), 0.0f);

  instantiate_urng("ranlux64_3_01", ranlux64_3_01(), 0.0);
  instantiate_urng("ranlux64_4_01", ranlux64_4_01(), 0.0);

  instantiate_urng("taus88", taus88(), 0u);
}

/*
 * A few equidistribution tests
 */

// yet to come...

template<class Generator>
void check_uniform_int(Generator & gen, int iter)
{
  std::cout << "testing uniform_int(" << (gen.min)() << "," << (gen.max)()
            << ")" << std::endl;
  int range = (gen.max)()-(gen.min)()+1;
  std::vector<int> bucket(range);
  for(int j = 0; j < iter; j++) {
    int result = gen();
    if(result < (gen.min)() || result > (gen.max)())
      std::cerr << "   ... delivers " << result << std::endl;
    else
      bucket[result-(gen.min)()]++;
  }
  int sum = 0;
  // use a different variable name "k", because MSVC has broken "for" scoping
  for(int k = 0; k < range; k++)
    sum += bucket[k];
  double avg = static_cast<double>(sum)/range;
  double threshold = 2*avg/std::sqrt(static_cast<double>(iter));
  for(int i = 0; i < range; i++) {
    if(std::fabs(bucket[i] - avg) > threshold) {
      // 95% confidence interval
      std::cout << "   ... has bucket[" << i << "] = " << bucket[i]
                << "  (distance " << (bucket[i] - avg) << ")"
                << std::endl;
    }
  }
}

template<class Generator>
void test_uniform_int(Generator & gen)
{
  typedef boost::uniform_int<int> int_gen;

  // large range => small range (modulo case)
  typedef boost::variate_generator<Generator&, int_gen> level_one;

  level_one uint12(gen, int_gen(1,2));
  BOOST_CHECK((uint12.distribution().min)() == 1);
  BOOST_CHECK((uint12.distribution().max)() == 2);
  check_uniform_int(uint12, 100000);
  level_one uint16(gen, int_gen(1,6));
  check_uniform_int(uint16, 100000);

  // test chaining to get all cases in operator()

  // identity map
  typedef boost::variate_generator<level_one&, int_gen> level_two;
  level_two uint01(uint12, int_gen(0, 1));
  check_uniform_int(uint01, 100000);

  // small range => larger range
  level_two uint05(uint12, int_gen(-3, 2));
  check_uniform_int(uint05, 100000);

  // larger => small range, rejection case
  typedef boost::variate_generator<level_two&, int_gen> level_three;
  level_three uint1_4(uint05, int_gen(1, 4));
  check_uniform_int(uint1_4, 100000);
}

#if defined(BOOST_MSVC) && _MSC_VER <= 1200

// These explicit instantiations are necessary, otherwise MSVC does
// not find the <boost/operators.hpp> inline friends.
// We ease the typing with a suitable preprocessor macro.
#define INSTANT(x) \
template class boost::uniform_smallint<x>; \
template class boost::uniform_int<x>; \
template class boost::uniform_real<x>; \
template class boost::bernoulli_distribution<x>; \
template class boost::geometric_distribution<x>; \
template class boost::triangle_distribution<x>; \
template class boost::exponential_distribution<x>; \
template class boost::normal_distribution<x>; \
template class boost::uniform_on_sphere<x>; \
template class boost::lognormal_distribution<x>;

INSTANT(boost::minstd_rand0)
INSTANT(boost::minstd_rand)
INSTANT(boost::ecuyer1988)
INSTANT(boost::kreutzer1986)
INSTANT(boost::hellekalek1995)
INSTANT(boost::mt19937)
INSTANT(boost::mt11213b)

#undef INSTANT
#endif

#if !defined(BOOST_NO_INT64_T) && !defined(BOOST_NO_INTEGRAL_INT64_T)
// testcase by Mario R�tti
class ruetti_gen
{
public:
  typedef boost::uint64_t result_type;
  result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
  result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return std::numeric_limits<result_type>::max BOOST_PREVENT_MACRO_SUBSTITUTION (); }
  result_type operator()() { return (max)()-1; }
};

void test_overflow_range()
{
  ruetti_gen gen;
  boost::variate_generator<ruetti_gen, boost::uniform_int<> >
    rng(gen, boost::uniform_int<>(0, 10));
  for (int i=0;i<10;i++)
    (void) rng();
}
#else
void test_overflow_range()
{ }
#endif

int test_main(int, char*[])
{

#if !defined(__INTEL_COMPILER) || !defined(_MSC_VER) || __INTEL_COMPILER > 700
  instantiate_all();
  validate_all();
  boost::mt19937 mt;
  test_uniform_int(mt);

  // bug report from Ken Mahler:  This used to lead to an endless loop.
  typedef boost::uniform_int<unsigned int> uint_dist;
  boost::minstd_rand mr;
  boost::variate_generator<boost::minstd_rand, uint_dist> r2(mr,
                                                            uint_dist(0, 0xffffffff));
  r2();
  r2();

  // bug report from Fernando Cacciola:  This used to lead to an endless loop.
  // also from Douglas Gregor
  boost::variate_generator<boost::minstd_rand, boost::uniform_int<> > x(mr, boost::uniform_int<>(0, 8361));
  (void) x();

  // bug report from Alan Stokes and others: this throws an assertion
  boost::variate_generator<boost::minstd_rand, boost::uniform_int<> > y(mr, boost::uniform_int<>(1,1));
  std::cout << "uniform_int(1,1) " << y() << ", " << y() << ", " << y()
            << std::endl;

  test_overflow_range();

  return 0;
#else
  std::cout << "Intel 7.00 on Win32 loops, so the test is disabled\n";
  return 1;
#endif
}


/*
 * Box.hpp
 *
 *  Created on: Feb 20, 2013
 *      Author: petr
 */
#pragma once
#ifndef BOUNDINGBOX_HPP_
#define BOUNDINGBOX_HPP_

#undef max
#undef min

#include <iostream>
#include <limits>
#include <Eigen/Dense>
#include "TriangleElement.hpp"

/// \brief Class representing simple bounding box.
///
/// Bounding box has one static method used for detecting intersection between two of them.
///
/// \tparam type Specifies the type of bounding box (double, int, ...)
/// \tparam dim Specifies the dimension of the bounding box (2 or 3)
template <typename type, int dim>
struct Box {

	// bottom left and top right corners
	type _bl[dim], _tr[dim];
//	double center[dim], extent[dim];

	/// \brief Default constructor. Initializes bounding box from [0,0,0,..] to [1,1,1,..]
	Box() {
		for (int i = 0; i < dim; i++) {

//			center[i] = 0.0;
//			extent[i] = -std::numeric_limits<type>::max();
			_bl[i] =  std::numeric_limits<type>::max();
			_tr[i] = -std::numeric_limits<type>::max();

		}

	}

	Box(const Box<type, dim>& b) {

		for (int i = 0; i < dim; ++i) {

//			this->center[i] = b.center[i];
//			this->extent[i] = b.extent[i];
			_bl[i] = b._bl[i];
			_tr[i] = b._tr[i];

		}

	}

	Box(const type minx[dim], const type maxx[dim]) {

		for (int i = 0; i < dim; ++i) {

			_bl[i] = minx[i];
			_tr[i] = maxx[i];

		}

	}

	Box(const type point[dim]) {

		for (int i = 0; i < dim; ++i) {

			_bl[i] = _tr[i] = point[i];

		}

	}

	Box(const Eigen::Vector3d& point) {

		for (int i = 0; i < dim; ++i) {

			_bl[i] = _tr[i] = point[i];

		}

	}

	static Box fromCenterExtent(type center[dim], type extent[dim]) {
		type _bl[dim], _tr[dim];

		for	(int i = 0; i < dim; ++i) {
			_bl[i] = center[i] - extent[i];
			_tr[i] = center[i] + extent[i];
		}

		return Box<type, dim>(_bl, _tr);
	}

	inline const type minX(int d) const {
		return _bl[d];
	}

	inline const type maxX(int d) const {
		return _tr[d];
	}

	inline double center(int d) const {
		return (_bl[d] + _tr[d]) / 2.0;
	}

	inline double extent(int d) const {
		return (_tr[d] - _bl[d]) / 2.0;
	}

	inline Eigen::Array<type, dim, 1> bl() const {
		return Eigen::Array<type, dim, 1>(_bl);
	}

	inline Eigen::Array<type, dim, 1> tr() const {
		return Eigen::Array<type, dim, 1>(_tr);
	}

	inline int dims() const {
		return dim;
	}

	inline void expand(const Box<type, dim>& bb) {

		for (int i = 0; i < dim; ++i) {

			if ( bb._bl[i] < _bl[i] )
				_bl[i] = bb._bl[i];

			if ( bb._tr[i] > _tr[i] )
				_tr[i] = bb._tr[i];

		}

	}

	// ratio is some positive double number. region will grow as extent * ratio
	inline void grow(const double ratio) {
		double center[dim], extent[dim];

		for (int i = 0; i < dim; ++i) {

			center[i] = double(_bl[i] + _tr[i]) / 2.0;
			extent[i] = double(_tr[i] - _bl[i]) / 2.0;

			extent[i] *= ratio;

			_bl[i] = type(center[i] - extent[i]);
			_tr[i] = type(center[i] + extent[i]);
		}
	}

	bool intersect(const Box<type, dim>& bb) const {
		double center[dim], extent[dim], bb_center[dim], bb_extent[dim];
		bool collide = true;

		for (int i = 0; i < dim; ++i) {

			center[i] = double(_bl[i] + _tr[i]) / 2.0;
			extent[i] = double(_tr[i] - _bl[i]) / 2.0;
			bb_center[i] = double(bb._bl[i] + bb._tr[i]) / 2.0;
			bb_extent[i] = double(bb._tr[i] - bb._bl[i]) / 2.0;

			collide &= std::abs( center[i] - bb_center[i] ) <= ( extent[i] + bb_extent[i] );

		}

		return collide;

	}

	/// \brief Determines if two bounding boxes collide
	/// \param b1 First bounding box
	/// \param b2 Second bounding box
	static bool intersects(const Box<type, dim>& b1, const Box<type, dim>& b2) {

		return b1.intersect(b2);

	}

	/// Simple test to check whether the point is inside the bounding rectangle.
	/// Mainly used as utility function in other tests.
	bool pointInside(const double point[3]) const {
		double center[dim], extent[dim];
		bool collide = true;

		for (int i = 0; i < dim; ++i) {

			center[i] = double(_bl[i] + _tr[i]) / 2.0;
			extent[i] = double(_tr[i] - _bl[i]) / 2.0;

			collide &= std::abs( point[i] - center[i] ) <= extent[i];

		}

		return collide;

	}

	void shiftCenter(const double shift[3]) {

		for (int i = 0; i < 3; ++i) {
			_bl[i] += shift[i];
			_tr[i] += shift[i];
		}

	}

	// return closest point projection onto the AABB
	Eigen::Array<type, dim, 1> closestPoint(const Eigen::Array<type, dim, 1>& pt) {

		double tmp[3];

		for (int i = 0; i < 3; ++i) {
			double v = pt[i];

			if ( v < _bl[i] ) v = _bl[i];
			if ( v > _tr[i] ) v = _tr[i];

			tmp[i] = v;
		}

		return Eigen::Array<type, dim, 1>(tmp);

	}

	double sqDistToPoint(const Eigen::Array<type, dim, 1>& pt) {
		double sqDist = 0.0;

		for (int i = 0; i < 3; ++i) {
			double v = pt[i];

			if ( v < _bl[i] ) sqDist += (_bl[i] - v) * (_bl[i] - v);
			if ( v > _tr[i] ) sqDist += (v - _tr[i]) * (v - _tr[i]);

		}

		return sqDist;
	}

	type operator() (int d) {
		switch (d) {
		case 0:
			return _bl[0];
			break;
		case 1:
			return _tr[0];
			break;
		case 2:
			return _bl[1];
			break;
		case 3:
			return _tr[1];
		}
	}

};

template <typename type, int dim, typename ownerType = void>
struct BoundingBox : public Box<type, dim> {
	const ownerType* _owner;

	BoundingBox(const type minx[dim], const type maxx[dim], const ownerType* owner) :
		Box<type, dim>(minx, maxx), _owner(owner) {}

	BoundingBox(const BoundingBox& bb) : Box<type, dim>(bb._bl, bb._tr), _owner(bb._owner) {}

	BoundingBox(const Eigen::Vector3d& point) : Box<type, dim>(point), _owner(nullptr) {}

	const ownerType* getOwner() const { return _owner; }

};

//==========================================
// ostream operators used for debug printing
//==========================================

template <typename type, int dim>
std::ostream& operator<<(std::ostream& os, const Box<type, dim>& bb) {

	os << "Bounding box : " << std::endl;

	for (int i = 0; i < dim; i++) {

		os << "| " << bb._bl[i] << " - " << bb._tr[i] << " |" << std::endl;

	}

    return os;
}

///////

struct BoundingBoxCompare {

	bool operator() (spatial::dimension_type dim, const Box<double, 3>& a, const Box<double, 3>& b) const {

		switch (dim) {
			case 0:
				return (a._bl[0] < b._bl[0]);
				break;
			case 1:
				return (a._tr[0] < b._tr[0]);
				break;
			case 2:
				return (a._bl[1] < b._bl[1]);
				break;
			case 3:
				return (a._tr[1] < b._tr[1]);
				break;
			case 4:
				return (a._bl[2] < b._bl[2]);
				break;
			case 5:
				return (a._tr[2] < b._tr[2]);
				break;
		}

		return false;
	}

};

struct BoundingBoxPredicate {
	const Box<double, 3>& box;

	BoundingBoxPredicate(const Box<double, 3>& b) : box(b) {}

	spatial::relative_order
	operator() (spatial::dimension_type dim, spatial::dimension_type, const Box<double, 3>& b ) const {

		switch (dim) {
			case 0:
			case 1:
				return (b._bl[0] > box._tr[0] && b._tr[0] > box._tr[0]) ? spatial::above :
					   (b._tr[0] < box._bl[0] && b._bl[0] < box._bl[0]) ? spatial::below :
						spatial::matching;
				break;
			case 2:
			case 3:
				return (b._bl[1] > box._tr[1] && b._tr[1] > box._tr[1]) ? spatial::above :
					   (b._tr[1] < box._bl[1] && b._bl[1] < box._bl[1]) ? spatial::below :
						spatial::matching;
				break;
			case 4:
			case 5:
				return (b._bl[2] > box._tr[2] && b._tr[2] > box._tr[2]) ? spatial::above :
					   (b._tr[2] < box._bl[2] && b._bl[2] < box._bl[2]) ? spatial::below :
						spatial::matching;
				break;
		}

	}
};

  struct BoundingBoxMetric {
    // Check that DistanceType is a fundamental floating point type
    typedef double distance_type;

    /**
     *  Compute the distance between the point of \c origin and the \c key.
     *  \return The resulting square distance.
     */
    distance_type
    distance_to_key(spatial::dimension_type rank,
                    const Box<double, 3>& origin,
                    const Box<double, 3>& key) const {

    	distance_type result = distance_type(0.0);

//    	std::cout << "===============================================" << std::endl;
//    	std::cout << origin << std::endl;
//    	std::cout << "--------------" << std::endl;
//    	std::cout << key << std::endl;
//    	std::cout << "--------------" << std::endl;

    	for (spatial::dimension_type i = 0; i < rank/2; ++i) {

    		distance_type tmp = std::abs( key.center(i) - origin.center(i) ) + key.extent(i);

    		result += tmp * tmp;

//    		if (tmp_bl < tmp_tr) result += tmp_bl*tmp_bl;
//    		else                 result += tmp_tr*tmp_tr;

//    		if ( origin._tr[i] < key._tr[i] ) result += (key._tr[i] - origin._tr[i]) * (key._tr[i] - origin._tr[i]);
//    		if ( key._bl[i] < origin._bl[i] ) result += (origin._bl[i] - key._bl[i]) * (origin._bl[i] - key._bl[i]);

    	}
//    	std::cout << "dist = " << result << std::endl;
//    	std::cout << "===============================================" << std::endl;
    	return result;

    }

    /**
     *  The distance between the point of \c origin and the closest point to
     *  the plane orthogonal to the axis of dimension \c dim and crossing \c
     *  key.
     *  \return The resulting square distance.
     */
    distance_type
    distance_to_plane(spatial::dimension_type,
    			      spatial::dimension_type dim,
                      const Box<double, 3>& origin,
                      const Box<double, 3>& key) const {

     	spatial::dimension_type i = dim % 3; // probably lets try it and see

    	distance_type tmp = std::abs( key.center(i) - origin.center(i) ) + key.extent(i);

		return tmp * tmp;

//    	if ( origin._tr[dim] < key._tr[dim] ) return (key._tr[dim] - origin._tr[dim]) * (key._tr[dim] - origin._tr[dim]);
//    	if ( key._bl[dim] < origin._bl[dim] ) return (origin._bl[dim] - key._bl[dim]) * (origin._bl[dim] - key._bl[dim]);

    }

  };

#endif /* BOUNDINGBOX_HPP_ */


// Boost.Polygon library voronoi_graphic_utils.hpp header file

//          Copyright Andrii Sydorchuk 2010-2012.
// 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)

// See http://www.boost.org for updates, documentation, and revision history.

#ifndef BOOST_POLYGON_VORONOI_VISUAL_UTILS
#define BOOST_POLYGON_VORONOI_VISUAL_UTILS

#include <stack>
#include <vector>

#include <boost/polygon/isotropy.hpp>
#include <boost/polygon/point_concept.hpp>
#include <boost/polygon/segment_concept.hpp>
#include <boost/polygon/rectangle_concept.hpp>

namespace boost {
namespace polygon {
// Utilities class, that contains set of routines handful for visualization.
template <typename CT>
class voronoi_visual_utils {
 public:
  // Discretize parabolic Voronoi edge.
  // Parabolic Voronoi edges are always formed by one point and one segment
  // from the initial input set.
  //
  // Args:
  //   point: input point.
  //   segment: input segment.
  //   max_dist: maximum discretization distance.
  //   discretization: point discretization of the given Voronoi edge.
  //
  // Template arguments:
  //   InCT: coordinate type of the input geometries (usually integer).
  //   Point: point type, should model point concept.
  //   Segment: segment type, should model segment concept.
  //
  // Important:
  //   discretization should contain both edge endpoints initially.
  template <class InCT1, class InCT2,
            template<class> class Point,
            template<class> class Segment>
  static
  typename enable_if<
    typename gtl_and<
      typename gtl_if<
        typename is_point_concept<
          typename geometry_concept< Point<InCT1> >::type
        >::type
      >::type,
      typename gtl_if<
        typename is_segment_concept<
          typename geometry_concept< Segment<InCT2> >::type
        >::type
      >::type
    >::type,
    void
  >::type discretize(
      const Point<InCT1>& point,
      const Segment<InCT2>& segment,
      const CT max_dist,
      std::vector< Point<CT> >* discretization) {
    // Apply the linear transformation to move start point of the segment to
    // the point with coordinates (0, 0) and the direction of the segment to
    // coincide the positive direction of the x-axis.
    CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
    CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
    CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y;

    // Compute x-coordinates of the endpoints of the edge
    // in the transformed space.
    CT projection_start = sqr_segment_length *
        get_point_projection((*discretization)[0], segment);
    CT projection_end = sqr_segment_length *
        get_point_projection((*discretization)[1], segment);

    // Compute parabola parameters in the transformed space.
    // Parabola has next representation:
    // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y).
    CT point_vec_x = cast(x(point)) - cast(x(low(segment)));
    CT point_vec_y = cast(y(point)) - cast(y(low(segment)));
    CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y;
    CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x;

    // Save the last point.
    Point<CT> last_point = (*discretization)[1];
    discretization->pop_back();

    // Use stack to avoid recursion.
    std::stack<CT> point_stack;
    point_stack.push(projection_end);
    CT cur_x = projection_start;
    CT cur_y = parabola_y(cur_x, rot_x, rot_y);

    // Adjust max_dist parameter in the transformed space.
    const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length;
    while (!point_stack.empty()) {
      CT new_x = point_stack.top();
      CT new_y = parabola_y(new_x, rot_x, rot_y);

      // Compute coordinates of the point of the parabola that is
      // furthest from the current line segment.
      CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x;
      CT mid_y = parabola_y(mid_x, rot_x, rot_y);

      // Compute maximum distance between the given parabolic arc
      // and line segment that discretize it.
      CT dist = (new_y - cur_y) * (mid_x - cur_x) -
          (new_x - cur_x) * (mid_y - cur_y);
      dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) +
          (new_x - cur_x) * (new_x - cur_x));
      if (dist <= max_dist_transformed) {
        // Distance between parabola and line segment is less than max_dist.
        point_stack.pop();
        CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) /
            sqr_segment_length + cast(x(low(segment)));
        CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) /
            sqr_segment_length + cast(y(low(segment)));
        discretization->push_back(Point<CT>(inter_x, inter_y));
        cur_x = new_x;
        cur_y = new_y;
      } else {
        point_stack.push(mid_x);
      }
    }

    // Update last point.
    discretization->back() = last_point;
  }

 private:
  // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b).
  static CT parabola_y(CT x, CT a, CT b) {
    return ((x - a) * (x - a) + b * b) / (b + b);
  }

  // Get normalized length of the distance between:
  //   1) point projection onto the segment
  //   2) start point of the segment
  // Return this length divided by the segment length. This is made to avoid
  // sqrt computation during transformation from the initial space to the
  // transformed one and vice versa. The assumption is made that projection of
  // the point lies between the start-point and endpoint of the segment.
  template <class InCT,
            template<class> class Point,
            template<class> class Segment>
  static
  typename enable_if<
    typename gtl_and<
      typename gtl_if<
        typename is_point_concept<
          typename geometry_concept< Point<int> >::type
        >::type
      >::type,
      typename gtl_if<
        typename is_segment_concept<
          typename geometry_concept< Segment<long> >::type
        >::type
      >::type
    >::type,
    CT
  >::type get_point_projection(
      const Point<CT>& point, const Segment<InCT>& segment) {
    CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
    CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
    CT point_vec_x = x(point) - cast(x(low(segment)));
    CT point_vec_y = y(point) - cast(y(low(segment)));
    CT sqr_segment_length =
        segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y;
    CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y;
    return vec_dot / sqr_segment_length;
  }

  template <typename InCT>
  static CT cast(const InCT& value) {
    return static_cast<CT>(value);
  }
};
}
}

#endif  // BOOST_POLYGON_VORONOI_VISUAL_UTILS


//=======================================================================
// Copyright 2014-2015 David Simmons-Duffin.
// Distributed under the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#include "Block_Diagonal_Matrix.hxx"
#include "SDP.hxx"
#include "SDP_Solver_Terminate_Reason.hxx"

#include "Solver_Parameters.hxx"
#include "../Timers.hxx"

#include <boost/filesystem.hpp>

// SDPSolver contains the data structures needed during the running of
// the interior point algorithm.  Each structure is allocated when an
// SDPSolver is initialized, and reused in each iteration.
//
class SDP_Solver
{
public:
  // a Vector of length P = sdp.primalObjective.size()
  Block_Vector x;

  // a Block_Diagonal_Matrix with block sizes given by
  // sdp.psdMatrixBlockDims()
  Block_Diagonal_Matrix X;

  // a Vector of length N = sdp.dualObjective.size()
  Block_Vector y;

  // a Block_Diagonal_Matrix with the same structure as X
  Block_Diagonal_Matrix Y;

  /********************************************/
  // Solver status

  // NB: here, primalObjective and dualObjective refer to the current
  // values of the objective functions.  In the class SDP, they refer
  // to the vectors c and b.  Hopefully the name-clash won't cause
  // confusion.
  El::BigFloat primal_objective, // f + c . x
    dual_objective,              // f + b . y
    duality_gap;                 // normalized difference of objectives

  // Discrepancy in the primal equality constraints, a
  // Block_Diagonal_Matrix with the same structure as X, called 'P' in
  // the manual:
  //
  //   PrimalResidues = \sum_p A_p x_p - X
  //
  Block_Diagonal_Matrix primal_residues;

  // primal_error is max of both primal_residues and p=(b - B^T x)
  El::BigFloat primal_error_P, primal_error_p; // |P| and |p|
  El::BigFloat primal_error() const
  {
    return std::max(primal_error_P, primal_error_p);
  }

  // Discrepancy in the dual equality constraints, a Vector of length
  // P, called 'd' in the manual:
  //
  //   dualResidues = c - Tr(A_* Y) - B y
  //
  Block_Vector dual_residues;
  El::BigFloat dual_error; // maxAbs(dualResidues)

  int64_t current_generation;
  boost::optional<int64_t> backup_generation;

  SDP_Solver(const Solver_Parameters &parameters,
             const Verbosity &verbosity,
             const bool &require_initial_checkpoint,
             const Block_Info &block_info, const El::Grid &grid,
             const size_t &dual_objective_b_height);

  SDP_Solver_Terminate_Reason
  run(const Solver_Parameters &parameters,
      const Verbosity &verbosity,
      const boost::property_tree::ptree &parameter_properties,
      const Block_Info &block_info, const SDP &sdp, const El::Grid &grid,
      Timers &timers);

  void step(
    const Solver_Parameters &parameters, const std::size_t &total_psd_rows,
    const bool &is_primal_and_dual_feasible, const Block_Info &block_info,
    const SDP &sdp, const El::Grid &grid,
    const Block_Diagonal_Matrix &X_cholesky,
    const Block_Diagonal_Matrix &Y_cholesky,
    const std::array<
      std::vector<std::vector<std::vector<El::DistMatrix<El::BigFloat>>>>, 2>
      &A_X_inv,
    const std::array<
      std::vector<std::vector<std::vector<El::DistMatrix<El::BigFloat>>>>, 2>
      &A_Y,
    const Block_Vector &primal_residue_p, El::BigFloat &mu,
    El::BigFloat &beta_corrector, El::BigFloat &primal_step_length,
    El::BigFloat &dual_step_length, bool &terminate_now, Timers &timers);

  void
  save_checkpoint(const boost::filesystem::path &checkpoint_directory,
                  const Verbosity &verbosity,
                  const boost::property_tree::ptree &parameter_properties);
  bool
  load_checkpoint(const boost::filesystem::path &checkpoint_directory,
                  const Block_Info &block_info, const Verbosity &verbosity,
                  const bool &require_initial_checkpoint);
};


#define CATCH_CONFIG_MAIN

#include "CALPHADFreeEnergyFunctionsBinary.h"
#include "CALPHADFreeEnergyFunctionsBinary3Ph2Sl.h"
#include "CALPHADFreeEnergyFunctionsBinaryThreePhase.h"
#include "InterpolationType.h"

#include "catch.hpp"

#include <boost/optional/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <fstream>
#include <iostream>
#include <string>

namespace pt = boost::property_tree;

TEST_CASE("AlCu database check, two phase", "[AlCu database checks, two phase]")
{
    Thermo4PFM::EnergyInterpolationType energy_interp_func_type
        = Thermo4PFM::EnergyInterpolationType::PBG;
    Thermo4PFM::ConcInterpolationType conc_interp_func_type
        = Thermo4PFM::ConcInterpolationType::PBG;

    std::cout << " Read CALPHAD database..." << std::endl;
    pt::ptree calphad_db;
    try
    {
        pt::read_json("../thermodynamic_data/calphadAlCuLFcc.json", calphad_db);
    }
    catch (std::exception& e)
    {
        std::cerr << "exception caught: " << e.what() << std::endl;
    }

    pt::ptree newton_db;
    newton_db.put("alpha", 0.1);
    newton_db.put("max_its", 10000);

    Thermo4PFM::CALPHADFreeEnergyFunctionsBinary cafe(
        calphad_db, newton_db, energy_interp_func_type, conc_interp_func_type);

    const Thermo4PFM::PhaseIndex pi0 = Thermo4PFM::PhaseIndex::phaseL;
    const Thermo4PFM::PhaseIndex pi1 = Thermo4PFM::PhaseIndex::phaseA;

    double temperature = 0.0;
    double conc        = 1.0;
    double f0          = 0.0;
    double f1          = 0.0;

    // Check the melting temperature
    temperature = 1000.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 <= f1);

    temperature = 934.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 <= f1);

    temperature = 933.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 >= f1);

    temperature = 900.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 >= f1);

    // Check at the eutectic (even though only two phases)
    temperature = 1000.0;
    conc        = 1.0 - 0.175;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 <= f1);

    temperature = 822.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    REQUIRE(f0 <= f1);

    // Check in the two-phase liquid-fcc region
    temperature          = 900.0;
    double init_guess[2] = { 0.8, 0.99 };
    double lceq[2]       = { init_guess[0], init_guess[1] };
    bool found_ceq       = cafe.computeCeqT(temperature, &lceq[0], 1000, true);
    REQUIRE(found_ceq);
    double expected_result[2] = { 0.94, 0.99 };
    CHECK(lceq[0] == Approx(expected_result[0]).margin(0.01));
    CHECK(lceq[1] == Approx(expected_result[1]).margin(0.01));

    temperature = 822.0;
    lceq[0]     = 0.8;
    lceq[1]     = 0.99;
    found_ceq   = cafe.computeCeqT(temperature, &lceq[0], 1000, true);
    REQUIRE(found_ceq);
    expected_result[0] = 1.0 - 0.175;
    expected_result[1] = 1.0 - 0.025;
    CHECK(lceq[0] == Approx(expected_result[0]).margin(0.01));
    CHECK(lceq[1] == Approx(expected_result[1]).margin(0.01));

    // Check a point on the curves
    temperature = 812.0;
    conc        = 0.8743844133668341;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    CHECK(f0 == Approx(-36490.44098336194).margin(0.0001));
    CHECK(f1 == Approx(-36449.059817785725).margin(0.0001));
}

TEST_CASE(
    "AlCu database check, three phase", "[AlCu database checks, three phase]")
{
    Thermo4PFM::EnergyInterpolationType energy_interp_func_type
        = Thermo4PFM::EnergyInterpolationType::PBG;
    Thermo4PFM::ConcInterpolationType conc_interp_func_type
        = Thermo4PFM::ConcInterpolationType::PBG;

    std::cout << " Read CALPHAD database..." << std::endl;
    pt::ptree calphad_db;
    try
    {
        pt::read_json(
            "../thermodynamic_data/calphadAlCuLFccTheta.json", calphad_db);
    }
    catch (std::exception& e)
    {
        std::cerr << "exception caught: " << e.what() << std::endl;
    }

    pt::ptree newton_db;
    newton_db.put("alpha", 0.1);
    newton_db.put("max_its", 10000);

    Thermo4PFM::CALPHADFreeEnergyFunctionsBinary3Ph2Sl cafe(
        calphad_db, newton_db, energy_interp_func_type, conc_interp_func_type);

    const Thermo4PFM::PhaseIndex pi0 = Thermo4PFM::PhaseIndex::phaseL;
    const Thermo4PFM::PhaseIndex pi1 = Thermo4PFM::PhaseIndex::phaseA;
    const Thermo4PFM::PhaseIndex pi2 = Thermo4PFM::PhaseIndex::phaseB;

    double temperature = 0.0;
    double conc        = 1.0;
    double f0          = 0.0;
    double f1          = 0.0;
    double f2          = 0.0;

    // Check the Al melting temperature
    temperature = 1000.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 <= f1);
    REQUIRE(f0 <= f2);

    temperature = 934.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 <= f1);
    REQUIRE(f0 <= f2);

    temperature = 933.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 >= f1);
    REQUIRE(f1 <= f2);

    temperature = 900.0;
    conc        = 1.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 >= f1);
    REQUIRE(f1 <= f2);

    // Check the liquid above the eutectic
    temperature = 1000.0;
    conc        = 1.0 - 0.175;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 <= f1);
    REQUIRE(f0 <= f2);

    temperature = 822.0;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    REQUIRE(f0 < f1);
    REQUIRE(f0 < f2);

    // Check a point on the curves
    temperature = 812.0;
    conc        = 0.8743844133668341;
    f0          = cafe.computeFreeEnergy(temperature, &conc, pi0);
    f1          = cafe.computeFreeEnergy(temperature, &conc, pi1);
    f2          = cafe.computeFreeEnergy(temperature, &conc, pi2);
    CHECK(f0 == Approx(-36490.44098336194).margin(0.0001));
    CHECK(f1 == Approx(-36449.059817785725).margin(0.0001));
    CHECK(f2 == Approx(-34045.5790319836).margin(0.0001));
}


// Copyright (c) 2017-2020 The WaykiChain Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "entities/key.h"
#include <boost/test/unit_test.hpp>

using namespace std;

BOOST_AUTO_TEST_SUITE(pub_key_tests)

BOOST_AUTO_TEST_CASE(pub_key_verify_test)
{
    ECC_Start();
    std::unique_ptr<ECCVerifyHandle> handle = std::make_unique<ECCVerifyHandle>();

    // Randomly generate a private key
    CKey  key;
    key.MakeNewKey();
    BOOST_CHECK(key.IsCompressed() == true);

    // get publicKey from key
    CPubKey pubKey = key.GetPubKey();

    // hash
    string strMsg = "hello world";
    uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());

    // sign
    vector<unsigned char> signature;
    BOOST_CHECK(key.SignCompact(hashMsg, signature));

    // get public key from hash and sign
    CPubKey rKey;
    BOOST_CHECK(rKey.RecoverCompact(hashMsg, signature));
    BOOST_CHECK(rKey == pubKey);

    handle.reset();
    ECC_Stop();
}

BOOST_AUTO_TEST_SUITE_END()


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2011 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * \file
 * \author Felix Lehmann
 */

#ifndef ICC_0_PRECOND_HH
#define ICC_0_PRECOND_HH

#include <memory>
#include <vector>

#include <boost/fusion/include/at_c.hpp>

#include "dune/grid/common/grid.hh"
#include "dune/istl/matrix.hh"
#include "dune/istl/bcrsmatrix.hh"
#include "dune/common/fmatrix.hh"
#include "dune/common/fvector.hh"
#include "dune/common/iteratorfacades.hh"
#include "dune/istl/matrixindexset.hh"

#include "dune/istl/preconditioners.hh"

namespace Kaskade
{

/**
 * \ingroup linalg
 *
 * Incomplete Cholesky factorization by algorithm from book "Matrix Computations" by Gene Golub & Charles van Loan
 *
 * algorith will create approximate cholesky factorization L by only using the sparsity pattern of A
 *
 * we will only touch the lower triangle of the matrix
 *
 * interface copied from iluprecond.hh
 *
 * iccprecond.hh (using TAUCS Incomplete Cholesky Decomposition) does the same (even faster),
 * if we set its droptolerance parameter sufficiently large
 *
 */

template <class Op>
class ICC_0Preconditioner: public Dune::Preconditioner<typename Op::Range, typename Op::Range>
{
public:
  typedef typename Op::Domain Domain;
  typedef typename Op::Range Range;
  typedef typename Op::Scalar Scalar;

private:
  typedef Dune::FieldMatrix<Scalar,1,1> BlockType;
  typedef Dune::BlockVector<Dune::FieldVector<Scalar,1> > CoeffVector ;
  typedef Dune::BCRSMatrix<BlockType> BCRS_Matrix;
  typedef typename BCRS_Matrix::ConstColIterator ColIter;

    BCRS_Matrix mL;
    std::vector<std::vector<int> > mEntries;
    // int const mChoice;
  public:
    static int const category = Dune::SolverCategory::sequential;
    ICC_0Preconditioner( Op& op );
    void pre (Domain&, Range&) {}
    void apply (Domain& x, Range const& y);
    void post (Domain&) {}
};

template <class Op>
ICC_0Preconditioner<Op>::ICC_0Preconditioner(Op& op)
{
  std::unique_ptr<BCRS_Matrix> mA = op.template getPointer<BCRS_Matrix>();
  int N = (*mA).N();
  // create kind of column iterator for BCRS
  // and simultaneously read out the sparsity pattern of mA
  mEntries.resize(N);
  for(int i=0;i<N;i++)
    for (ColIter cI=(*mA)[i].begin(); cI!=(*mA)[i].end() && cI.index()<i; ++cI)
      mEntries[cI.index()].push_back(i);

  // backward substitution in ICC_0Preconditioner::apply() with BCRSMatrix mL^T is not
  // efficiently applicable, therefore create two BCRSMatrices mL and mLT := mL^T
  // set up sparsity pattern of lower triangle of mA (upper triangle of mA^T) for mL (mLT)
  Dune::MatrixIndexSet sparsityPattern_mL(N,N);
  for (int i=0; i<N; ++i)
    for (ColIter cI=(*mA)[i].begin(); cI!=(*mA)[i].end() && cI.index()<=i; ++cI)
      sparsityPattern_mL.add(i,cI.index() );

  sparsityPattern_mL.exportIdx( mL );
  for(int i=0;i<N;++i)
    for (ColIter cI=mL[i].begin(); cI!=mL[i].end(); ++cI)
      mL[i][cI.index()] = (*mA)[i][cI.index()];
  //find factorization mL of mA
  double tmp;
  for(int k=0;k<N;k++)
  {
    mL[k][k] = sqrt(mL[k][k]);
    for(std::vector<int>::iterator it = mEntries[k].begin(); it!=mEntries[k].end(); it++)
      mL[*it][k] /= mL[k][k];
    for(std::vector<int>::iterator it = mEntries[k].begin(); it!=mEntries[k].end(); it++)
    {
      tmp = mL[*it][k];
      mL[*it][*it] -= (mL[*it][k])*(mL[*it][k]); // "mEntries" contains not diagonal indices
      for(std::vector<int>::iterator it2 = mEntries[*it].begin(); it2!=mEntries[*it].end(); it2++)
        if( mL.exists(*it2,k) )
	  mL[*it2][*it] -= mL[*it2][k]*tmp;
    }
  }
}

template <class Op>
void ICC_0Preconditioner<Op>::apply(Domain& x, Range const& b)
{
  int N = x.dim();

  CoeffVector &sol = boost::fusion::at_c<0>(x.data);
  CoeffVector const &rhs = boost::fusion::at_c<0>(b.data);
  // for forward substitution
  CoeffVector forward(N); forward = 0;

  double tmp;
  // forward substitution
  for(int i=0;i<N;i++)
  {
    tmp = rhs[i];
    for (ColIter cI = mL[i].begin(); cI != mL[i].end(); ++cI)
      if( cI.index()<i )
	tmp -= mL[i][cI.index()]*forward[cI.index()];
    forward[i] = tmp/mL[i][i];
  }
  // backward substitution
  for(int i = N-1;i>=0;i--)
  {
    tmp = forward[i];
    for(std::vector<int>::iterator it = mEntries[i].begin();it!=mEntries[i].end();it++)
      tmp -= mL[*it][i]*sol[*it];
    sol[i] = tmp/mL[i][i];
  }
}
}  // namespace Kaskade
#endif


#ifndef __BOOST_GEOMETRY_DISSOLVE_H__
#define __BOOST_GEOMETRY_DISSOLVE_H__

/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Wouter van Kleunen wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.
 * ----------------------------------------------------------------------------
 */

#include <vector>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/function_output_iterator.hpp>

namespace geometry {

namespace impl {

template<typename C, typename T>
static inline void result_combine(C &result, T &&new_element)
{
    result.push_back(new_element);

   	for(std::size_t i = 0; i < result.size() - 1; ) {
        if(!boost::geometry::intersects(result[i], result.back())) {
            ++i;
            continue;
        }

        std::vector<T> union_result;
        boost::geometry::union_(result[i], result.back(), union_result);

        if(union_result.size() != 1) {
			++i;
			continue;
		}

       	result.back() = std::move(union_result[0]);
       	result.erase(result.begin() + i);
    }
}

struct pseudo_vertice_key
{
    std::size_t index_1;
    double scale;
    std::size_t index_2;
    bool reroute;

    pseudo_vertice_key(std::size_t index_1 = 0, std::size_t index_2 = 0, double scale = 0.0, bool reroute = false)
        : index_1(index_1), scale(scale), index_2(index_2), reroute(reroute)
    { }
};

struct compare_pseudo_vertice_key
{
    bool operator()(pseudo_vertice_key const &a, pseudo_vertice_key const &b) const {
        if(a.index_1 < b.index_1) return true;
        if(a.index_1 > b.index_1) return false;
        if(a.scale < b.scale) return true;
        if(a.scale > b.scale) return false;
        if(a.index_2 > b.index_2) return true;
        if(a.index_2 < b.index_2) return false;
        if(a.reroute && !b.reroute) return true;
        if(!a.reroute && b.reroute) return false;
		return false;
    }
};

template<typename point_t = boost::geometry::model::d2::point_xy<double>>
struct pseudo_vertice
{
    point_t p;
    pseudo_vertice_key link;

    pseudo_vertice(point_t p, pseudo_vertice_key link = pseudo_vertice_key())
        : p(p), link(link)
    { }
};

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename ring_t = boost::geometry::model::ring<point_t>
	>
static inline void dissolve_find_intersections(
			ring_t const &ring,
			std::map<pseudo_vertice_key, pseudo_vertice<point_t>, compare_pseudo_vertice_key> &pseudo_vertices,
    		std::set<pseudo_vertice_key, compare_pseudo_vertice_key> &start_keys)
{
	if(ring.empty()) return;

	boost::geometry::index::rtree<std::pair< boost::geometry::model::segment<point_t>, std::size_t >, boost::geometry::index::quadratic<16>> index;

	// Generate all by-pass intersections in the graph
	// Generate a list of all by-pass intersections
    pseudo_vertices.emplace(pseudo_vertice_key(ring.size() - 1, ring.size() - 1, 0.0), ring.back());
    for(std::size_t i = ring.size() - 1; i--; )
    {
        pseudo_vertices.emplace(pseudo_vertice_key(i, i, 0.0), ring[i]);
		boost::geometry::model::segment<point_t> line_1(ring[i], ring[i + 1]);

		boost::geometry::index::query(
				index, boost::geometry::index::intersects(line_1),
				boost::make_function_output_iterator([&](std::pair< boost::geometry::model::segment<point_t>, std::size_t > const &iter) {

			auto const &line_2 = iter.first;
			auto j = iter.second;

			std::vector<point_t> output;
			boost::geometry::intersection(line_1, line_2, output);

			for(auto const &p: output) {
				double scale_1 = boost::geometry::comparable_distance(p, ring[i]) / boost::geometry::comparable_distance(ring[i + 1], ring[i]);
				double scale_2 = boost::geometry::comparable_distance(p, ring[j]) / boost::geometry::comparable_distance(ring[j + 1], ring[j]);
				if(scale_1 < 1.0 && scale_2 < 1.0) {
					pseudo_vertice_key key_j(j, i, scale_2);
					pseudo_vertices.emplace(pseudo_vertice_key(i, j, scale_1, true), pseudo_vertice<point_t>(p, key_j));
					pseudo_vertices.emplace(key_j, p);
					start_keys.insert(key_j);

					pseudo_vertice_key key_i(i, j, scale_1);
					pseudo_vertices.emplace(pseudo_vertice_key(j, i, scale_2, true), pseudo_vertice<point_t>(p, key_i));
					pseudo_vertices.emplace(key_i, p);
					start_keys.insert(key_i);
				}
			}
		}));

		index.insert(std::make_pair(boost::geometry::model::segment<point_t>(ring[i], ring[i+1]), i));
    }
}

// Remove invalid points (NaN) from ring
template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename ring_t = boost::geometry::model::ring<point_t>
	>
static inline void correct_invalid(ring_t &ring)
{
	for(auto i = ring.begin(); i != ring.end(); ) {
		if(!boost::geometry::is_valid(*i))
			i = ring.erase(i);
		else
			++i;
	}
}

// Correct orientation of ring
template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename ring_t = boost::geometry::model::ring<point_t>
	>
static inline double correct_orientation(ring_t &ring, boost::geometry::order_selector order)
{
	auto area = boost::geometry::area(ring);
	bool should_reverse =
		(order == boost::geometry::clockwise && area < 0) ||
		(order == boost::geometry::counterclockwise && area > 0);

	if(should_reverse) {
		std::reverse(ring.begin(), ring.end());
	}

	return area;
}

// Close ring if not closed
template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename ring_t = boost::geometry::model::ring<point_t>
	>
static inline void correct_close(ring_t &ring)
{
	// Close ring if not closed
	if(!ring.empty() && !boost::geometry::equals(ring.back(), ring.front()))
		ring.push_back(ring.front());

}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename ring_t = boost::geometry::model::ring<point_t>
	>
static inline std::vector<ring_t> dissolve_generate_rings(
			std::map<pseudo_vertice_key, pseudo_vertice<point_t>, compare_pseudo_vertice_key> &pseudo_vertices,
    		std::set<pseudo_vertice_key, compare_pseudo_vertice_key> &start_keys,
			boost::geometry::order_selector order, double remove_spike_min_area = 0.0)
{
	std::vector<ring_t> result;

	// Generate all polygons by tracing all the intersections
	// Perform union to combine all polygons into single polygon again
    while(!start_keys.empty()) {
		ring_t new_ring;

		// Store point in generated polygon
		auto push_point = [&new_ring](auto const &p) {
            if(new_ring.empty() || boost::geometry::comparable_distance(new_ring.back(), p) > 0)
                new_ring.push_back(p);
		};

		auto is_closed = [](ring_t &ring) {
			if(ring.size() < 2) return false;
			for(std::size_t i = 0; i < ring.size() - 1; ++i) {
				if(boost::geometry::comparable_distance(ring[i], ring.back()) == 0) {
					ring.erase(ring.begin(), ring.begin() + i);
					return true;
				}
			}

			return false;
		};

        auto start_iter = pseudo_vertices.find(*start_keys.begin());
        auto i = start_iter;

        do {
            auto const &key = i->first;
            auto const &value = i->second;

			// Store the point in output polygon
			push_point(value.p);

            start_keys.erase(key);
            if(key.reroute) {
				// Follow by-pass
                i = pseudo_vertices.find(value.link);
			} else {
				// Continu following original polygon
                ++i;
                if(i == pseudo_vertices.end())
                    i = pseudo_vertices.begin();
            }

			// Repeat until back at starting point
       	} while(!is_closed(new_ring));

		// Combine with already generated polygons
		auto area = boost::geometry::area(new_ring);
		if(std::abs(area) > remove_spike_min_area) {
	    	result.push_back(std::move(new_ring));
		}
   	}

    return result;
}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename ring_t = boost::geometry::model::ring<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline std::vector<ring_t> correct(ring_t const &ring, boost::geometry::order_selector order, double remove_spike_min_area = 0.0)
{
	constexpr std::size_t min_nodes = 3;
	if(ring.size() < min_nodes)
		return std::vector<ring_t>();

    std::map<pseudo_vertice_key, pseudo_vertice<point_t>, compare_pseudo_vertice_key> pseudo_vertices;
    std::set<pseudo_vertice_key, compare_pseudo_vertice_key> start_keys;

	ring_t new_ring = ring;

	// Remove invalid coordinates
	correct_invalid(new_ring);

	// Close ring
	correct_close(new_ring);

	// Correct orientation
	correct_orientation(new_ring, order);

	// Detect self-intersection points
	dissolve_find_intersections(new_ring, pseudo_vertices, start_keys);

	if(start_keys.empty()) {
		if(std::abs(boost::geometry::area(new_ring)) > remove_spike_min_area)
			return { new_ring };
		else
			return { };
	}

	return dissolve_generate_rings(pseudo_vertices, start_keys, order, remove_spike_min_area);
}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
struct combine_non_zero_winding
{
	inline void operator()(multi_polygon_t &combined_outers, multi_polygon_t &combined_inners, polygon_t &poly)
	{
		if(boost::geometry::area(poly) > 0)
			result_combine(combined_outers, std::move(poly));
		else {
			std::reverse(poly.outer().begin(), poly.outer().end());
			result_combine(combined_inners, std::move(poly));
		}
	}
};

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
struct combine_odd_even
{
	inline void operator()(multi_polygon_t &combined_outers, multi_polygon_t &combined_inners, polygon_t &poly)
	{
		if(boost::geometry::area(poly) < 0)
			std::reverse(poly.outer().begin(), poly.outer().end());

		multi_polygon_t result;
		boost::geometry::sym_difference(combined_outers, poly, result);
		combined_outers = std::move(result);
	}
};

template<
	typename combine_function_t,
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename ring_t = boost::geometry::model::ring<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct(polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area, combine_function_t combine)
{
	auto order = boost::geometry::point_order<polygon_t>::value;
	auto outer_rings = correct(input.outer(), order, remove_spike_min_area);

	// Calculate all outers and combine them if possible
	multi_polygon_t combined_outers;
	multi_polygon_t combined_inners;

	for(auto &ring: outer_rings) {
		polygon_t poly;
		poly.outer() = std::move(ring);
		combine(combined_outers, combined_inners, poly);
	}

	// Calculate all inners and combine them if possible
	for(auto const &ring: input.inners()) {
		polygon_t poly;
		poly.outer() = std::move(ring);

		multi_polygon_t new_inners;
		correct(poly, new_inners, remove_spike_min_area, combine);

		for(auto &poly: new_inners) {
			result_combine(combined_inners, std::move(poly));
		}
	}

	// Cut out all inners from all the outers
	boost::geometry::difference(combined_outers, combined_inners, output);
}

template<
	typename combine_function_t,
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename ring_t = boost::geometry::model::ring<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct(multi_polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area, combine_function_t combine)
{
	for(auto const &polygon: input)
	{
		multi_polygon_t new_polygons;
		correct(polygon, new_polygons, remove_spike_min_area, combine);

		for(auto &new_polygon: new_polygons)
			result_combine(output, std::move(new_polygon));
	}
}

}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct(polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area = 0.0)
{
	impl::correct(input, output, remove_spike_min_area, impl::combine_non_zero_winding<point_t, polygon_t, multi_polygon_t>());
}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct_odd_even(polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area = 0.0)
{
	impl::correct(input, output, remove_spike_min_area, impl::combine_odd_even<point_t, polygon_t, multi_polygon_t>());
}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename ring_t = boost::geometry::model::ring<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct(multi_polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area = 0.0)
{
	impl::correct(input, output, remove_spike_min_area, impl::combine_non_zero_winding<point_t, polygon_t, multi_polygon_t>());
}

template<
	typename point_t = boost::geometry::model::d2::point_xy<double>,
	typename polygon_t = boost::geometry::model::polygon<point_t>,
	typename ring_t = boost::geometry::model::ring<point_t>,
	typename multi_polygon_t = boost::geometry::model::multi_polygon<polygon_t>
	>
static inline void correct_odd_even(multi_polygon_t const &input, multi_polygon_t &output, double remove_spike_min_area = 0.0)
{
	impl::correct(input, output, remove_spike_min_area, impl::combine_odd_even<point_t, polygon_t, multi_polygon_t>());
}

}

#endif


//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_REFINE_RSQRT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_REFINE_RSQRT_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!

    @ingroup group-arithmetic
    This function object performs a Newton-Raphson step to improve precision of rsqrt estimate.

    This function can be used in conjunction with raw_(rsqrt)
    to add more precision to the estimates if their default
    precision is not enough.

    @par Header <boost/simd/function/refine_rsqrt.hpp>

    @par semantic:

    @code
     auto r = refine_rsqrt(x, est);
    @endcode

    is similar to

    @code
       auto r = fma( fnms(x, sqr(est), 1), est/2, est);
    @endcode

    @see rec

  **/
  IEEEValue refine_rsqrt(IEEEValue const& x, IEEEValue const& est);
} }
#endif

#include <boost/simd/function/scalar/refine_rsqrt.hpp>
#include <boost/simd/function/simd/refine_rsqrt.hpp>

#endif


#include "distribution_mixture_models.hxx"

#include <boost/range/combine.hpp>
#include <iomanip>
#include <math.h>

namespace vmf_fitting
{

template<int N_>
void ExpApproximation(Eigen::Array<float, N_, 1> &vals)
{
  // From http://spfrnd.de/posts/2018-03-10-fast-exponential.html
  // It is also implemented in the supplemental code of the Vorba paper about gaussian mixtures for path guiding.
  // Eigen also does it this way by default but with more precision.

  static_assert(std::numeric_limits<float>::is_iec559);
  static_assert(sizeof(float) == sizeof(std::uint32_t));
  constexpr int N = N_;
  constexpr float log2_e = 1.4426950408889634f;
  // This polynomial gives less than 0.3% relative error.
  constexpr float poly_coeffs[3] = { 0.34271437f, 0.6496069f , 1.f + 0.0036554f };
  constexpr uint32_t exp_mask = 255u << 23u;

  using FloatVals = Eigen::Array<float, N, 1>;
  using IntVals = Eigen::Array<int, N, 1>;

  vals *= log2_e;

  // Notes:
  //  floor() needs some fancy instructions sets to be vectorized. Compilation with -march=core2 is not enough. corei7-avx works.
  //  Apparently my CPU has it
  //  https://ark.intel.com/content/www/us/en/ark/products/52214/intel-core-i7-2600k-processor-8m-cache-up-to-3-80-ghz.html
  //  See also the Eigen manual https://eigen.tuxfamily.org/dox/group__CoeffwiseMathFunctions.html
  auto floored = vals.floor().eval();
  const IntVals xi = floored.template cast<int>();
  const FloatVals xf = vals - floored;

  vals = xf*(xf*poly_coeffs[0] + poly_coeffs[1]) + poly_coeffs[2];

  // memcpy does actually copy stuff :-(
  //std::uint32_t int_view[N];
  //std::memcpy(int_view, vals.data(), N*sizeof(float));

  // This is UB afaik. But every reasonable compiler should do the right thing ...
  std::uint32_t* int_view = reinterpret_cast<uint32_t*>(vals.data());

  // Should be auto-vectorized. Clang 9 in compiler explorer can do it!
  for (int i=0; i<N; ++i)
  {
      int_view[i] = (int_view[i] & ~exp_mask) | ((((xi[i] + 127)) << 23) & exp_mask);
  }

  //std::memcpy(vals.data(), int_view, N*sizeof(float));
}

float ExpApproximation(float x)
{
  // Should compile to ca 10 instructions.
  // And it can be inlined in contrast to std::exp().
  static_assert(std::numeric_limits<float>::is_iec559);
  static_assert(sizeof(float) == sizeof(std::uint32_t));

  constexpr float log2_e = 1.4426950408889634f;
  constexpr float poly_coeffs[3] = { 0.34271437f, 0.6496069f , 1.f + 0.0036554f };
  constexpr uint32_t exp_mask = 255u << 23u;

  x *= log2_e;

  const float floored = std::floor(x);
  const int xi = int(floored);
  const float xf = x - floored;

  const float mantisse = xf*(xf*poly_coeffs[0] + poly_coeffs[1]) + poly_coeffs[2];

  std::uint32_t int_view;
  std::memcpy(&int_view, &mantisse, sizeof(float));

  int_view = (int_view & ~exp_mask) | ((((xi + 127)) << 23) & exp_mask);

  float result;
  std::memcpy(&result, &int_view, sizeof(float));

  return result;
}

template<int N = 8>
inline Eigen::Array<float, N, 1> ComponentPdfs(const VonMisesFischerMixture<N> & mixture, const Eigen::Vector3f & pos) noexcept
{
  const auto& k = mixture.concentrations;
  assert((k >= K_THRESHOLD).all() && (k <= K_THRESHOLD_MAX).all());
  auto t1 = (-2.f*k).eval();
  ExpApproximation<N>(t1);
  //assert(t1.isFinite().all());
  const auto prefactors = (float(Pi)*2.f*(1.f - t1)).eval();
  //assert((prefactors > -0.1f).all());
  //assert((prefactors > 0.f).all());
  auto t2 = (k*((mixture.means.matrix() * pos).array() - 1.f)).eval();
  ExpApproximation<N>(t2);
  //assert(t2.isFinite().all());
  const auto result = (k / prefactors * t2).eval();
  assert(result.isFinite().all() && (result >= 0.f).all());
  return result;
}

template<int N = 8>
float Pdf(const VonMisesFischerMixture<N> & mixture, const Eigen::Vector3f & pos) noexcept
{
  const auto component_pdfs = ComponentPdfs(mixture, pos);
  return (component_pdfs * mixture.weights).sum();
}

Eigen::Vector3f Sample(const Eigen::Vector3f& mu, float k, float r1, float r2) noexcept
{
  assert (k >= K_THRESHOLD);
  assert (k <= K_THRESHOLD_MAX);
  const float vx = std::cos((float)(Pi*2.)*r1);
  const float vy = std::sin((float)(Pi*2.)*r1);
  // if (!std::isfinite(vx) || !std::isfinite(vy))
  //   std::cerr << "Oh noz vx or vy are non-finite! " << vx << ", " << vy << std::endl;
  const float w = 1.f + std::log(r2 + (1.f - r2)*ExpApproximation(-2.f*k)) / k;
  // if (!std::isfinite(w))
  //   std::cerr << "Oh noz w is not finite! " << w << std::endl;
  const float tmp = 1.f - w * w;
  // if (tmp < 0.f)
  //   std::cerr << "Oh noz tmp is negative! " << std::setprecision(std::numeric_limits<float>::digits10 + 1) << tmp << std::endl;
  const float rho = std::sqrt(std::max(0.f, tmp));
  // if (!std::isfinite(rho))
  //   std::cerr << "Oh noz rho is not finite! " << rho << ", " << w << std::endl;
  Eigen::Vector3f x{
    rho*vx, rho*vy, w
  };
  Eigen::Matrix3f frame = OrthogonalSystemZAligned(mu);
  // if (!frame.array().isFinite().all())
  //   std::cerr << "Oh noz the frame is not finite! " << frame << ", mu = " << mu << std::endl;
  return frame * x;
}

template<int N = 8>
Eigen::Vector3f Sample(const VonMisesFischerMixture<N> & mixture, std::array<double, 3> rs) noexcept
{
  const int idx = TowerSampling<N>(mixture.weights.data(), (float)rs[0]);
  return Sample(mixture.means.row(idx).matrix(), mixture.concentrations[idx], (float)rs[1], (float)rs[2]);
}

template<int N>
void InitializeForUnitSphere(VonMisesFischerMixture<N> & mixture)  noexcept
{
  assert ("!Not implemented!");
}

template<>
void InitializeForUnitSphere<2>(VonMisesFischerMixture<2> & mixture)  noexcept
{
  using MoVMF = VonMisesFischerMixture<2>;
  mixture.means <<
    -1., 0., 0.,
     1., 0., 0.;
  mixture.concentrations = MoVMF::ConcArray::Constant(0.1f);
  mixture.weights = typename MoVMF::WeightArray(1.f / MoVMF::NUM_COMPONENTS);
}

template<>
void InitializeForUnitSphere<8>(VonMisesFischerMixture<8> & mixture)  noexcept
{
  using MoVMF = VonMisesFischerMixture<8>;
    mixture.means <<
      0.66778004,0.73539025,0.11519922,
      -0.9836298,-0.008926501,0.1799797,
      -0.14629413,-0.7809748,0.60718733,
      0.49043104,0.04368747,-0.87038434,
      -0.10088497,0.42765132,0.8982965,
      -0.35999632,-0.6945797,-0.62286574,
      -0.43178025,0.7588791,-0.48751223,
      0.860208,-0.47938251,0.17388113;
  mixture.concentrations = MoVMF::ConcArray::Constant(2.f);
  mixture.weights = typename MoVMF::WeightArray(1.f / MoVMF::NUM_COMPONENTS);
}

template<>
void InitializeForUnitSphere<16>(VonMisesFischerMixture<16> & mixture)  noexcept
{
  using MoVMF = VonMisesFischerMixture<16>;
  mixture.means <<
    0.49468744,-0.1440351,-0.8570521,
    0.36735174,-0.8573688,-0.36051556,
    -0.40503788,-0.48854542,-0.772831,
    -0.32770208,-0.12784185,0.9360917,
    -0.46322507,-0.8861626,-0.011769729,
    -0.933043,0.16280192,-0.32082137,
    -0.4759586,0.8746706,-0.09173246,
    -0.2931369,0.36415973,-0.8840013,
    -0.9006278,-0.24284472,0.360411,
    -0.32578856,0.650924,0.685682,
    0.5459307,0.11810207,0.8294646,
    0.9575066,0.22513846,-0.18026012,
    0.84576833,-0.47598344,0.24107178,
    0.47158864,0.8217275,0.31995004,
    0.12246728,-0.77039504,0.6256942,
    0.34670526,0.77106386,-0.5340936;
    mixture.concentrations = MoVMF::ConcArray::Constant(5.f);
    mixture.weights = typename MoVMF::WeightArray(1.f / MoVMF::NUM_COMPONENTS);
}

template<int N, int M>
VonMisesFischerMixture<N*M> Product(const VonMisesFischerMixture<N> &m1, const VonMisesFischerMixture<M> &m2) noexcept
{
  static constexpr int NM = N*M;
  VonMisesFischerMixture<NM> result;
  for (int i=0; i<N; ++i)
  {
    result.means.block(i*M, 0, M, 3) = (m1.concentrations[i]*m1.means.row(i)).replicate(M,1) + (m2.means*m2.concentrations.replicate(1,3));
  }
  result.concentrations = result.means.matrix().rowwise().norm();
  result.means.colwise() /= result.concentrations;

  // Weights ...
  const typename VonMisesFischerMixture<N>::WeightArray  exponentials1 = 1.f+incremental::eps - (-2.f*m1.concentrations    ).exp();
  const typename VonMisesFischerMixture<M>::WeightArray  exponentials2 = 1.f+incremental::eps - (-2.f*m2.concentrations    ).exp();
  const typename VonMisesFischerMixture<NM>::WeightArray exponentialsk = 1.f+incremental::eps - (-2.f*result.concentrations).exp();

  for (int i=0; i<N; ++i)
  {
    for (int j=0; j<M; ++j)
    {
      const int k = i*M + j;
      result.weights[k] = m1.concentrations[i]*m2.concentrations[j]*exponentialsk[k] /
                          (2.f*PiFloat*result.concentrations[k]*exponentials1[i]*exponentials2[j] + incremental::eps);
      result.weights[k] *= ExpApproximation(m1.concentrations[i]*(m1.means.row(i).matrix().dot(result.means.row(k).matrix())-1.f) +
                                    m2.concentrations[j]*(m2.means.row(j).matrix().dot(result.means.row(k).matrix())-1.f));
      result.weights[k] *= m1.weights[i] * m2.weights[j];
    }
  }

  // Hack to avoid numerical problems ...
  result.concentrations = result.concentrations.max(K_THRESHOLD).min(K_THRESHOLD_MAX).eval();
  return result;
}

template<int N>
void Normalize(VonMisesFischerMixture<N> &mixture) noexcept
{
  const float wsum = mixture.weights.sum();
  if (unlikely(wsum <= 0.f))
  {
    // Case may happen due to underflow in calculations with exponentials
    mixture.weights.setConstant(1.f/N);
  }
  else
  {
    mixture.weights /= wsum;
  }
}

namespace incremental
{

template<int N>
void UpdateStatistics(VonMisesFischerMixture<N> &mixture, Data<N> &dta, const Params<N> &params, const Eigen::Vector3f &x, float weight) noexcept;

template<int N>
void MaximizationStep(VonMisesFischerMixture<N> &mixture, const Data<N> &dta, const Params<N> &params) noexcept;

template<int N>
void Fit(VonMisesFischerMixture<N> &mixture, Data<N> &dta, const Params<N> &params, Span<const Eigen::Vector3f> data, Span<const float> data_weights) noexcept
{
  assert(data.size() == data_weights.size());
  assert(params.prior_mode != nullptr);

  for (int i=0; i<data.size(); ++i)
  {
    UpdateStatistics(mixture, dta, params, data[i], data_weights[i]);
    if (dta.avg_positions.Count() % params.maximization_step_every == 0)
    {
      MaximizationStep(mixture, dta, params);

      // Clear the statistics. Only keep average weights since they don't depend on the mixture parameters.
      auto backup = dta.avg_weights;
      dta = Data<N>{};
      dta.avg_weights = backup;
    }
  }
}

template<int N>
void UpdateStatistics(VonMisesFischerMixture<N> & mixture, Data<N> &fitdata, const Params<N> &params, const Eigen::Vector3f & x, float weight) noexcept
{
  Eigen::Array<float, N, 1> responsibilities = mixture.weights * ComponentPdfs(mixture, x) + eps;
  responsibilities /= responsibilities.sum();

#if 0
  if (fitdata.data_count_weights == 0)
    fitdata.avg_weights = weight;
  else
    fitdata.avg_weights = Lerp(fitdata.avg_weights, weight, (float)std::pow(double(fitdata.data_count_weights), -0.75));

  if (fitdata.data_count == 0)
  {
    fitdata.avg_responsibilities_unweighted = responsibilities;
    fitdata.avg_responsibilities = weight * responsibilities;
    fitdata.avg_positions = weight*(responsibilities.matrix()*x.transpose()).array();
  }
  else
  {
    // How much to "trust" new data.
    const float mix_factor = (float)std::pow(double(fitdata.data_count), -0.75f);
    fitdata.avg_responsibilities_unweighted += mix_factor*(responsibilities        - fitdata.avg_responsibilities_unweighted);
    fitdata.avg_responsibilities            += mix_factor*(weight*responsibilities - fitdata.avg_responsibilities);
    fitdata.avg_positions                   += mix_factor*(weight*(responsibilities.matrix()*x.transpose()).array() - fitdata.avg_positions);
  }
  ++fitdata.data_count;
  ++fitdata.data_count_weights;
#else
  fitdata.avg_weights += weight;
  fitdata.avg_responsibilities_unweighted += responsibilities;
  fitdata.avg_responsibilities  += weight * responsibilities;
  fitdata.avg_positions += weight*(responsibilities.matrix()*x.transpose()).array();
#endif
}

template<int N>
void MaximizationStep(VonMisesFischerMixture<N> & mixture, const Data<N> &dta, const Params<N> &params) noexcept
{

  const std::uint64_t unique_data_count = dta.avg_responsibilities.Count();
  for (int k = 0; k < VonMisesFischerMixture<N>::NUM_COMPONENTS; ++k)
  {
    const float scaled_responsibilities = dta.avg_responsibilities()[k] / (dta.avg_weights() + eps);

    // The eps is there in case both of the former terms evaluate to zero!
    mixture.weights[k] = (params.prior_nu-1)/unique_data_count*params.prior_mode->weights[k] + scaled_responsibilities + eps;

    { // Posterior of mean.
      const float diminished_prior_factor = dta.avg_weights() * params.prior_tau / unique_data_count;
      mixture.means.row(k) = (diminished_prior_factor*params.prior_mode->means.row(k) + dta.avg_positions().row(k)) /
        (diminished_prior_factor + dta.avg_responsibilities()[k] + eps);
      float norm = mixture.means.row(k).matrix().norm();
      if (norm > eps)
        mixture.means.row(k) /= norm;
      else
        mixture.means.row(k).matrix() = Eigen::Vector3f{1.f, 0.f, 0.f};
    }

    {
      #if 1
      // Note: Don't use avg_responsibilities_unweighted[k]*avg_weights here. It computes the average position wrong
      // in a way that mean_cosine is way overestimated.
      const float mean_cosine = (1.f/(dta.avg_responsibilities()[k] + eps)) * dta.avg_positions().row(k).matrix().norm();
      const float conc_estimate = MeanCosineToConc(mean_cosine);
      const float diminished_alpha = params.prior_alpha/unique_data_count;
      const float post_conc =
        (params.prior_mode->concentrations[k]*diminished_alpha + conc_estimate) / (diminished_alpha + 1.f);
      mixture.concentrations[k] = post_conc;
      #endif

      #if 0
      // This does not do percievably better than the simpler code above!!
      // Here the prior is on the mean cosine, forcing me to go back and forth with the cosine-k-conversions.
      const float prior_cos = ConcToMeanCos(params.prior_mode->concentrations[k]);
      const float mean_cosine = (1.f/(dta.avg_responsibilities()[k] + eps)) * dta.avg_positions().row(k).matrix().norm();
      const float diminished_alpha = params.prior_alpha/unique_data_count;
      const float mean_cosine_post = (diminished_alpha*prior_cos + mean_cosine) / (diminished_alpha + 1.f);
      mixture.concentrations[k] = MeanCosineToConc(mean_cosine_post);
      #endif
    }
  }

  mixture.concentrations = mixture.concentrations.max(K_THRESHOLD).min(K_THRESHOLD_MAX).eval();

  assert(mixture.weights.sum() > 0.f);
  mixture.weights /= mixture.weights.sum();
}

} // namespace incremental

} // namespace vmf_fitting

#define INSTANTIATE_VonMisesFischerMixture(n) \
  template void vmf_fitting::incremental::Fit<n>(VonMisesFischerMixture<n> &mixture, Data<n> &fitdata, const Params<n> &params, Span<const Eigen::Vector3f> data, Span<const float> data_weights) noexcept; \
  template float  vmf_fitting::Pdf<n>(const VonMisesFischerMixture<n> &mixture, const Eigen::Vector3f &pos) noexcept; \
  template Eigen::Vector3f  vmf_fitting::Sample<n>(const VonMisesFischerMixture<n> &mixture, std::array<double, 3> rs) noexcept; \
  template void  vmf_fitting::InitializeForUnitSphere(VonMisesFischerMixture<n> &mixture) noexcept; \
  template void vmf_fitting::Normalize(VonMisesFischerMixture<n> &mixture) noexcept; \
  template void vmf_fitting::ExpApproximation<n>(Eigen::Array<float, n, 1> &vals);

INSTANTIATE_VonMisesFischerMixture(2)
INSTANTIATE_VonMisesFischerMixture(8)
INSTANTIATE_VonMisesFischerMixture(16)

template vmf_fitting::VonMisesFischerMixture<16> vmf_fitting::Product(const vmf_fitting::VonMisesFischerMixture<2> &m1, const vmf_fitting::VonMisesFischerMixture<8> &m2) noexcept;


/// \file
/// Maintainer: Felice Serena
///
///

#include "brute_force.h"
#include <Eigen/Core>
#include <set>

#include <boost/test/unit_test.hpp>

namespace utf = boost::unit_test;

typedef std::vector<int> CellCoordinate;

using namespace MouseTrack;
using namespace Eigen;

BOOST_AUTO_TEST_CASE(brute_force_4d_in_range) {
  constexpr int Dim = 4;
  typedef BruteForce4d BF;
  BF::PointList all(Dim, 5);
  all.col(0) = Vector4d(0.0, 0.0, 0.0, 0.0);
  all.col(1) = Vector4d(2.0, 2.0, 2.0, 2.0);
  all.col(2) = Vector4d(10.0, 0.0, 0.0, 0.0);
  all.col(3) = Vector4d(0.0, -1.0, -1.0, -1.0);
  all.col(4) = Vector4d(10.0, 1.0, 1.0, 1.0);

  std::set<PointIndex> expected0;
  expected0.insert(2);
  expected0.insert(4);

  std::set<PointIndex> expected1;
  expected1.insert(0);
  expected1.insert(3);

  BF oracle;
  oracle.compute(all);

  Eigen::Matrix<double, 4, -1> query(4, 2);
  query.col(0) = Vector4d(9.5, 0, 0, 0);
  query.col(1) = Vector4d(0.0, -0.5, -0.5, -0.5);

  auto result = oracle.find_in_range(query, 2);

  std::set<PointIndex> received0(result[0].begin(), result[0].end());
  std::set<PointIndex> received1(result[1].begin(), result[1].end());

  BOOST_CHECK_EQUAL(result.size(), 2);

  BOOST_CHECK_MESSAGE(
      expected0.size() == received0.size(),
      "Expected and received sets have different cardinalities.");
  BOOST_CHECK_MESSAGE(
      expected0 == received0,
      "Expected and received set do not contain same elements.");

  BOOST_CHECK_MESSAGE(
      expected1.size() == received1.size(),
      "Expected and received sets have different cardinalities.");
  BOOST_CHECK_MESSAGE(
      expected1 == received1,
      "Expected and received set do not contain same elements.");
}

BOOST_AUTO_TEST_CASE(brute_force_Xd_in_range) {
  constexpr int Dim = 4;
  typedef BruteForceXd BF;
  BF::PointList all(Dim, 5);
  all.col(0) = Vector4d(0.0, 0.0, 0.0, 0.0);
  all.col(1) = Vector4d(2.0, 2.0, 2.0, 2.0);
  all.col(2) = Vector4d(10.0, 0.0, 0.0, 0.0);
  all.col(3) = Vector4d(0.0, -1.0, -1.0, -1.0);
  all.col(4) = Vector4d(10.0, 1.0, 1.0, 1.0);

  std::multiset<PointIndex> expected;
  expected.insert(2);
  expected.insert(4);

  BF oracle;
  oracle.compute(all);

  auto result = oracle.find_in_range(Vector4d(9.5, 0, 0, 0), 2)[0];

  std::multiset<PointIndex> received(result.begin(), result.end());

  BOOST_CHECK_MESSAGE(
      expected.size() == received.size(),
      "Expected and received sets have different cardinalities.");
  BOOST_CHECK_MESSAGE(
      expected == received,
      "Expected and received set do not contain same elements.");
}

BOOST_AUTO_TEST_CASE(brute_force_Xd_find_closest) {
  constexpr int Dim = 4;
  typedef BruteForceXd BF;
  BF::PointList all(Dim, 5);
  all.col(0) = Vector4d(0.0, 0.0, 0.0, 0.0);
  all.col(1) = Vector4d(2.0, 2.0, 2.0, 2.0);
  all.col(2) = Vector4d(10.0, 0.0, 0.0, 0.0);
  all.col(3) = Vector4d(0.0, -1.0, -1.0, -1.0);
  all.col(4) = Vector4d(10.0, 1.0, 1.0, 1.0);

  BF oracle;
  oracle.compute(all);

  std::vector<size_t> expected0;
  expected0.push_back(4);

  std::vector<size_t> expected1;
  expected1.push_back(0);

  Eigen::MatrixXd query(4, 2);
  query.col(0) = Vector4d(10.1, 0.9, 0.9, 0.9);
  query.col(1) = Vector4d(0.1, 0.1, 0.1, 0.1);

  auto result = oracle.find_closest(query, 1);
  BOOST_CHECK_EQUAL(expected1.size(), result[0].size());
  BOOST_CHECK_EQUAL(expected0[0], result[0][0]);

  BOOST_CHECK_EQUAL(expected1.size(), result[1].size());
  BOOST_CHECK_EQUAL(expected1[0], result[1][0]);
}

BOOST_AUTO_TEST_CASE(brute_force_Xd_find_closestK) {
  constexpr int Dim = 4;
  typedef BruteForceXd BF;
  BF::PointList all(Dim, 5);
  all.col(0) = Vector4d(0.0, 0.0, 0.0, 0.0);
  all.col(1) = Vector4d(2.0, 2.0, 2.0, 2.0);
  all.col(2) = Vector4d(10.0, 0.0, 0.0, 0.0);
  all.col(3) = Vector4d(0.0, -1.0, -1.0, -1.0);
  all.col(4) = Vector4d(10.0, 1.0, 1.0, 1.0);

  std::multiset<PointIndex> expected;
  expected.insert(2);
  expected.insert(4);

  BF oracle;
  oracle.compute(all);

  auto result = oracle.find_closest(Vector4d(10.1, 0.9, 0.9, 0.9), 2)[0];

  std::multiset<PointIndex> received(result.begin(), result.end());

  BOOST_CHECK_MESSAGE(
      expected.size() == received.size(),
      "Expected and received sets have different cardinalities.");
  BOOST_CHECK_MESSAGE(
      expected == received,
      "Expected and received set do not contain same elements.");
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                          *** Mesh.hpp ***                                 //
//                                                                           //
// Base class responsible for holding discretized mesh points and weights    //
// templatized over dimension of the mesh                                    //
//                                                                           //
// created November 26, 2017                                                 //
// copyright Christopher N. Singh Binghamton University Physics              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifndef Mesh_hpp
#define Mesh_hpp

#include <Eigen/Dense>
#include <numeric>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

class Mesh
{
public:
	Mesh(std::string klist_file) {

		std::ifstream file(klist_file);
		assert(file.is_open());

		std::string line;
		Eigen::Vector3d invec;
		double trash, weight, div;

		while (std::getline(file, line)) {
			if (line == "END") break;
			std::stringstream input(line);
			input >> trash;
			for (int i = 0; i < 3; ++i) input >> invec(i);
			input >> div;
			m_points.push_back(invec * 2 * M_PI / div);
			input >> weight;
			m_weights.push_back(weight);
		}
	}

	Eigen::Vector3d point(int index) { return m_points[index]; }
	int weight(int index) { return m_weights[index]; }
	int size() { return m_points.size(); }
	int norm() { return std::accumulate(m_weights.begin(), m_weights.end(), 0); }

private:
	std::vector<Eigen::Vector3d> m_points;
	std::vector<int> m_weights;
};

#endif /* Mesh_hpp */