/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2010: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#ifndef BOOST_LIBS_ICL_TEST_ICL_LAWS_HPP_JOFA_090119
#define BOOST_LIBS_ICL_TEST_ICL_LAWS_HPP_JOFA_090119

#include <boost/icl/type_traits/identity_element.hpp>
#include <boost/icl/associative_interval_container.hpp>

namespace boost{namespace icl
{

template<class Type>
struct equality
{
    typedef Type first_argument_type;
    typedef Type second_argument_type;
    typedef bool result_type;
    typedef bool (type)(const Type&, const Type&);
};

//------------------------------------------------------------------------------
// Associativity
//------------------------------------------------------------------------------

// The definition of a boost test via a macro, that allows to be instantiated for
// different operator signs 'op_sign'. For every 'op_sign' a separate function
// template is generated by appending the suffic 'op_tag' to the function
// name.
// So this is the function template via macro that checks the law
// associativity for an operator 'op_sign'
#define DEFINE_ASSOCIATIVITY_CHECK_WRT(op_tag, op_sign) \
template<class Type, class TypeB, class TypeC> \
void check_associativity_wrt_##op_tag(const Type& a, const TypeB& b, const TypeC& c) \
{ \
    Type left  = (a op_sign b) op_sign c; \
    Type right = a op_sign (b op_sign c); \
    BOOST_CHECK(left==right); \
}

#define DEFINE_ASSOCIATIVITY_CHECK_WRT_EQUAL(op_tag, op_sign) \
template<class Type, class TypeB, class TypeC> \
void check_associativity_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    Type left  = (a op_sign b) op_sign c; \
    Type right = a op_sign (b op_sign c); \
    BOOST_CHECK((*equal)(left,right)); \
}

// Here we define the call of the test function thats definition
// template has been generated by the macro above.
#define CHECK_ASSOCIATIVITY_WRT(op_tag)       check_associativity_wrt_##op_tag
#define CHECK_ASSOCIATIVITY_WRT_EQUAL(op_tag) check_associativity_wrt_equal_##op_tag

// Here we actually generate function templates by calling the defining
// macros.
DEFINE_ASSOCIATIVITY_CHECK_WRT      (plus,  + )
DEFINE_ASSOCIATIVITY_CHECK_WRT_EQUAL(plus,  + )
DEFINE_ASSOCIATIVITY_CHECK_WRT      (pipe,  | )
DEFINE_ASSOCIATIVITY_CHECK_WRT_EQUAL(pipe,  | )
DEFINE_ASSOCIATIVITY_CHECK_WRT      (et,    & )
DEFINE_ASSOCIATIVITY_CHECK_WRT_EQUAL(et,    & )
DEFINE_ASSOCIATIVITY_CHECK_WRT      (caret, ^ )
DEFINE_ASSOCIATIVITY_CHECK_WRT_EQUAL(caret, ^ )

//------------------------------------------------------------------------------
// Neutrality
//------------------------------------------------------------------------------

#define DEFINE_RIGHT_NEUTRALITY_CHECK_WRT_EQUAL(op_tag, op_sign) \
template<class Type> \
void check_right_neutrality_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& a, const Type& identity_element) \
{ \
    Type left  = a op_sign identity_element; \
    Type right = identity_element; \
    BOOST_CHECK((*equal)(left,right)); \
}

#define DEFINE_RIGHT_NEUTRALITY_CHECK_WRT(op_tag, op_sign) \
template<class Type> \
void check_right_neutrality_wrt_##op_tag \
(typename equality<Type>::type* equal, const Type& a, const Type& identity_element) \
{ \
    Type left  = a op_sign identity_element; \
    Type right = identity_element; \
    BOOST_CHECK(left==right); \
}

#define DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(op_tag, op_sign) \
template<class Type> \
void check_neutrality_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& a, const Type& identity_element) \
{ \
    Type left  = a op_sign identity_element; \
    Type right = identity_element op_sign a; \
    BOOST_CHECK((*equal)(left,right)); \
}

#define DEFINE_NEUTRALITY_CHECK_WRT(op_tag, op_sign) \
template<class Type> \
void check_neutrality_wrt_##op_tag \
(const Type& a, const Type& identity_element) \
{ \
    Type left  = a op_sign identity_element; \
    Type right = identity_element op_sign a; \
    BOOST_CHECK(left==right); \
}

#define CHECK_NEUTRALITY_WRT(op_tag) check_neutrality_wrt_##op_tag
#define CHECK_NEUTRALITY_WRT_EQUAL(op_tag) check_neutrality_wrt_equal_##op_tag

DEFINE_NEUTRALITY_CHECK_WRT      (plus,  + )
DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(plus,  + )
DEFINE_NEUTRALITY_CHECK_WRT      (pipe,  | )
DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(pipe,  | )
DEFINE_NEUTRALITY_CHECK_WRT      (minus, - )
DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(minus, - )
DEFINE_NEUTRALITY_CHECK_WRT      (et,    & )
DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(et,    & )
DEFINE_NEUTRALITY_CHECK_WRT      (caret, ^ )
DEFINE_NEUTRALITY_CHECK_WRT_EQUAL(caret, ^ )

//------------------------------------------------------------------------------
// Commutativity
//------------------------------------------------------------------------------

#define DEFINE_COMMUTATIVITY_CHECK_WRT_EQUAL(op_tag, op_sign) \
template<class Type, class TypeB> \
void check_commutativity_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& a, const TypeB& b) \
{ \
    Type left  = a op_sign b; \
    Type right = b op_sign a; \
    BOOST_CHECK((*equal)(left,right)); \
}

#define DEFINE_COMMUTATIVITY_CHECK_WRT(op_tag, op_sign) \
template<class Type, class TypeB> \
    void check_commutativity_wrt_##op_tag(const Type& a, const TypeB& b) \
{ \
    Type left  = a op_sign b; \
    Type right = b op_sign a; \
    BOOST_CHECK(left==right); \
}

#define CHECK_COMMUTATIVITY_WRT_EQUAL(op_tag) check_commutativity_wrt_equal_##op_tag
#define CHECK_COMMUTATIVITY_WRT(op_tag) check_commutativity_wrt_##op_tag

DEFINE_COMMUTATIVITY_CHECK_WRT      (plus,  + )
DEFINE_COMMUTATIVITY_CHECK_WRT_EQUAL(plus,  + )
DEFINE_COMMUTATIVITY_CHECK_WRT      (pipe,  | )
DEFINE_COMMUTATIVITY_CHECK_WRT_EQUAL(pipe,  | )
DEFINE_COMMUTATIVITY_CHECK_WRT      (et,    & )
DEFINE_COMMUTATIVITY_CHECK_WRT_EQUAL(et,    & )
DEFINE_COMMUTATIVITY_CHECK_WRT      (caret, ^ )
DEFINE_COMMUTATIVITY_CHECK_WRT_EQUAL(caret, ^ )

//------------------------------------------------------------------------------
// Natural inversion
//------------------------------------------------------------------------------

#define DEFINE_PARTIAL_INVERSION_CHECK_WRT_EQUAL(plus_tag, plus_sign) \
template<class Type> \
    void check_partial_inversion_wrt_equal_##plus_tag \
(typename equality<Type>::type* equal, const Type& identity_element, const Type& var_a) \
{ \
    Type positive_difference  = var_a - var_a; \
    BOOST_CHECK((*equal)(positive_difference, identity_element)); \
}

#define DEFINE_PARTIAL_INVERSION_CHECK_WRT(plus_tag, plus_sign) \
template<class Type> \
    void check_partial_inversion_wrt_##plus_tag \
(const Type& identity_element, const Type& var_a) \
{ \
    Type positive_difference  = var_a - var_a; \
    BOOST_CHECK(positive_difference==identity_element); \
}

#define CHECK_PARTIAL_INVERSION_WRT_EQUAL(plus_tag) check_partial_inversion_wrt_equal_##plus_tag
#define CHECK_PARTIAL_INVERSION_WRT(plus_tag) check_partial_inversion_wrt_##plus_tag

DEFINE_PARTIAL_INVERSION_CHECK_WRT      (plus,  + )
DEFINE_PARTIAL_INVERSION_CHECK_WRT_EQUAL(plus,  + )
DEFINE_PARTIAL_INVERSION_CHECK_WRT      (pipe,  | )
DEFINE_PARTIAL_INVERSION_CHECK_WRT_EQUAL(pipe,  | )

//------------------------------------------------------------------------------
// Inverse
//------------------------------------------------------------------------------

#define DEFINE_INVERSE_CHECK_WRT_EQUAL(plus_tag, plus_sign) \
template<class Type> \
    void check_inverse_wrt_equal_##plus_tag \
(typename equality<Type>::type* equal, const Type& identity_element, const Type& var_a) \
{ \
    Type positive_difference  = var_a - var_a; \
    BOOST_CHECK((*equal)(positive_difference, identity_element)); \
    Type negative_difference = (identity_element - var_a) plus_sign var_a; \
    BOOST_CHECK((*equal)(negative_difference, identity_element)); \
}

#define DEFINE_INVERSE_CHECK_WRT(plus_tag, plus_sign) \
template<class Type> \
    void check_inverse_wrt_##plus_tag \
(const Type& identity_element, const Type& var_a) \
{ \
    Type positive_difference  = var_a - var_a; \
    BOOST_CHECK(positive_difference==identity_element); \
    Type negative_difference = (identity_element - var_a) plus_sign var_a; \
    BOOST_CHECK(negative_difference==identity_element); \
}

#define CHECK_INVERSE_WRT_EQUAL(plus_tag) check_inverse_wrt_equal_##plus_tag
#define CHECK_INVERSE_WRT(plus_tag) check_inverse_wrt_##plus_tag

DEFINE_INVERSE_CHECK_WRT      (plus,  + )
DEFINE_INVERSE_CHECK_WRT_EQUAL(plus,  + )
DEFINE_INVERSE_CHECK_WRT      (pipe,  | )
DEFINE_INVERSE_CHECK_WRT_EQUAL(pipe,  | )

//------------------------------------------------------------------------------
// Containedness laws (Valid for sets, NOT for maps)
//------------------------------------------------------------------------------
template<class Type, class TypeB>
void check_intersection_containedness(const Type& a, const TypeB& b)
{
    BOOST_CHECK_EQUAL(contains(a, a & b), true);
    BOOST_CHECK_EQUAL(contains(a, b & a), true);
    BOOST_CHECK_EQUAL(within(a & b, a), true);
    BOOST_CHECK_EQUAL(within(b & a, a), true);
}

template<class Type, class TypeB>
void check_union_containedness(const Type& a, const TypeB& b)
{
    BOOST_CHECK_EQUAL(within(a, a + b), true);
    BOOST_CHECK_EQUAL(within(a, b + a), true);
    BOOST_CHECK_EQUAL(contains(a + b, a), true);
    BOOST_CHECK_EQUAL(contains(b + a, a), true);
}

template<class MapT>
void check_domain_containedness(const MapT& a)
{
    typedef typename MapT::set_type set_type;
    set_type dom;
    domain(dom, a);
    BOOST_CHECK_EQUAL(within(dom, a), true);
    BOOST_CHECK_EQUAL(contains(a, dom), true);

    if(!dom.empty())
    {
        typename MapT::domain_type a1 = *dom.begin();
        BOOST_CHECK_EQUAL(within(a1, a), true);
        BOOST_CHECK_EQUAL(contains(a, a1), true);
    }
}

//------------------------------------------------------------------------------
// Laws on inner complement, between and distance
//------------------------------------------------------------------------------
template<class Type, class SetT>
bool check_length_as_distance( const Type& object
                             , SetT& in_between
                             , typename Type::difference_type& between_len
                             , typename Type::difference_type& obj_dist )
{
    //LAW LengthAsDistance: distance(x) == length(between(x))
    icl::between(in_between, object);
    between_len  = icl::length(in_between);
    obj_dist     = icl::distance(object);
    BOOST_CHECK_EQUAL(obj_dist, between_len);
    return obj_dist == between_len;
}

template<class Type, class SetT>
bool has_length_as_distance(const Type& object)
{
    typedef typename Type::difference_type DiffT;
    SetT in_between; DiffT between_len, obj_dist;
    return check_length_as_distance(object, in_between, between_len, obj_dist);
}

template<class Type>
bool check_length_complementarity( const Type& object
                                 , typename Type::difference_type& obj_len
                                 , typename Type::difference_type& obj_dist
                                 , typename Type::difference_type& hull_len )
{
    //LAW Length Complementarity: length(x) + distance(x) == length(hull(x))
    obj_len  = icl::length(object);
    obj_dist = icl::distance(object);
    hull_len = icl::length(hull(object));
    BOOST_CHECK_EQUAL(obj_len + obj_dist, hull_len);
    return obj_len + obj_dist == hull_len;
}

template<class Type>
bool has_length_complementarity(const Type& object)
{
    typedef typename Type::difference_type DiffT;
    DiffT obj_len, obj_dist, hull_len;
    return check_length_complementarity(object, obj_len, obj_dist, hull_len);
}

template<class Type, class SetT>
bool check_inner_complementarity(const Type& object, SetT& in_between, SetT& lhs, SetT& rhs)
{
    //LAW Inner Complementarity1: domain(x) + between(x)       =e= hull(x)
    //LAW Inner Complementarity2: join(domain(x) + between(x))  == hull(x)
    SetT dom;
    icl::domain(dom, object);
    lhs = dom + between(in_between, object);
    join(lhs);
    rhs = SetT(hull(object));
    BOOST_CHECK_EQUAL(lhs, rhs);
    return lhs == rhs;
}

template<class Type, class SetT>
bool has_inner_complementarity(const Type& object)
{
    SetT in_between, lhs, rhs;
    return check_inner_complementarity(object, in_between, lhs, rhs);
}

//==============================================================================
// Law tests are now combined to test algebraic concepts.
//------------------------------------------------------------------------------
// Monodid EAN
//------------------------------------------------------------------------------

#define DEFINE_MONOID_CHECK_WRT_EQUAL(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_monoid_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ASSOCIATIVITY_WRT_EQUAL(op_tag)(equal,a,b,c); \
    CHECK_NEUTRALITY_WRT_EQUAL(op_tag)(equal,a,identity_element); \
}

#define DEFINE_MONOID_CHECK_WRT(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_monoid_wrt_##op_tag \
(const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ASSOCIATIVITY_WRT(op_tag)(a,b,c); \
    CHECK_NEUTRALITY_WRT(op_tag)(a,identity_element); \
}

DEFINE_MONOID_CHECK_WRT      (plus)
DEFINE_MONOID_CHECK_WRT_EQUAL(plus)
DEFINE_MONOID_CHECK_WRT      (pipe)
DEFINE_MONOID_CHECK_WRT_EQUAL(pipe)
DEFINE_MONOID_CHECK_WRT      (et)
DEFINE_MONOID_CHECK_WRT_EQUAL(et)
DEFINE_MONOID_CHECK_WRT      (caret)
DEFINE_MONOID_CHECK_WRT_EQUAL(caret)

#define CHECK_MONOID_WRT(op_tag) check_monoid_wrt_##op_tag
#define CHECK_MONOID_WRT_EQUAL(op_tag) check_monoid_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Commutative or Abelian monodid EANC
//------------------------------------------------------------------------------
#define DEFINE_ABELIAN_MONOID_CHECK_WRT_EQUAL(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_abelian_monoid_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ASSOCIATIVITY_WRT_EQUAL(op_tag)(equal,a,b,c); \
    CHECK_NEUTRALITY_WRT_EQUAL(op_tag)(equal,a,identity_element); \
    CHECK_COMMUTATIVITY_WRT_EQUAL(op_tag)(equal,a,b); \
    CHECK_COMMUTATIVITY_WRT_EQUAL(op_tag)(equal,a,c); \
}

#define DEFINE_ABELIAN_MONOID_CHECK_WRT(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_abelian_monoid_wrt_##op_tag \
(const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ASSOCIATIVITY_WRT(op_tag)(a,b,c); \
    CHECK_NEUTRALITY_WRT(op_tag)(a,identity_element); \
    CHECK_COMMUTATIVITY_WRT(op_tag)(a,b); \
    CHECK_COMMUTATIVITY_WRT(op_tag)(a,c); \
}

DEFINE_ABELIAN_MONOID_CHECK_WRT      (plus)
DEFINE_ABELIAN_MONOID_CHECK_WRT_EQUAL(plus)
DEFINE_ABELIAN_MONOID_CHECK_WRT      (pipe)
DEFINE_ABELIAN_MONOID_CHECK_WRT_EQUAL(pipe)
DEFINE_ABELIAN_MONOID_CHECK_WRT      (et)
DEFINE_ABELIAN_MONOID_CHECK_WRT_EQUAL(et)
DEFINE_ABELIAN_MONOID_CHECK_WRT      (caret)
DEFINE_ABELIAN_MONOID_CHECK_WRT_EQUAL(caret)

#define CHECK_ABELIAN_MONOID_WRT(op_tag) check_abelian_monoid_wrt_##op_tag
#define CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag) check_abelian_monoid_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian monodid EANC with partial inversion
//------------------------------------------------------------------------------
#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT_EQUAL(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_partial_invertive_monoid_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal,identity_element,a,b,c); \
    CHECK_PARTIAL_INVERSION_WRT_EQUAL(op_tag)(equal,identity_element,a); \
}

#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT(op_tag) \
template<class Type, class TypeB, class TypeC> \
void check_partial_invertive_monoid_wrt_##op_tag \
(const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ABELIAN_MONOID_WRT(op_tag)(identity_element,a,b,c); \
    CHECK_PARTIAL_INVERSION_WRT(op_tag)(identity_element,a); \
}

DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT      (plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT_EQUAL(plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT      (pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT_EQUAL(pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT      (et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT_EQUAL(et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT      (caret)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_WRT_EQUAL(caret)

#define CHECK_PARTIAL_INVERTIVE_MONOID_WRT(op_tag) check_partial_invertive_monoid_wrt_##op_tag
#define CHECK_PARTIAL_INVERTIVE_MONOID_WRT_EQUAL(op_tag) check_partial_invertive_monoid_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian group EANIC
//------------------------------------------------------------------------------

#define DEFINE_ABELIAN_GROUP_CHECK_WRT_EQUAL(op_tag) \
template<class Type, class TypeB, class TypeC> \
    void check_abelian_group_wrt_equal_##op_tag \
(typename equality<Type>::type* equal, const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal,identity_element,a,b,c); \
    CHECK_INVERSE_WRT_EQUAL(op_tag)(equal,identity_element,a); \
}

#define DEFINE_ABELIAN_GROUP_CHECK_WRT(op_tag) \
template<class Type, class TypeB, class TypeC> \
    void check_abelian_group_wrt_##op_tag \
(const Type& identity_element, \
const Type& a, const TypeB& b, const TypeC& c) \
{ \
    CHECK_ABELIAN_MONOID_WRT(op_tag)(identity_element,a,b,c); \
    CHECK_INVERSE_WRT(op_tag)(identity_element,a); \
}

DEFINE_ABELIAN_GROUP_CHECK_WRT      (plus)
DEFINE_ABELIAN_GROUP_CHECK_WRT_EQUAL(plus)
DEFINE_ABELIAN_GROUP_CHECK_WRT      (pipe)
DEFINE_ABELIAN_GROUP_CHECK_WRT_EQUAL(pipe)

#define CHECK_ABELIAN_GROUP_WRT(op_tag) check_abelian_group_wrt_##op_tag
#define CHECK_ABELIAN_GROUP_WRT_EQUAL(op_tag) check_abelian_group_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Modoid permuted
//------------------------------------------------------------------------------

#define DEFINE_MONOID_CHECK_PERMUTED_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_monoid_permuted_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

#define DEFINE_MONOID_CHECK_PERMUTED_WRT(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_monoid_permuted_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

DEFINE_MONOID_CHECK_PERMUTED_WRT      (plus)
DEFINE_MONOID_CHECK_PERMUTED_WRT_EQUAL(plus)
DEFINE_MONOID_CHECK_PERMUTED_WRT      (pipe)
DEFINE_MONOID_CHECK_PERMUTED_WRT_EQUAL(pipe)
DEFINE_MONOID_CHECK_PERMUTED_WRT      (et)
DEFINE_MONOID_CHECK_PERMUTED_WRT_EQUAL(et)
DEFINE_MONOID_CHECK_PERMUTED_WRT      (caret)
DEFINE_MONOID_CHECK_PERMUTED_WRT_EQUAL(caret)

#define CHECK_MONOID_PERMUTED_WRT(op_tag)       check_monoid_permuted_wrt_##op_tag
#define CHECK_MONOID_PERMUTED_WRT_EQUAL(op_tag) check_monoid_permuted_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian modoid permuted
//------------------------------------------------------------------------------

#define DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_abelian_monoid_permuted_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_ABELIAN_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

#define DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_abelian_monoid_permuted_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_ABELIAN_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_ABELIAN_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_ABELIAN_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_ABELIAN_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT      (plus)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT_EQUAL(plus)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT      (pipe)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT_EQUAL(pipe)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT      (et)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT_EQUAL(et)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT      (caret)
DEFINE_ABELIAN_MONOID_CHECK_PERMUTED_WRT_EQUAL(caret)

#define CHECK_ABELIAN_MONOID_PERMUTED_WRT(op_tag)       check_abelian_monoid_permuted_wrt_##op_tag
#define CHECK_ABELIAN_MONOID_PERMUTED_WRT_EQUAL(op_tag) check_abelian_monoid_permuted_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian modoid with partial inversion permuted
//------------------------------------------------------------------------------

#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_partial_invertive_monoid_permuted_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
void check_partial_invertive_monoid_permuted_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_PARTIAL_INVERTIVE_MONOID_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT      (plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT_EQUAL(plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT      (pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT_EQUAL(pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT      (et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT_EQUAL(et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT      (caret)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_PERMUTED_WRT_EQUAL(caret)

#define CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT(op_tag)       check_partial_invertive_monoid_permuted_wrt_##op_tag
#define CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT_EQUAL(op_tag) check_partial_invertive_monoid_permuted_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian group permuted
//------------------------------------------------------------------------------

#define DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
    void check_abelian_group_permuted_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_ABELIAN_GROUP_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_ABELIAN_GROUP_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_ABELIAN_GROUP_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_ABELIAN_GROUP_WRT_EQUAL(op_tag)(equal, icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

#define DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT(op_tag) \
template<class TypeA, class TypeB, class Assoc> \
    void check_abelian_group_permuted_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const Assoc& assoc) \
{ \
    CHECK_ABELIAN_GROUP_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, var_b, assoc);\
    CHECK_ABELIAN_GROUP_WRT(op_tag)(icl::identity_element<TypeA>::value(), var_a, assoc, var_b);\
    CHECK_ABELIAN_GROUP_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, var_a, assoc);\
    CHECK_ABELIAN_GROUP_WRT(op_tag)(icl::identity_element<TypeB>::value(), var_b, assoc, var_a);\
}

DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT      (plus)
DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT_EQUAL(plus)
DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT      (pipe)
DEFINE_ABELIAN_GROUP_CHECK_PERMUTED_WRT_EQUAL(pipe)

#define CHECK_ABELIAN_GROUP_PERMUTED_WRT(op_tag)       check_abelian_group_permuted_wrt_##op_tag
#define CHECK_ABELIAN_GROUP_PERMUTED_WRT_EQUAL(op_tag) check_abelian_group_permuted_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Modoid instance
//------------------------------------------------------------------------------

#define DEFINE_MONOID_CHECK_INSTANCE_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_monoid_instance_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, var_c);\
    CHECK_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_a);\
    CHECK_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_b);\
}

#define DEFINE_MONOID_CHECK_INSTANCE_WRT(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_monoid_instance_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, var_c);\
    CHECK_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_a);\
    CHECK_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_b);\
}

DEFINE_MONOID_CHECK_INSTANCE_WRT      (plus)
DEFINE_MONOID_CHECK_INSTANCE_WRT_EQUAL(plus)
DEFINE_MONOID_CHECK_INSTANCE_WRT      (pipe)
DEFINE_MONOID_CHECK_INSTANCE_WRT_EQUAL(pipe)
DEFINE_MONOID_CHECK_INSTANCE_WRT      (et)
DEFINE_MONOID_CHECK_INSTANCE_WRT_EQUAL(et)
DEFINE_MONOID_CHECK_INSTANCE_WRT      (caret)
DEFINE_MONOID_CHECK_INSTANCE_WRT_EQUAL(caret)

#define CHECK_MONOID_INSTANCE_WRT(op_tag)       check_monoid_instance_wrt_##op_tag
#define CHECK_MONOID_INSTANCE_WRT_EQUAL(op_tag) check_monoid_instance_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian modoid instance
//------------------------------------------------------------------------------

#define DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_abelian_monoid_instance_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_ABELIAN_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, var_c);\
    CHECK_ABELIAN_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_a);\
    CHECK_ABELIAN_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_b);\
}

#define DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_abelian_monoid_instance_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_ABELIAN_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, var_c);\
    CHECK_ABELIAN_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_a);\
    CHECK_ABELIAN_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_b);\
}

DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT      (plus)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT_EQUAL(plus)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT      (pipe)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT_EQUAL(pipe)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT      (et)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT_EQUAL(et)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT      (caret)
DEFINE_ABELIAN_MONOID_CHECK_INSTANCE_WRT_EQUAL(caret)

#define CHECK_ABELIAN_MONOID_INSTANCE_WRT(op_tag)       check_abelian_monoid_instance_wrt_##op_tag
#define CHECK_ABELIAN_MONOID_INSTANCE_WRT_EQUAL(op_tag) check_abelian_monoid_instance_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian partial invertive modoid instance
//------------------------------------------------------------------------------

#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_partial_invertive_monoid_instance_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, var_c);\
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_a);\
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_b);\
}

#define DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
void check_partial_invertive_monoid_instance_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, var_c);\
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_a);\
    CHECK_PARTIAL_INVERTIVE_MONOID_PERMUTED_WRT(op_tag)(var_a, var_b, ass_b);\
}

DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT      (plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT_EQUAL(plus)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT      (pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT_EQUAL(pipe)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT      (et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT_EQUAL(et)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT      (caret)
DEFINE_PARTIAL_INVERTIVE_MONOID_CHECK_INSTANCE_WRT_EQUAL(caret)

#define CHECK_PARTIAL_INVERTIVE_MONOID_INSTANCE_WRT(op_tag)       check_partial_invertive_monoid_instance_wrt_##op_tag
#define CHECK_PARTIAL_INVERTIVE_MONOID_INSTANCE_WRT_EQUAL(op_tag) check_partial_invertive_monoid_instance_wrt_equal_##op_tag

//------------------------------------------------------------------------------
// Abelian group instance
//------------------------------------------------------------------------------

#define DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT_EQUAL(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
    void check_abelian_group_instance_wrt_equal_##op_tag \
(typename equality<TypeA>::type* equal, const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_ABELIAN_GROUP_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, var_c);\
    CHECK_ABELIAN_GROUP_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_a);\
    CHECK_ABELIAN_GROUP_PERMUTED_WRT_EQUAL(op_tag)(equal, var_a, var_b, ass_b);\
}

#define DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT(op_tag) \
template<class TypeA, class TypeB, class TypeC, class AssocA, class AssocB> \
    void check_abelian_group_instance_wrt_##op_tag \
(const TypeA& var_a, const TypeB& var_b, const TypeC& var_c, \
 const AssocA& ass_a, const AssocB& ass_b) \
{ \
    CHECK_ABELIAN_GROUP_PERMUTED_WRT(op_tag)(var_a, var_b, var_c);\
    CHECK_ABELIAN_GROUP_PERMUTED_WRT(op_tag)(var_a, var_b, ass_a);\
    CHECK_ABELIAN_GROUP_PERMUTED_WRT(op_tag)(var_a, var_b, ass_b);\
}

DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT      (plus)
DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT_EQUAL(plus)
DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT      (pipe)
DEFINE_ABELIAN_GROUP_CHECK_INSTANCE_WRT_EQUAL(pipe)

#define CHECK_ABELIAN_GROUP_INSTANCE_WRT(op_tag)       check_abelian_group_instance_wrt_##op_tag
#define CHECK_ABELIAN_GROUP_INSTANCE_WRT_EQUAL(op_tag) check_abelian_group_instance_wrt_equal_##op_tag

}} // namespace icl boost

#endif // BOOST_LIBS_ICL_TEST_ICL_LAWS_HPP_JOFA_090119


//---------------------------------------------------------------------------//
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
//
// 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://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestSetDifference
#include <boost/test/unit_test.hpp>

#include <boost/compute/command_queue.hpp>
#include <boost/compute/algorithm/set_difference.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/types/builtin.hpp>

#include "check_macros.hpp"
#include "context_setup.hpp"

namespace bc = boost::compute;

BOOST_AUTO_TEST_CASE(set_difference_int)
{
    int dataset1[] = {1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 6, 10};
    bc::vector<bc::int_> set1(dataset1, dataset1 + 12, queue);

    int dataset2[] = {0, 2, 2, 4, 5, 6, 8, 8, 9, 9, 9, 13};
    bc::vector<bc::int_> set2(dataset2, dataset2 + 12, queue);

    bc::vector<bc::uint_>result(7, queue.get_context());

    bc::vector<bc::uint_>::iterator iter =
        bc::set_difference(set1.begin(), set1.begin() + 12,
                             set2.begin(), set2.begin() + 12,
                             result.begin(), queue);

    CHECK_RANGE_EQUAL(int, 7, result, (1, 1, 2, 2, 3, 3, 10));
    BOOST_VERIFY(iter == result.begin()+7);
}

BOOST_AUTO_TEST_CASE(set_difference_string)
{
    char string1[] = "abcccdddeeff";
    bc::vector<bc::char_> set1(string1, string1 + 12, queue);

    char string2[] = "bccdfgh";
    bc::vector<bc::char_> set2(string2, string2 + 7, queue);

    bc::vector<bc::char_>result(7, queue.get_context());

    bc::vector<bc::char_>::iterator iter =
        bc::set_difference(set1.begin(), set1.begin() + 12,
                             set2.begin(), set2.begin() + 7,
                             result.begin(), queue);

    CHECK_RANGE_EQUAL(char, 7, result, ('a', 'c', 'd', 'd', 'e', 'e', 'f'));
    BOOST_VERIFY(iter == result.begin()+7);
}

BOOST_AUTO_TEST_SUITE_END()


#include "dijkstra.hpp"
#include <boost/assert.hpp>
#include <limits>

sssp::node_map<sssp::dijkstra_result>
sssp::dijkstra(const graph& graph, size_t start_node, boost::base_collection<criteria>& criteria) {
    node_map<dijkstra_result> info = graph.make_node_map([](size_t i) { return dijkstra_result(); });
    int current_phase = 0;
    size_t fringe_size = 0;

    // Put the start node in the fringe set.
    info[start_node].distance = 0.0;
    for (auto& crit : criteria) {
        crit.changed_predecessor(start_node, size_t(-1), 0.0);
    }
    fringe_size += 1;

    criteria::todo_output todo;
    todo.reserve(graph.node_count());
    while (true) {
        // Find nodes to be relaxed.
        todo.clear();
        for (auto& crit : criteria) {
            crit.relaxable_nodes(todo);
        }

        // If there is nothing more to do, we are done.
        if (todo.empty()) {
            break;
        }

        // First all todo nodes will be set to settled. This exposes some errors
        // and simulates the parallel relaxation better. If this does not happen
        // now but later in the loop, the arbritary relaxation order might hide
        // errors.
        for (size_t node : todo) {
            info[node].relaxation_phase = current_phase;
            info[node].fringe_size = fringe_size;
            fringe_size -= 1;
            BOOST_ASSERT(fringe_size != size_t(-1));
        }

        // Relax each node.
        for (size_t node : todo) {
            dijkstra_result& current_node = info[node];
            BOOST_ASSERT(current_node.settled()); // set above

            for (const edge_info& edge : graph.outgoing_edges(node)) {
                dijkstra_result& destination_node = info[edge.destination];
                if (!destination_node.settled() && current_node.distance + edge.cost < destination_node.distance) {
                    if (destination_node.unexplored()) {
                        fringe_size += 1;
                        BOOST_ASSERT(fringe_size <= graph.node_count());
                    }
                    destination_node.distance = current_node.distance + edge.cost;
                    destination_node.predecessor = node;
                    for (auto& crit : criteria) {
                        crit.changed_predecessor(edge.destination, node, destination_node.distance);
                    }
                }
            }

            for (auto& crit : criteria) {
                crit.relaxed_node(node);
            }
        }

        // Next round.
        ++current_phase;
    }

    BOOST_ASSERT(fringe_size == 0);
    return info;
}


#include <boost/test/unit_test.hpp>

#include <sway/math.h>

using namespace sway;

BOOST_AUTO_TEST_SUITE(TPointTestSuite)

/*!
 * \brief
 *    Убеждаемся, что конструктор по умолчанию приводит все компоненты к нулю.
 */
BOOST_AUTO_TEST_CASE(TPointTestCase_DefaultConstructor) {
	const math::point2i_t point;

	BOOST_CHECK_EQUAL(point.getX(), 0);
	BOOST_CHECK_EQUAL(point.getY(), 0);
}

/*!
 * \brief
 *    Убеждаемся, что конструктор устанавливает все значения компонентов в те,
 *    которые были заданы.
 */
BOOST_AUTO_TEST_CASE(TPointTestCase_ComponentConstructor) {
	const s32_t x = 1, y = 2;
	const math::point2i_t point(x, y);

	BOOST_CHECK_EQUAL(point.getX(), x);
	BOOST_CHECK_EQUAL(point.getY(), y);
}

BOOST_AUTO_TEST_CASE(TPointTestCase_ScalarConstructor) {
	const math::point2i_t size(math::point2i_one);

	BOOST_CHECK_EQUAL(size.getX(), 1);
	BOOST_CHECK_EQUAL(size.getY(), 1);
}

/*!
 * \brief
 *    Тест для оператора равенства.
 */
BOOST_AUTO_TEST_CASE(TPointTestCase_EqualityOperator) {
	const math::point2i_t point(0, 0);

	BOOST_CHECK_EQUAL(point == math::point2i_zero, true);
	BOOST_CHECK_EQUAL(point == math::point2i_one, false);
}

/*!
 * \brief
 *    Тест для оператора неравенства.
 */
BOOST_AUTO_TEST_CASE(TPointTestCase_NonEqualityOperator) {
	const math::point2i_t point(1, 1);

	BOOST_CHECK_EQUAL(point != math::point2i_zero, true);
	BOOST_CHECK_EQUAL(point != math::point2i_one, false);
}

BOOST_AUTO_TEST_SUITE_END()


#ifndef INCLUDE_MIMKL_DEFINITIONS_HPP_
#define INCLUDE_MIMKL_DEFINITIONS_HPP_

#include <Eigen/Core>
#include <Eigen/SparseCore>
#include <map>

#define MATRIX(T) Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>
#define ROW(T) Eigen::Matrix<T, 1, Eigen::Dynamic>
#define COLUMN(T) Eigen::Matrix<T, Eigen::Dynamic, 1>

namespace mimkl
{
namespace definitions
{

typedef unsigned int Index;
typedef std::multimap<std::string, Index> Indexing;
typedef std::map<Index, std::string> ReversedIndexing;
typedef MATRIX(double) Matrix;
typedef Eigen::Ref<const MATRIX(double)> RefConstMatrix;
typedef std::function<Matrix(const Matrix &, const Matrix &)> Kernel;
typedef COLUMN(double) Column;
typedef ROW(double) Row;
typedef Eigen::SparseMatrix<double> SparseMatrix;

} // namespace definitions
} // namespace mimkl

#endif /* INCLUDE_MIMKL_DEFINITIONS_HPP_ */


#define DEBUG 1
/**
 * File    : F.cpp
 * Author  : Kazune Takahashi
 * Created : 5/17/2020, 11:21:17 PM
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/rational.hpp>
#include <boost/multiprecision/cpp_int.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::multiprecision::cpp_int;
using ll = long long;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
ll MOD;
// constexpr ll MOD{998244353LL}; // be careful
constexpr ll MAX_SIZE{3000010LL};
// constexpr ll MAX_SIZE{30000010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
void ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
  }
}
template <typename T>
void ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
  }
}
// ----- Mint -----
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(const Mint &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(const Mint &a) { return *this += -a; }
  Mint &operator++() { return *this += 1; }
  Mint operator++(int)
  {
    Mint tmp{*this};
    ++*this;
    return tmp;
  }
  Mint &operator--() { return *this -= 1; }
  Mint operator--(int)
  {
    Mint tmp{*this};
    --*this;
    return tmp;
  }
  Mint &operator*=(const Mint &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(const Mint &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(const Mint &a) const { return Mint(*this) += a; }
  Mint operator-(const Mint &a) const { return Mint(*this) -= a; }
  Mint operator*(const Mint &a) const { return Mint(*this) *= a; }
  Mint operator/(const Mint &a) const { return Mint(*this) /= a; }
  bool operator<(const Mint &a) const { return x < a.x; }
  bool operator<=(const Mint &a) const { return x <= a.x; }
  bool operator>(const Mint &a) const { return x > a.x; }
  bool operator>=(const Mint &a) const { return x >= a.x; }
  bool operator==(const Mint &a) const { return x == a.x; }
  bool operator!=(const Mint &a) const { return !(*this == a); }
  const Mint power(ll N)
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
Mint operator+(ll lhs, const Mint &rhs)
{
  return rhs + lhs;
}
Mint operator-(ll lhs, const Mint &rhs)
{
  return -rhs + lhs;
}
Mint operator*(ll lhs, const Mint &rhs)
{
  return rhs * lhs;
}
Mint operator/(ll lhs, const Mint &rhs)
{
  return Mint{lhs} / rhs;
}
istream &operator>>(istream &stream, Mint &a)
{
  return stream >> a.x;
}
ostream &operator<<(ostream &stream, const Mint &a)
{
  return stream << a.x;
}
// ----- for C++14 -----
using mint = Mint;
template <typename T>
T gcd(T x, T y) { return y ? gcd(y, x % y) : x; }
template <typename T>
T lcm(T x, T y) { return x / gcd(x, y) * y; }
// ----- for C++17 -----
template <typename T>
int popcount(T x) // C++20
{
  int ans{0};
  while (x != 0)
  {
    ans += x & 1;
    x >>= 1;
  }
  return ans;
}
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1000000000000000LL}; // or
// constexpr int infty{1'000'000'010};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}

// ----- Mat and Vec -----

template <typename T = ll>
using Vec = vector<T>;
template <typename T = ll>
using Mat = vector<Vec<T>>;

// operators of Vec / Mat

template <typename T = ll>
Vec<T> operator+(Vec<T> const &A)
{
  return A;
}

template <typename T = ll>
Mat<T> operator+(Mat<T> const &A)
{
  return A;
}

template <typename T = ll>
Vec<T> operator+(Vec<T> const &A, Vec<T> const &B)
{
  assert(A.size() == B.size());
  Vec<T> C(A.size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    C[i] = A[i] + B[i];
  }
  return C;
}

template <typename T = ll>
Mat<T> operator+(Mat<T> const &A, Mat<T> const &B)
{
  assert(A.size() == B.size());
  assert(A[0].size() == B[0].size());
  Mat<T> C(A.size(), Vec<T>(A[0].size()));
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      C[i][j] += A[i][j] + B[i][j];
    }
  }
  return C;
}

template <typename T = ll>
Vec<T> &operator+=(Vec<T> &A, Vec<T> const &B)
{
  assert(A.size() == B.size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    A[i] += B[i];
  }
  return A;
}

template <typename T = ll>
Mat<T> &operator+=(Mat<T> &A, Mat<T> const &B)
{
  assert(A.size() == B.size());
  assert(A[0].size() == B[0].size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      A[i][j] += B[i][j];
    }
  }
  return A;
}

template <typename T = ll>
Vec<T> operator-(Vec<T> const &A)
{
  Vec<T> C(A.size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    C[i] = -A[i];
  }
  return C;
}

template <typename T = ll>
Mat<T> operator-(Mat<T> const &A)
{
  Mat<T> C(A.size(), Vec<T>(A[0].size()));
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      C[i][j] = -A[i][j];
    }
  }
  return C;
}

template <typename T = ll>
Vec<T> operator-(Vec<T> const &A, Vec<T> const &B)
{
  return A + (-B);
}

template <typename T = ll>
Mat<T> operator-(Mat<T> const &A, Mat<T> const &B)
{
  return A + (-B);
}

template <typename T = ll>
Vec<T> &operator-=(Vec<T> &A, Vec<T> const &B)
{
  return A += (-B);
}

template <typename T = ll>
Mat<T> &operator-=(Mat<T> &A, Mat<T> const &B)
{
  return A += (-B);
}

template <typename T = ll>
Vec<T> operator*(T K, Vec<T> const &A)
{
  Vec<T> C(A.size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    C[i] + A[i] * K;
  }
  return C;
}

template <typename T = ll>
Mat<T> operator*(T K, Mat<T> const &A)
{
  Mat<T> C(A.size(), Vec<T>(A[0].size()));
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      C[i][j] + A[i][j] * K;
    }
  }
  return C;
}

template <typename T = ll>
Vec<T> operator*(Vec<T> const &A, T K)
{
  return K * A;
}

template <typename T = ll>
Mat<T> operator*(Mat<T> const &A, T K)
{
  return K * A;
}

template <typename T = ll>
Vec<T> &operator*=(Vec<T> const &A, T K)
{
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    A[i] *= K;
  }
  return A;
}

template <typename T = ll>
Mat<T> operator*=(Mat<T> const &A, T K)
{
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      A[i][j] *= K;
    }
  }
  return A;
}

template <typename T = ll>
Vec<T> operator/(Vec<T> const &A, T K)
{
  return A * (1 / K);
}

template <typename T = ll>
Mat<T> operator/(Mat<T> const &A, T K)
{
  return A * (1 / K);
}

template <typename T = ll>
Vec<T> &operator/=(Vec<T> &A, T K)
{
  return A *= (1 / K);
}

template <typename T = ll>
Mat<T> &operator/=(Mat<T> &A, T K)
{
  return A *= (1 / K);
}

// generator of unit matrix

template <typename T = ll>
Mat<T> unit_matrix(size_t s)
{
  Mat<T> res(s, Vec<T>(s, T{0}));
  for (auto i = size_t{0}; i < s; ++i)
  {
    res[i][i] = T{1};
  }
  return res;
}

// multiply operators

template <typename T = ll>
Mat<T> operator*(Mat<T> const &A, Mat<T> const &B)
{
  assert(A[0].size() == B.size());
  Mat<T> C(A.size(), Vec<T>(B[0].size()));
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < B[0].size(); j++)
    {
      for (auto k = size_t{0}; k < A[0].size(); k++)
      {
        C[i][j] += A[i][k] * B[k][j];
      }
    }
  }
  return C;
}

template <typename T = ll>
Mat<T> &operator*=(Mat<T> &A, Mat<T> const &B)
{
  auto C{A * B};
  return A = C;
}

template <typename T = ll>
Vec<T> operator*(Mat<T> const &A, Vec<T> const &V)
{
  assert(A[0].size() == V.size());
  Vec<T> W(A.size());
  for (auto i = size_t{0}; i < A.size(); i++)
  {
    for (auto j = size_t{0}; j < A[0].size(); j++)
    {
      W[i] += A[i][j] * V[j];
    }
  }
  return W;
}

template <typename T = ll>
Mat<T> power(Mat<T> const &A, ll N)
{
  assert(A.size() == A[0].size());
  if (N == 0)
  {
    return unit_matrix<T>(A.size());
  }
  if ((N & 1) == 1)
  {
    return A * power(A, N - 1);
  }
  auto B{power(A, N / 2)};
  return B * B;
}

// ----- main() -----

ll L, A, B;

int f(ll i)
{
  return to_string(A + B * i).size();
}

int main()
{
  cin >> L >> A >> B >> MOD;
  vector<ll> delta(19);
  delta[0] = 0;
  for (auto i = 1; i <= 18; ++i)
  {
    ll ng{delta[i - 1] - 1};
    ll ok{L};
    while (abs(ok - ng) > 1)
    {
      ll t{(ok + ng) / 2};
      if (f(t) != i)
      {
        ok = t;
      }
      else
      {
        ng = t;
      }
    }
    delta[i] = ok;
  }
  vector<ll> n(18);
  for (auto i = 0; i < 18; ++i)
  {
    n[i] = delta[i + 1] - delta[i];
  }
  vector<Mat<mint>> M(18, Mat<mint>(3, Vec<mint>(3, 0)));
  for (auto i = 0; i < 18; ++i)
  {
    auto &P{M[i]};
    P[0][0] = 1;
    for (auto k = 0; k <= i; ++k)
    {
      P[0][0] *= 10;
    }
    P[0][1] = 1;
    P[1][1] = 1;
    P[1][2] = B;
    P[2][2] = 1;
  }
  Vec<mint> V = {0, A, 1};
  Mat<mint> R{unit_matrix<mint>(3)};
  for (auto i = 0; i < 18; ++i)
  {
    R = power(M[i], n[i]) * R;
  }
  V = R * V;
  cout << V[0] << endl;
}


#include "scheme/nest/pmap/UnitMap.hh"
#include "scheme/nest/pmap/ScaleMap.hh"
#include "scheme/nest/NEST.hh"
#include <gtest/gtest.h>
#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <random>
#include <boost/foreach.hpp>

namespace scheme {
namespace nest {
namespace pmap {

using std::cout;
using std::endl;

TEST(ParamMap,UnitMap_get_neighboring_cells){
	{
		typedef UnitMap<2> MapType;
		MapType umap(1);
		std::vector<size_t> cnb;
		std::back_insert_iterator<std::vector<size_t> > biter(cnb);

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(1.5,0.5), 0, 0.4, biter );
		EXPECT_EQ( cnb.size(), 0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(-0.5,0.5), 0, 0.4, biter );
		EXPECT_EQ( cnb.size(), 0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(-0.39,0.5), 0, 0.4, biter );
		EXPECT_EQ( cnb.size(), 1 );
		EXPECT_EQ( cnb[0], 0 );

		// cout << "size: "<< cnb.size() << endl;
		// BOOST_FOREACH(size_t i,cnb) cout << "val " << i << std::endl;

	}
	{
		typedef UnitMap<2> MapType;
		MapType umap(100);
		std::vector<size_t> cnb;
		std::back_insert_iterator<std::vector<size_t> > biter(cnb);

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(1.5,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0], 1 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(10.5,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0], 10 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(10.3,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 2 );
		ASSERT_EQ( cnb[0],  9 );
		ASSERT_EQ( cnb[1], 10 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(0.3,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0],  0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(0.3,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0],  0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(101.0,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(100.3,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0],  99 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(-0.5,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 0 );

		cnb.clear();
		umap.get_neighboring_cells( MapType::ValueType(-0.3,0.5), 0, 0.4, biter );
		ASSERT_EQ( cnb.size(), 1 );
		ASSERT_EQ( cnb[0], 0 );
	}
}

TEST(ScaleMap,ScaleMap_cellindices){
	util::SimpleArray<1,size_t> bs(3); ScaleMap<1> m1(bs);
	for(size_t i = 0; i < 3; ++i) ASSERT_EQ( i, m1.indices_to_cellindex(m1.cellindex_to_indices(i)) );
	ScaleMap<2> m2(util::SimpleArray<2,size_t>(3,4));
	for(size_t i = 0; i < 3*4; ++i) ASSERT_EQ( i, m2.indices_to_cellindex(m2.cellindex_to_indices(i)) );
	ScaleMap<3> m3(util::SimpleArray<3,size_t>(3,4,8));
	for(size_t i = 0; i < 3*4*8; ++i) ASSERT_EQ( i, m3.indices_to_cellindex(m3.cellindex_to_indices(i)) );
	ScaleMap<4> m4(util::SimpleArray<4,size_t>(3,4,8,17));
	for(size_t i = 0; i < 3*4*8*17; ++i) ASSERT_EQ( i, m4.indices_to_cellindex(m4.cellindex_to_indices(i)) );
}

TEST(ScaleMap,ScaleMap_get_neighboring_cells_244){
	typedef ScaleMap<2> MapType;
	MapType umap(
		MapType::Params(0,0),
		MapType::Params(4,4),
		MapType::Indices(4,4)
	);
	std::vector<size_t> cnb;
	std::back_insert_iterator<std::vector<size_t> > biter(cnb);
	int i;

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,0.5), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,0.5), 0, 0.6, biter );
	ASSERT_EQ( cnb.size(), 6 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 2 );
	ASSERT_EQ( cnb[i++], 4 );
	ASSERT_EQ( cnb[i++], 5 );
	ASSERT_EQ( cnb[i++], 6 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,1.5), 0, 0.6, biter );
	ASSERT_EQ( cnb.size(), 9 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 2 );
	ASSERT_EQ( cnb[i++], 4 );
	ASSERT_EQ( cnb[i++], 5 );
	ASSERT_EQ( cnb[i++], 6 );
	ASSERT_EQ( cnb[i++], 8 );
	ASSERT_EQ( cnb[i++], 9 );
	ASSERT_EQ( cnb[i++], 10 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.5,4.5), 0, 0.6, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 15 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.5,1.5), 0, 0.2, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 6 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.1,1.5), 0, 0.2, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 5 );
	ASSERT_EQ( cnb[i++], 6 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.1,1.9), 0, 0.2, biter );
	ASSERT_EQ( cnb.size(), 4 );
	ASSERT_EQ( cnb[i++], 5 );
	ASSERT_EQ( cnb[i++], 6 );
	ASSERT_EQ( cnb[i++], 9 );
	ASSERT_EQ( cnb[i++], 10 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.4,4.4), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 0 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.4,4.2), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 0 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.2,4.4), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 0 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.2,4.2), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 15 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(4.2,3.2), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 11 );
	ASSERT_EQ( cnb[i++], 15 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(3.2,4.2), 0, 0.3, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 14 );
	ASSERT_EQ( cnb[i++], 15 );

}

TEST(ScaleMap,ScaleMap_get_neighboring_cells_433){
	typedef ScaleMap<4> MapType;
	MapType umap(
		MapType::Params(0,0,0,0),
		MapType::Params(3,3,3,3),
		MapType::Indices(3,3,3,3)
	);
	std::vector<size_t> cnb;
	std::back_insert_iterator<std::vector<size_t> > biter(cnb);
	int i;

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,0.5,0.5,0.5), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.5,0.5,0.5), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,0.5), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,1.3), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 4 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 27 );
	ASSERT_EQ( cnb[i++], 28 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.3,1.8,2.1,1.8), 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 16 );
	ASSERT_EQ( cnb[i++], 40 );
	ASSERT_EQ( cnb[i++], 41 );
	ASSERT_EQ( cnb[i++], 43  );
	ASSERT_EQ( cnb[i++], 44  );
	ASSERT_EQ( cnb[i++], 49 );
	ASSERT_EQ( cnb[i++], 50 );
	ASSERT_EQ( cnb[i++], 52  );
	ASSERT_EQ( cnb[i++], 53  );
	ASSERT_EQ( cnb[i++], 67 );
	ASSERT_EQ( cnb[i++], 68 );
	ASSERT_EQ( cnb[i++], 70  );
	ASSERT_EQ( cnb[i++], 71  );
	ASSERT_EQ( cnb[i++], 76 );
	ASSERT_EQ( cnb[i++], 77 );
	ASSERT_EQ( cnb[i++], 79  );
	ASSERT_EQ( cnb[i++], 80  );

	// cout << "size: "<< cnb.size() << endl;
	// BOOST_FOREACH(size_t i,cnb) cout << "val " << i << " : " << umap.cellindex_to_indices(i).transpose() << std::endl;
}

TEST(ScaleMap,ScaleMap_get_neighboring_cells_433_m1){
	typedef ScaleMap<4> MapType;
	MapType umap(
		MapType::Params(0,0,0,0)-1.0,
		MapType::Params(3,3,3,3)-1.0,
		MapType::Indices(3,3,3,3)
	);
	std::vector<size_t> cnb;
	std::back_insert_iterator<std::vector<size_t> > biter(cnb);
	int i;

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,0.5,0.5,0.5)-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.5,0.5,0.5)-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,0.5)-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,1.3)-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 4 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 27 );
	ASSERT_EQ( cnb[i++], 28 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.3,1.8,2.1,1.8)-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 16 );
	ASSERT_EQ( cnb[i++], 40 );
	ASSERT_EQ( cnb[i++], 41 );
	ASSERT_EQ( cnb[i++], 43  );
	ASSERT_EQ( cnb[i++], 44  );
	ASSERT_EQ( cnb[i++], 49 );
	ASSERT_EQ( cnb[i++], 50 );
	ASSERT_EQ( cnb[i++], 52  );
	ASSERT_EQ( cnb[i++], 53  );
	ASSERT_EQ( cnb[i++], 67 );
	ASSERT_EQ( cnb[i++], 68 );
	ASSERT_EQ( cnb[i++], 70  );
	ASSERT_EQ( cnb[i++], 71  );
	ASSERT_EQ( cnb[i++], 76 );
	ASSERT_EQ( cnb[i++], 77 );
	ASSERT_EQ( cnb[i++], 79  );
	ASSERT_EQ( cnb[i++], 80  );

	// cout << "size: "<< cnb.size() << endl;
	// BOOST_FOREACH(size_t i,cnb) cout << "val " << i << " : " << umap.cellindex_to_indices(i).transpose() << std::endl;
}

TEST(ScaleMap,ScaleMap_get_neighboring_cells_433_o2){
	typedef ScaleMap<4> MapType;
	MapType umap(
		MapType::Params(0,0,0,0)/2.0,
		MapType::Params(3,3,3,3)/2.0,
		MapType::Indices(3,3,3,3)
	);
	std::vector<size_t> cnb;
	std::back_insert_iterator<std::vector<size_t> > biter(cnb);
	int i;

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.5,0.5,0.5,0.5)/2.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.5,0.5,0.5)/2.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,0.5)/2.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,1.3)/2.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 4 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 27 );
	ASSERT_EQ( cnb[i++], 28 );

	cnb.clear(); i = 0;
	umap.get_neighboring_cells( MapType::ValueType(2.3,1.8,2.1,1.8)/2.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 16 );
	ASSERT_EQ( cnb[i++], 40 );
	ASSERT_EQ( cnb[i++], 41 );
	ASSERT_EQ( cnb[i++], 43  );
	ASSERT_EQ( cnb[i++], 44  );
	ASSERT_EQ( cnb[i++], 49 );
	ASSERT_EQ( cnb[i++], 50 );
	ASSERT_EQ( cnb[i++], 52  );
	ASSERT_EQ( cnb[i++], 53  );
	ASSERT_EQ( cnb[i++], 67 );
	ASSERT_EQ( cnb[i++], 68 );
	ASSERT_EQ( cnb[i++], 70  );
	ASSERT_EQ( cnb[i++], 71  );
	ASSERT_EQ( cnb[i++], 76 );
	ASSERT_EQ( cnb[i++], 77 );
	ASSERT_EQ( cnb[i++], 79  );
	ASSERT_EQ( cnb[i++], 80  );

	// cout << "size: "<< cnb.size() << endl;
	// BOOST_FOREACH(size_t i,cnb) cout << "val " << i << " : " << umap.cellindex_to_indices(i).transpose() << std::endl;
}

TEST(ScaleMap,ScaleMap_get_neighboring_cells_433_o5m1){
	typedef ScaleMap<4> MapType;
	MapType smap(
		MapType::Params(0,0,0,0)/5.0-1.0,
		MapType::Params(3,3,3,3)/5.0-1.0,
		MapType::Indices(3,3,3,3)
	);
	std::vector<size_t> cnb;
	std::back_insert_iterator<std::vector<size_t> > biter(cnb);
	int i;

	cnb.clear(); i = 0;
	smap.get_neighboring_cells( MapType::ValueType(1.5,0.5,0.5,0.5)/5.0-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 1 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	smap.get_neighboring_cells( MapType::ValueType(1.3,0.5,0.5,0.5)/5.0-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	smap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,0.5)/5.0-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 2 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );

	cnb.clear(); i = 0;
	smap.get_neighboring_cells( MapType::ValueType(1.3,0.1,0.1,1.3)/5.0-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 4 );
	ASSERT_EQ( cnb[i++], 0 );
	ASSERT_EQ( cnb[i++], 1 );
	ASSERT_EQ( cnb[i++], 27 );
	ASSERT_EQ( cnb[i++], 28 );

	cnb.clear(); i = 0;
	smap.get_neighboring_cells( MapType::ValueType(2.3,1.8,2.1,1.8)/5.0-1.0, 0, 0.4, biter );
	ASSERT_EQ( cnb.size(), 16 );
	ASSERT_EQ( cnb[i++], 40 );
	ASSERT_EQ( cnb[i++], 41 );
	ASSERT_EQ( cnb[i++], 43  );
	ASSERT_EQ( cnb[i++], 44  );
	ASSERT_EQ( cnb[i++], 49 );
	ASSERT_EQ( cnb[i++], 50 );
	ASSERT_EQ( cnb[i++], 52  );
	ASSERT_EQ( cnb[i++], 53  );
	ASSERT_EQ( cnb[i++], 67 );
	ASSERT_EQ( cnb[i++], 68 );
	ASSERT_EQ( cnb[i++], 70  );
	ASSERT_EQ( cnb[i++], 71  );
	ASSERT_EQ( cnb[i++], 76 );
	ASSERT_EQ( cnb[i++], 77 );
	ASSERT_EQ( cnb[i++], 79  );
	ASSERT_EQ( cnb[i++], 80  );

	// cout << "size: "<< cnb.size() << endl;
	// BOOST_FOREACH(size_t i,cnb) cout << "val " << i << " : " << smap.cellindex_to_indices(i).transpose() << std::endl;
}

}
}
}


/*
 * main.cpp
 *
 *  Created on: Oct 14, 2016
 *      Author: wyan
 */

#include "ChebNodal.h"
#include "Ewald.hpp"
#include "regularized_stokeslet.hpp"
#include <FMM/FMMWrapper.hpp>
#include <FMM/cmdparser.hpp>

#include <chrono>
#include <iostream>
#include <random>
#include <vector>

#include <Eigen/Dense>
#include <mpi.h>

constexpr int MAXP = 16;

void configure_parser(cli::Parser &parser) {
    parser.set_optional<int>("P", "periodicity", 0,
                             "0: NONE. 1: PX. 4:PXY. 7:PXYZ. Default 0");
    parser.set_optional<int>("T", "ntarget", 2,
                             "target number in each dimension. default 2");
    parser.set_optional<double>("B", "box", 1.0, "box edge length");
    parser.set_optional<double>("M", "move", 0.0, "box origin shift move");
    parser.set_optional<int>("R", "random", 1,
                             "1 for random points, 0 for regular mesh");
    parser.set_optional<int>("S", "source", 1,
                             "1 for point force, 2 for force dipole, 4 for "
                             "quadrupole, other for same as target.");
    parser.set_optional<double>(
        "E", "regularize", 0,
        "The regularization parameter epsilon. Default to 0, Recommend 1e-4");
}

void distributePts(std::vector<double> &pts, const int dim = 3) {
    int myRank;
    int nProcs;
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    MPI_Comm_size(MPI_COMM_WORLD, &nProcs);
    int ptsGlobalSize;
    if (myRank == 0) {
        ptsGlobalSize = pts.size();
        MPI_Bcast(&ptsGlobalSize, 1, MPI_INT, 0, MPI_COMM_WORLD);
        // std::cout << "rank " << myRank << " global size" << ptsGlobalSize <<
        // std::endl;
    } else {
        ptsGlobalSize = 0;
        MPI_Bcast(&ptsGlobalSize, 1, MPI_INT, 0, MPI_COMM_WORLD);
        // std::cout << "rank " << myRank << " global size" << ptsGlobalSize <<
        // std::endl;
    }

    // bcast to all
    pts.resize(ptsGlobalSize);
    MPI_Bcast(pts.data(), ptsGlobalSize, MPI_DOUBLE, 0, MPI_COMM_WORLD);

    // each take a portion
    const int nPts = ptsGlobalSize / dim;
    // inclusive low
    int indexlow = dim * floor(myRank * nPts / static_cast<double>(nProcs));
    // non-inclusive high
    int indexhigh =
        dim * floor((myRank + 1) * nPts / static_cast<double>(nProcs));
    if (myRank == nProcs - 1) {
        indexhigh = ptsGlobalSize;
    }
    std::vector<double>::const_iterator first = pts.begin() + indexlow;
    std::vector<double>::const_iterator last = pts.begin() + indexhigh;
    std::vector<double> newVec(first, last);
    pts = std::move(newVec);
}

void collectPts(std::vector<double> &pts) {
    int myRank;
    int nProcs;
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    MPI_Comm_size(MPI_COMM_WORLD, &nProcs);
    int ptsLocalSize = pts.size();
    int ptsGlobalSize = 0;

    std::vector<int> recvSize(0);
    std::vector<int> displs(0);
    if (myRank == 0) {
        recvSize.resize(nProcs);
    }
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Gather(&ptsLocalSize, 1, MPI_INT, recvSize.data(), 1, MPI_INT, 0,
               MPI_COMM_WORLD);
    // MPI_Gather(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
    // void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
    // MPI_Comm comm)
    for (auto &p : recvSize) {
        ptsGlobalSize += p;
    }
    // std::cout << "rank " << myRank << " globalSize " << ptsGlobalSize <<
    // std::endl;
    displs.resize(recvSize.size());
    if (displs.size() > 0) {
        displs[0] = 0;
        for (size_t i = 1; i < displs.size(); i++) {
            displs[i] = recvSize[i - 1] + displs[i - 1];
        }
    }

    // int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype
    // sendtype, void *recvbuf, const int recvcounts[], const int displs[],
    // MPI_Datatype recvtype, int root, MPI_Comm comm)
    MPI_Barrier(MPI_COMM_WORLD);

    std::vector<double> ptsRecv(ptsGlobalSize); // size=0 on rank !=0
    // std::cout << "globalSize " << ptsGlobalSize << std::endl;
    if (myRank == 0) {
        MPI_Gatherv(pts.data(), pts.size(), MPI_DOUBLE, ptsRecv.data(),
                    recvSize.data(), displs.data(), MPI_DOUBLE, 0,
                    MPI_COMM_WORLD);
    } else {
        MPI_Gatherv(pts.data(), pts.size(), MPI_DOUBLE, NULL, NULL, NULL,
                    MPI_DOUBLE, 0, MPI_COMM_WORLD);
    }

    pts = std::move(ptsRecv);
}

void calcTrueValueFMM(std::vector<double> &trg_value_true,
                      const std::vector<double> &trg_coord,
                      std::vector<double> &src_value,
                      const std::vector<double> &src_coord, const double box,
                      const double shift, const FMM_Wrapper::PAXIS pset,
                      const bool reg) {
    std::cout << "***************************************************"
              << std::endl;
    std::cout << "Skip O(N^2) true value calculation for large system"
              << std::endl;
    std::cout << "Use FMM p=" << MAXP
              << " as 'true' value                       " << std::endl;
    std::cout << "***************************************************"
              << std::endl;
    FMM_Wrapper myFMM(MAXP, 2000, 0, pset, reg);
    myFMM.FMM_SetBox(shift, shift + box, shift, shift + box, shift,
                     shift + box);
    myFMM.FMM_UpdateTree(src_coord, trg_coord);
    myFMM.FMM_Evaluate(trg_value_true, trg_coord.size() / 3, &src_value);
    return;
}

void calcTrueValueN2(std::vector<double> &trg_value_true,
                     const std::vector<double> &trg_coord,
                     const std::vector<double> &src_value,
                     const std::vector<double> &src_coord, const double box,
                     const double shift, const FMM_Wrapper::PAXIS pset,
                     const bool reg) {

    if (reg) {
        const int n_trg = trg_coord.size() / 3;
        trg_value_true.resize(n_trg * 6);
        if (pset == FMM_Wrapper::PAXIS::NONE) {
            combined_flow(src_coord, src_value, trg_coord, trg_value_true);
        }

    } else {
        // calc Ewald accuracy test
        trg_value_true.resize(trg_coord.size());
#pragma omp parallel for
        for (size_t t = 0; t < trg_coord.size() / 3; t++) {
            Eigen::Vector3d target(trg_coord[3 * t], trg_coord[3 * t + 1],
                                   trg_coord[3 * t + 2]);
            // shift and scale to [0,1)
            target[0] -= shift;
            target[1] -= shift;
            target[2] -= shift;
            target *= (1 / box);

            Eigen::Vector3d targetValue(0, 0, 0);
            for (size_t s = 0; s < src_coord.size() / 3; s++) {
                Eigen::Vector3d source(src_coord[3 * s], src_coord[3 * s + 1],
                                       src_coord[3 * s + 2]);
                Eigen::Vector3d sourceValue(src_value[3 * s],
                                            src_value[3 * s + 1],
                                            src_value[3 * s + 2]);
                // shift and rotate to [0,1)
                source[0] -= shift;
                source[1] -= shift;
                source[2] -= shift;
                source *= (1 / box);

                Eigen::Vector3d rst = target - source;
                Eigen::Matrix3d G;
                if (pset == FMM_Wrapper::PAXIS::PXYZ) {
                    GkernelEwald3D(rst, G, 1.0);
                } else if (pset == FMM_Wrapper::PAXIS::PXY) {
                    GkernelEwald2D(rst, G); // default box = 1
                } else if (pset == FMM_Wrapper::PAXIS::PX) {
                    Gkernel1D(rst, G); // default box =1
                } else if (pset == FMM_Wrapper::PAXIS::NONE) {
                    Gkernel(rst, Eigen::Vector3d(0, 0, 0), G);
                }
                targetValue += (G * sourceValue) / (8 * PI314);
            }
            // scale back
            trg_value_true[3 * t] = targetValue[0] / box;
            trg_value_true[3 * t + 1] = targetValue[1] / box;
            trg_value_true[3 * t + 2] = targetValue[2] / box;
        }
    }
}

void initPts(std::vector<double> &src_coord, std::vector<double> &src_value,
             std::vector<double> &trg_coord, std::vector<double> &trg_value,
             const cli::Parser &parser) {

    // initialize source and target coord and value
    bool randomS = (parser.get<int>("R") > 0 ? true : false);
    const int ntrgEdge = parser.get<int>("T");
    const double box = parser.get<double>("B");
    const double shift = parser.get<double>("M");
    const double reg = std::abs(parser.get<double>("E"));
    FMM_Wrapper::PAXIS pset =
        static_cast<FMM_Wrapper::PAXIS>(parser.get<int>("P"));

    std::random_device rd;
    std::mt19937 gen(rd());

    // initialize target points
    if (randomS == true) {
        std::lognormal_distribution<> d(log(0.2), 0.5);
        for (int i = 0; i < ntrgEdge + 1; i++) {
            for (int j = 0; j < ntrgEdge + 1; j++) {
                for (int k = 0; k < ntrgEdge + 1; k++) {
                    double r01 = fmod(d(gen), 1.0);
                    trg_coord.push_back(r01 * box + shift); // x
                    r01 = fmod(d(gen), 1.0);
                    trg_coord.push_back(r01 * box + shift); // y
                    r01 = fmod(d(gen), 1.0);
                    trg_coord.push_back(r01 * box + shift); // z
                }
            }
        }
    } else {
        const int chebN = ntrgEdge;
        ChebNodal chebData(chebN);
        if (pset != FMM_Wrapper::PAXIS::PXYZ) {
            chebData.points[0] += 0;
            chebData.points.back() -=
                1e-13; // prevent PVFMM crash with point located at the edge
        }
        const int dimension = chebData.points.size();

        std::vector<double> &chebMesh = trg_coord;
        chebMesh.resize(pow(dimension, 3) * 3);
        for (int i = 0; i < dimension; i++) {
            for (int j = 0; j < dimension; j++) {
                for (int k = 0; k < dimension; k++) {
                    chebMesh[3 *
                             (i * dimension * dimension + j * dimension + k)] =
                        (chebData.points[i] + 1) * box / 2 + shift;
                    chebMesh[3 * (i * dimension * dimension + j * dimension +
                                  k) +
                             1] = (chebData.points[j] + 1) * box / 2 + shift;
                    chebMesh[3 * (i * dimension * dimension + j * dimension +
                                  k) +
                             2] = (chebData.points[k] + 1) * box / 2 + shift;
                }
            }
        }
    }
    {
        FILE *pfile = fopen("trgPoints.txt", "w");
        for (size_t i = 0; i < trg_coord.size() / 3; i++) {
            fprintf(pfile, "%f\t%f\t%f\n", trg_coord[3 * i],
                    trg_coord[3 * i + 1], trg_coord[3 * i + 2]);
        }
        fclose(pfile);
    }

    // initialize source points
    int nsource = parser.get<int>("S");
    switch (nsource) {
    case 1: {
        src_coord.push_back(0.7 * box + shift);
        src_coord.push_back(0.6 * box + shift);
        src_coord.push_back(0.4 * box + shift);
    } break;
    case 2: {
        src_coord.push_back(0.7 * box + shift); // 1
        src_coord.push_back(0.6 * box + shift);
        src_coord.push_back(0.5 * box + shift);
        src_coord.push_back(0.2 * box + shift); // 2
        src_coord.push_back(0.8 * box + shift);
        src_coord.push_back(0.7 * box + shift);
    } break;
    case 4: {
        src_coord.push_back(0.1 * box + shift); // 1
        src_coord.push_back(0.1 * box + shift);
        src_coord.push_back(0.1 * box + shift);
        src_coord.push_back(0.2 * box + shift); // 2
        src_coord.push_back(0.2 * box + shift);
        src_coord.push_back(0.2 * box + shift);
        src_coord.push_back(0.3 * box + shift); // 3
        src_coord.push_back(0.3 * box + shift);
        src_coord.push_back(0.3 * box + shift);
        src_coord.push_back(0.4 * box + shift); // 4
        src_coord.push_back(0.4 * box + shift);
        src_coord.push_back(0.4 * box + shift);
    } break;
    default:
        src_coord = trg_coord;
        break;
    }

    // set src_value
    const int n_src = src_coord.size() / 3;
    std::uniform_real_distribution<> uni(-0.5, 0.5);
    const int SDim = reg > 0 ? 7 : 3;
    src_value.resize(SDim * n_src);
    if (reg > 0) {
        for (int i = 0; i < n_src; i++) {
            for (int j = 0; j < SDim - 1; j++)
                src_value[SDim * i + j] = uni(gen);
            src_value[SDim * i + SDim - 1] = reg;
        }
    } else {
        for (int i = 0; i < n_src; i++) {
            for (int j = 0; j < SDim - 1; j++)
                src_value[SDim * i + j] = uni(gen);
        }
    }

    // enforce net charge for 1P and 2P
    if (pset == FMM_Wrapper::PAXIS::PX || pset == FMM_Wrapper::PAXIS::PXY) {
        double fx = 0, fy = 0, fz = 0;
        for (int i = 0; i < n_src; i++) {
            fx += src_value[SDim * i];
            fy += src_value[SDim * i + 1];
            fz += src_value[SDim * i + 2];
        }
        fx /= n_src;
        fy /= n_src;
        fz /= n_src;
        for (int i = 0; i < n_src; i++) {
            src_value[SDim * i] -= fx;
            src_value[SDim * i + 1] -= fy;
            src_value[SDim * i + 2] -= fz;
        }
    }
    {
        FILE *pfile = fopen("srcPoints.txt", "w");
        for (int i = 0; i < n_src; i++) {
            fprintf(pfile, "%f\t%f\t%f, %f\t%f\t%f\n", src_coord[3 * i],
                    src_coord[3 * i + 1], src_coord[3 * i + 2],
                    src_value[SDim * i], src_value[SDim * i + 1],
                    src_value[SDim * i + 2]);
        }
        fclose(pfile);
    }

    return;
}

void testFMM(std::vector<double> &trg_value, std::vector<double> &trg_coord,
             std::vector<double> &src_value, std::vector<double> &src_coord,
             std::vector<double> &trg_value_true, const int p,
             const cli::Parser &parser) {

    const double box = parser.get<double>("B");
    const double shift = parser.get<double>("M");
    const double reg = std::abs(parser.get<double>("E"));
    FMM_Wrapper::PAXIS pset =
        static_cast<FMM_Wrapper::PAXIS>(parser.get<int>("P"));

    int myRank;
    int nProcs;
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    MPI_Comm_size(MPI_COMM_WORLD, &nProcs);
    if (myRank == 0) {
        std::cout << "------------------------------" << std::endl;
        std::cout << "FMM Initializing for order p:" << p
                  << " src points: " << src_coord.size() / 3
                  << " trg points: " << trg_coord.size() / 3 << std::endl;
        std::cout << "MPI Procs: " << nProcs << std::endl;
        std::cout << "omp threads: " << omp_get_max_threads() << std::endl;
    }

    FMM_Wrapper myFMM(p, 2000, 0, pset, (reg > 0));

    distributePts(src_coord);
    distributePts(src_value, myFMM.SDim);
    distributePts(trg_coord);
    distributePts(trg_value, myFMM.TDim);

    std::cout << "Set Box" << std::endl;
    myFMM.FMM_SetBox(shift, shift + box, shift, shift + box, shift,
                     shift + box);

    std::cout << "Update Tree" << std::endl;
    myFMM.FMM_UpdateTree(src_coord, trg_coord);

    // run a 'noisy fmm' first, to check the old data is properly cleared by
    // DataClear().
    std::vector<double> src_noise = src_value;
    std::fill(src_noise.begin(), src_noise.end(), 1.0);
    std::cout << "Run FMM 1" << std::endl;
    myFMM.FMM_Evaluate(trg_value, trg_coord.size() / 3, &src_noise);

    myFMM.FMM_DataClear();
    std::cout << "Run FMM 2" << std::endl;
    myFMM.FMM_Evaluate(trg_value, trg_coord.size() / 3, &src_value);
    std::cout << "Tree Evaluated" << std::endl;

    collectPts(src_coord);
    collectPts(src_value);
    collectPts(trg_coord);
    collectPts(trg_value);
    MPI_Barrier(MPI_COMM_WORLD);

    {
        FILE *pfile;
        const int n_trg = trg_coord.size() / 3;
        const int TDim = myFMM.TDim;

        pfile = fopen("trgValues.txt", "w");
        for (int i = 0; i < n_trg; i++) {
            for (int j = 0; j < TDim; j++) {
                fprintf(pfile, "%.10e\t", trg_value[TDim * i + j]);
            }
            fprintf(pfile, "\n");
        }
        fclose(pfile);

        pfile = fopen("trgTrueValues.txt", "w");
        for (int i = 0; i < n_trg; i++) {
            for (int j = 0; j < TDim; j++) {
                fprintf(pfile, "%.10e\t", trg_value_true[TDim * i + j]);
            }
            fprintf(pfile, "\n");
        }
        fclose(pfile);
    }

    if (myRank == 0) {

        std::cout << "checking error" << std::endl;
        // calc error and max error
        double errorL2 = 0, errorAbs = 0, L2 = 0, errorMaxL2 = 0, maxU = 0;
        double errorMaxRel = 0;
        for (size_t i = 0; i < trg_value_true.size(); i++) {
            double temp = pow(trg_value_true[i] - trg_value[i], 2);
            // std::cout << trg_value[i]-trg_value_true[i] << std::endl;
            // calc error and max error
            //		if (temp >= pow(1e-5, 2)) {
            //			std::cout << "i" << i << "error L2" << temp <<
            // std::endl;
            //		}
            errorL2 += temp;
            errorAbs += sqrt(temp);
            L2 += pow(trg_value_true[i], 2);
            errorMaxL2 = std::max(sqrt(temp), errorMaxL2);
            maxU = std::max(maxU, fabs(trg_value_true[i]));
            errorMaxRel = std::max(sqrt(temp) / trg_value_true[i], errorMaxRel);
        }

        std::cout << std::setprecision(16)
                  << "Max Abs Error L2: " << (errorMaxL2) << std::endl;
        std::cout << std::setprecision(16)
                  << "Ave Abs Error L2: " << errorAbs / trg_value_true.size()
                  << std::endl;
        std::cout << std::setprecision(16)
                  << "Max Rel Error L2: " << (errorMaxRel) << std::endl;
        std::cout << std::setprecision(16)
                  << "RMS Error L2: " << sqrt(errorL2 / trg_value_true.size())
                  << std::endl;
        std::cout << std::setprecision(16)
                  << "Relative Error L2: " << sqrt(errorL2 / L2) << std::endl;
    }
}

int main(int argc, char **argv) {
    Eigen::initParallel();
    Eigen::setNbThreads(1);
    MPI_Init(&argc, &argv);

    cli::Parser parser(argc, argv);
    configure_parser(parser);
    parser.run_and_exit_if_error();

    std::cout << "Running setting: " << std::endl;
    std::cout << "Periodicity: " << parser.get<int>("P") << std::endl;
    std::cout << "nTarget: " << parser.get<int>("T") << std::endl;
    std::cout << "Box: " << parser.get<double>("B") << std::endl;
    std::cout << "Shift: " << parser.get<double>("M") << std::endl;
    std::cout << "Random: " << parser.get<int>("R") << std::endl;
    std::cout << "Source: " << parser.get<int>("S") << std::endl;
    std::cout << "Regularization: " << parser.get<double>("E") << std::endl;

    const double box = parser.get<double>("B");
    const double shift = parser.get<double>("M");
    const double reg = parser.get<double>("E");
    const FMM_Wrapper::PAXIS pset =
        static_cast<FMM_Wrapper::PAXIS>(parser.get<int>("P"));

    std::vector<double> src_coord(0);
    std::vector<double> src_value(0);
    std::vector<double> trg_coord(0);
    std::vector<double> trg_value(0);
    std::vector<double> trg_true(0);
    int myRank = 0;
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    if (myRank == 0) {
        // initialize source and target coord and value
        // calc true value
        initPts(src_coord, src_value, trg_coord, trg_value, parser);
        trg_true.resize(trg_value.size());
        // determine which truevalue routine to call
        int trueN2 = 0;
        if (src_coord.size() * trg_coord.size() < 3 * 1e8) {
            trueN2 = 1;
            calcTrueValueN2(trg_true, trg_coord, src_value, src_coord, box,
                            shift, pset, (reg > 0));
        }
        MPI_Bcast(&trueN2, 1, MPI_INT, 0, MPI_COMM_WORLD);
        if (trueN2 == 0) {
            calcTrueValueFMM(trg_true, trg_coord, src_value, src_coord, box,
                             shift, pset, (reg > 0));
        }
    } else {
        int trueN2 = 0;
        MPI_Bcast(&trueN2, 1, MPI_INT, 0, MPI_COMM_WORLD);
        if (trueN2 == 0) {
            calcTrueValueFMM(trg_true, trg_coord, src_value, src_coord, box,
                             shift, pset, (reg > 0));
        }
    }

    MPI_Barrier(MPI_COMM_WORLD);

    // send to test
    double boxfac = pow(box / 2, 2); // for surface integral, scale the cheb
                                     // weight from [-1,1] to box length
    for (int p = 6; p <= MAXP; p += 2) {
        testFMM(trg_value, trg_coord, src_value, src_coord, trg_true, p,
                parser);
        if (parser.get<int>("R") <= 0 && myRank == 0) {
            const int chebN = parser.get<int>("T");
            ChebNodal chebData(chebN);
            const int dimension = chebData.points.size();

            // chebyshev integration
            // calculate cheb Integrate
            double vx = 0, vy = 0, vz = 0;
            int imax, jmax, kmax;

            // X flux through YZ plane
            imax = 1;
            jmax = dimension;
            kmax = dimension;
            for (int i = 0; i < imax; i++) {
                for (int j = 0; j < jmax; j++) {
                    for (int k = 0; k < kmax; k++) {
                        double vxp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k)] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vyp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               1] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vzp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               2] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        vx += vxp;
                        vy += vyp;
                        vz += vzp;
                    }
                }
            }
            std::cout << "Flux X through YZ: " << vx / chebData.weights[0]
                      << "," << vy / chebData.weights[0] << ","
                      << vz / chebData.weights[0] << std::endl;

            // Y flux through XZ plane
            imax = dimension;
            jmax = 1;
            kmax = dimension;
            vx = 0, vy = 0, vz = 0;
            for (int i = 0; i < imax; i++) {
                for (int j = 0; j < jmax; j++) {
                    for (int k = 0; k < kmax; k++) {
                        double vxp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k)] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vyp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               1] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vzp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               2] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        vx += vxp;
                        vy += vyp;
                        vz += vzp;
                    }
                }
            }
            std::cout << "Flux Y through XZ: " << vx / chebData.weights[0]
                      << "," << vy / chebData.weights[0] << ","
                      << vz / chebData.weights[0] << std::endl;

            // Z flux through YZ plane
            imax = dimension;
            jmax = dimension;
            kmax = 1;
            vx = 0, vy = 0, vz = 0;
            for (int i = 0; i < imax; i++) {
                for (int j = 0; j < jmax; j++) {
                    for (int k = 0; k < kmax; k++) {
                        double vxp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k)] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vyp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               1] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        double vzp = trg_value[3 * (i * dimension * dimension +
                                                    j * dimension + k) +
                                               2] *
                                     chebData.weights[i] * chebData.weights[j] *
                                     chebData.weights[k] * boxfac;
                        vx += vxp;
                        vy += vyp;
                        vz += vzp;
                    }
                }
            }
            std::cout << "Flux Z through XY: " << vx / chebData.weights[0]
                      << "," << vy / chebData.weights[0] << ","
                      << vz / chebData.weights[0] << std::endl;
        }
    }

    MPI_Barrier(MPI_COMM_WORLD);

    MPI_Finalize();

    return 0;
}


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

#ifndef BOOST_HANA_TEST_LAWS_GROUP_HPP
#define BOOST_HANA_TEST_LAWS_GROUP_HPP

#include <boost/hana/assert.hpp>
#include <boost/hana/bool.hpp>
#include <boost/hana/concept/comparable.hpp>
#include <boost/hana/core/when.hpp>
#include <boost/hana/concept/group.hpp>
#include <boost/hana/lazy.hpp>

#include <laws/base.hpp>

namespace boost { namespace hana { namespace test {
    template <typename G, typename = when<true>>
    struct TestGroup : TestGroup<G, laws> {
        using TestGroup<G, laws>::TestGroup;
    };

    template <typename G>
    struct TestGroup<G, laws> {
        template <typename Xs>
        TestGroup(Xs xs) {
            hana::for_each(xs, [](auto x) {
                static_assert(Group<decltype(x)>{}, "");
            });

            foreach2(xs, [](auto x, auto y) {

                // left inverse
                BOOST_HANA_CHECK(hana::equal(
                    hana::plus(x, hana::negate(x)),
                    zero<G>()
                ));

                // right inverse
                BOOST_HANA_CHECK(hana::equal(
                    hana::plus(hana::negate(x), x),
                    zero<G>()
                ));

                // default definition of minus
                BOOST_HANA_CHECK(hana::equal(
                    hana::minus(x, y),
                    hana::plus(x, hana::negate(y))
                ));

                BOOST_HANA_CHECK(hana::equal(
                    hana::minus(y, x),
                    hana::plus(y, hana::negate(x))
                ));

                // default definition of negate
                BOOST_HANA_CHECK(hana::equal(
                    hana::negate(hana::negate(x)),
                    x
                ));
            });
        }
    };

    template <typename C>
    struct TestGroup<C, when<Constant<C>::value>>
        : TestGroup<C, laws>
    {
        template <typename Xs>
        TestGroup(Xs xs) : TestGroup<C, laws>{xs} {
            foreach2(xs, [](auto x, auto y) {

                BOOST_HANA_CHECK(hana::equal(
                    hana::negate(hana::value(x)),
                    hana::value(hana::negate(x))
                ));

                BOOST_HANA_CHECK(hana::equal(
                    hana::minus(hana::value(x), hana::value(y)),
                    hana::value(hana::minus(x, y))
                ));

            });
        }
    };
}}} // end namespace boost::hana::test

#endif // !BOOST_HANA_TEST_LAWS_GROUP_HPP


//------------------------------------------------------------------------------
/// \file HashSet_tests.cpp
/// \date 20201029 12:05
//------------------------------------------------------------------------------
#include "DataStructures/HashSet.h"

#include <boost/test/unit_test.hpp>
#include <memory> // std::make_shared

using DataStructures::HashTables::HashSet::HashSetT;
using DataStructures::HashTables::HashSet::Node;
using DataStructures::HashTables::HashSet::NodeShared;
using std::make_shared;
using std::shared_ptr;

BOOST_AUTO_TEST_SUITE(DataStructures)
BOOST_AUTO_TEST_SUITE(HashSet_tests)

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(HashSetNodeConstructs)
{
  {
    // Dynamically allocate because you don't know the size at compile time. For
    // example std::vector, you don't know the size at runtime, internally it's
    // dynamically allocated.
    // Static allocation is deallocated as soon as the allocating function
    // returns.
    // Returns a pointer to the beginning of the new block of memory.
    Node<int>* root_ptr {new Node<int>{42}};

    Node<int>* left_ptr {new Node<int>{7}};
    Node<int>* right_ptr {new Node<int>{69}};

    root_ptr->left_ = left_ptr;
    root_ptr->right_ = right_ptr;

    BOOST_TEST(root_ptr->left_->value_ == 7);
    BOOST_TEST(root_ptr->right_->value_ == 69);
    BOOST_TEST(root_ptr->left_->left_ == nullptr);
    BOOST_TEST(root_ptr->left_->right_ == nullptr);
    BOOST_TEST(root_ptr->right_->left_ == nullptr);
    BOOST_TEST(root_ptr->right_->right_ == nullptr);

    // Given pointer to memory block previously allocated with new, free up
    // memory.
    delete root_ptr;
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(HashSetNodeSharedConstructsWithMakeShared)
{
  {
    // cf. https://www.acodersjourney.com/top-10-dumb-mistakes-avoid-c-11-smart-pointers/
    // make_shared dynamically allocates for manager and new object at same
    // time. i.e. When you use make_shared, C++ compiler does a single memory
    // allocation big enough to hold both manager object and the new object.
    shared_ptr<NodeShared<int>> root_ptr {make_shared<NodeShared<int>>(42)};

    // Wrong, there are 2 dynamic memory allocations that happen, one for the
    // object itself from new, and then second for the manager object created by
    // the shared_ptr ctor.
    // shared_ptr<Node<int>> root_ptr {new Node<int>{42}};

    shared_ptr<NodeShared<int>> left_ptr {make_shared<NodeShared<int>>(7)};
    shared_ptr<NodeShared<int>> right_ptr {make_shared<NodeShared<int>>(69)};

    root_ptr->left_ = left_ptr;
    root_ptr->right_ = right_ptr;

    BOOST_TEST(root_ptr->value_ == 42);
    BOOST_TEST(root_ptr->left_->value_ == 7);
    BOOST_TEST(root_ptr->right_->value_ == 69);
    BOOST_TEST(root_ptr->left_->left_ == nullptr);
    BOOST_TEST(root_ptr->left_->right_ == nullptr);
    BOOST_TEST(root_ptr->right_->left_ == nullptr);
    BOOST_TEST(root_ptr->right_->right_ == nullptr);
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(HashSetTIsASet)
{
  HashSetT<int>* obj = new HashSetT<int>{};

  obj->add(1);
  obj->add(2);
  BOOST_TEST(obj->contains(1));
  BOOST_TEST(!(obj->contains(3)));
  obj->add(2);
  BOOST_TEST(obj->contains(2));
  obj->remove(2);
  BOOST_TEST(!obj->contains(2));
}

BOOST_AUTO_TEST_SUITE_END() // HashSet_tests
BOOST_AUTO_TEST_SUITE_END() // DataStructures