/*****************************************************************************
 *   GATB : Genome Assembly Tool Box
 *   Copyright (C) 2014  INRIA
 *   Authors: R.Chikhi, G.Rizk, E.Drezen
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/

/** \file Integer.hpp
 *  \date 01/03/2013
 *  \author edrezen
 *  \brief Entry point class for large integer usage
 */
#ifndef _GATB_CORE_TOOLS_MATH_INTEGER_HPP_
#define _GATB_CORE_TOOLS_MATH_INTEGER_HPP_

/********************************************************************************/
#include <gatb/tools/math/LargeInt.hpp>
#include <gatb/system/api/Exception.hpp>

#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/at.hpp>

/********************************************************************************/
namespace gatb  {  namespace core  { namespace tools {  namespace math  {
/********************************************************************************/

/** \brief Class for large integers calculus
 *
 * The IntegerTemplate is implemented as a boost variant, which means that it can act like T1, T2, T3 or T4, etc.. type
 * according to the configuration.
 *
 * All the methods are implemented through a boost variant visitor.
 *
 *  Note that we have 2 possible native implementations (NativeInt64 and NativeInt128)
 *  that rely on native types uint64_t and __uint128_t.
 *
 *  For larger integer, a multi-precision LargeInt is used.
 *
 *  From the user point of view, [s]he has just to include this file and use the Integer
 *  class.
 *
 */
template <typename IntegerList>
class IntegerTemplate
{
private:

    /** We define a transformation of the provided integer list in order to get a LargeInt type list. */
    template<typename T>  struct ToLargeInt  {  typedef LargeInt<((T::value+31)/32)> type;  };
    typedef typename boost::mpl::transform<IntegerList, ToLargeInt<boost::mpl::_> >::type transfolist;

public:

    /** We define a boost variant from this type list. */
    typedef typename boost::make_variant_over<transfolist>::type Type;
    /* so, before, Type was defined as a plain boost::variant like this:
     typedef typename boost::variant<LargeInt<1>, LargeInt<2>, LargeInt<3>, LargeInt<4> > Type;
     I checked: the new make_variant_over has no impact on runtime performance. Probably not surprising, as this code is probably purely compile-time.
     */

    /** Apply a functor with the best template specialization according to the provided kmer size. */
    template <template<size_t> class Functor, typename Parameter>
    static void apply (size_t kmerSize, Parameter params)
    {
        typedef typename boost::mpl::empty<IntegerList>::type empty;

        /** We delegate the execution to the Apply structure, defined with two template specializations
         * that allows recursion. */
        Apply<Functor, Parameter, IntegerList, empty::value>::execute (kmerSize, params);
    }

    /** Constructor. Note that the type (see getType and setType) has to be first initialized
     * otherwise no instance can be created (exception thrown).
     * \param[in] n : value for initialization of the integer.
     */
    IntegerTemplate (int64_t n=0)  {}

    /** Copy constructor. Relies on the copy constructor of boost variant
     * \param[in] t : the object to be used for initialization
     */
    template<typename T>  explicit IntegerTemplate (const T& t) : v (t)  {}

    /** Affectation operator. Relies on the affectation operator of boost variant
     * \param[in] t : object to be copied
     * \return the current object.
     */
    template<typename T>
    IntegerTemplate& operator=(const T& t)
    {
        v = t;
        return *this;
    }

    /** Get the name of the class used by the variant (ie. one of the Ti template class parameters)
     * \return the class name.
     */
    const char*  getName ()         { return boost::apply_visitor (Integer_name(),  *(*this)); }

    /** Get the size of an instance of the class used by the variant  (ie. one of the Ti template class parameters)
     * \return the size of an object (in bits).
     */
    const size_t getSize ()         { return boost::apply_visitor (Integer_size(),  *(*this)); }

    /** Get the HDF5 type for the the class used by the variant  (ie. one of the Ti template class parameters)
     * \param[in] isCompound : tells whether the type is composed or not
     * \return a HDF5 identifier for the type.
     */
    hid_t hdf5 (bool& isCompound)   { return boost::apply_visitor (Integer_hdf5(isCompound),  *(*this)); }

    /** Operator +
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return sum of the two operands.
     */
    inline friend IntegerTemplate   operator+  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_plus(),  *a, *b);  }

    /** Operator -
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return substraction of the two operands.
     */
    inline friend IntegerTemplate   operator-  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_minus(), *a, *b);  }

    /** Operator |
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return 'or' of the two operands.
     */
    inline friend IntegerTemplate   operator|  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_or(),    *a, *b);  }

    /** Operator ^
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return 'xor' of the two operands.
     */
    inline friend IntegerTemplate   operator^  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_xor(),   *a, *b);  }

    /** Operator &
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return 'and' of the two operands.
     */
    inline friend IntegerTemplate   operator&  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_and(),   *a, *b);  }

    /** Operator ~
     * \param[in] a : operand
     * \return negation of the operand
     */
    inline friend IntegerTemplate   operator~  (const IntegerTemplate& a)                    {  Integer_compl v; return  boost::apply_visitor (v, *a);  }

    /** Operator ==
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return equality of the two operands.
     */
    inline friend bool      operator== (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_equals(), *a, *b);  }

    /** Operator !=
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return inequality of the two operands.
     */
    inline friend bool      operator!= (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  ! (*a==*b);  }

    /** Operator <
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return '<' of the two operands.
     */
    inline friend bool      operator<  (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_less(),   *a, *b);  }

    /** Operator <=
     * \param[in] a : first operand
     * \param[in] b : second operand
     * \return '<=' of the two operands.
     */
    inline friend bool      operator<= (const IntegerTemplate& a, const IntegerTemplate& b)  {  return  boost::apply_visitor (Integer_lesseq(), *a, *b);  }

    /** Operator *
     * \param[in] a : first operand
     * \param[in] c : second operand
     * \return multiplication of the two operands.
     */
    inline friend IntegerTemplate   operator*  (const IntegerTemplate& a, const int&       c)  {  return  boost::apply_visitor (Integer_mult(c), *a);  }

    /** Operator /
     * \param[in] a : first operand
     * \param[in] c : second operand
     * \return division of the two operands.
     */
    inline friend IntegerTemplate   operator/  (const IntegerTemplate& a, const u_int32_t& c)  {  return  boost::apply_visitor (Integer_div(c),  *a);  }

    /** Operator %
     * \param[in] a : first operand
     * \param[in] c : second operand
     * \return modulo of the two operands.
     */
    inline friend u_int32_t operator%  (const IntegerTemplate& a, const u_int32_t& c)  {  return  boost::apply_visitor (Integer_mod(c),  *a);  }

    /** Operator >>
     * \param[in] a : first operand
     * \param[in] c : second operand
     * \return right shift of the two operands.
     */
    inline friend IntegerTemplate   operator>> (const IntegerTemplate& a, const int& c)  {  return  boost::apply_visitor (Integer_shiftLeft(c),   *a);  }

    /** Operator <<
     * \param[in] a : first operand
     * \param[in] c : second operand
     * \return left shift of the two operands.
     */
    inline friend IntegerTemplate   operator<< (const IntegerTemplate& a, const int& c)  {  return  boost::apply_visitor (Integer_shiftRight(c),  *a);  }

    /** Operator +=
     * \param[in] a : first operand
     * \return addition and affectation.
     */
    void operator+= (const IntegerTemplate& a)  {  boost::apply_visitor (Integer_plusaffect(),  *(*this), *a);  }

    /** Operator ^=
     * \param[in] a : first operand
     * \return xor and affectation.
     */
    void operator^= (const IntegerTemplate& a)  {  boost::apply_visitor (Integer_xoraffect(),   *(*this), *a);  }

    /** Operator[] access the ith nucleotide in the given integer. For instance a[4] get the 5th nucleotide of
     * a kmer encoded as an Integer object.
     * \param[in] idx : index of the nucleotide to be retrieved
     * \return the nucleotide value as follow: A=0, C=1, T=2 and G=3
     */
    u_int8_t  operator[]  (size_t idx) const   { return  boost::apply_visitor (Integer_value_at(idx), *(*this)); }

    /** Get the reverse complement of a kmer encoded as an IntegerTemplate object. Note that the kmer size must be known.
     * \param[in] a : kmer value to be reversed-complemented
     * \param[in] sizeKmer : size of the kmer
     * \return the reverse complement kmer as a IntegerTemplate value
     */
    friend IntegerTemplate revcomp (const IntegerTemplate& a,  size_t sizeKmer)  {  return  boost::apply_visitor (Integer_revcomp(sizeKmer),  *a);  }

    /** Get a hash value on 64 bits for a given IntegerTemplate object.
     * \param[in] a : the integer value
     * \param[in] seed : some seed value used for the hash computation.
     * \return the hash value on 64 bits.
     */
    friend u_int64_t hash1        (const IntegerTemplate& a,  u_int64_t seed)  {  return  boost::apply_visitor (Integer_hash1(seed),  *a);          }

    /** Get a hash value on 64 bits for a given IntegerTemplate object.
     * \param[in] a : the integer value
     * \return the hash value on 64 bits.
     */
    friend u_int64_t oahash       (const IntegerTemplate& a)                   {  return  boost::apply_visitor (Integer_oahash(), *a);              }

    /** Get a hash value on 64 bits for a given IntegerTemplate object.
     * Note: although we return 64 bits, only the first 16 bits are set
     * \param[in] a : the integer value
     * \param[in] shift : some value used for the hash computation.
     * \return the hash value on 64 bits.
     */
    friend u_int64_t simplehash16 (const IntegerTemplate& a,  int shift)       {  return  boost::apply_visitor (Integer_simplehash16(shift),  *a);  }

    /** Get the lexicographical minimizer of the k-mer, quickly, using bit tricks.
     * minimizers containing 'AA' are forbidden. might not return a valid result sometimes.
     * \param[in] nbminimizers: number of minimizers inside this kmer
     * \param[in] &validResult: whether the returned result should be considered valid (if not, compute minimizer with another approach please)
     * \return the reverse complement kmer as a IntegerTemplate value
     */
    friend u_int32_t fastLexiMinimizer (const IntegerTemplate& a, int _nbMinimizers, bool &validResult) {  return  boost::apply_visitor (Integer_fastLexiMinimizer(_nbMinimizers, validResult),  *a);  }

    /** Get an ASCII string representation of a kmer encoded as a IntegerTemplate object
     * \param[in] sizeKmer : size of the kmer
     * \return the ASCII representation of the kmer.
     */
    std::string toString (size_t sizeKmer) const  {  return boost::apply_visitor (Integer_toString(sizeKmer), *(*this)); }

    /** Output stream operator for the IntegerTemplate class
     * \param[in] s : the output stream to be used.
     * \param[in] a : the object to output
     * \return the modified output stream.
     */
    friend std::ostream & operator<<(std::ostream & s, const IntegerTemplate& a)  {  s << *a;  return s;  }

    /** Get the value of the IntegerTemplate object as a U type, U being one of the T1,T2,T3,T4
     * template class parameters. This method can be seen as a converter from the IntegerTemplate class
     * to a specific U type (given as a template parameter of this method).
     * \return  the converted value as a U type.
     */
    template<typename U>
    const U& get ()  const  {  return * boost::get<U>(&v);  }

private:

    struct Integer_name : public boost::static_visitor<const char*>    {
        template<typename T>  const char* operator() (const T& a) const { return a.getName();  }};

    struct Integer_size : public boost::static_visitor<const size_t>    {
        template<typename T>  const size_t operator() (const T& a) const  { return a.getSize();  }};

    struct Integer_plus : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>              IntegerTemplate operator() (const T& a, const T& b) const  { return IntegerTemplate(a + b);  }
        template<typename T, typename U>  IntegerTemplate operator() (const T& a, const U& b) const  { return IntegerTemplate();       }
    };

    struct Integer_minus : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>              IntegerTemplate operator() (const T& a, const T& b) const  { return IntegerTemplate(a - b);  }
        template<typename T, typename U>  IntegerTemplate operator() (const T& a, const U& b) const  { return IntegerTemplate();  }
    };

    struct Integer_or : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>              IntegerTemplate operator() (const T& a, const T& b) const  { return IntegerTemplate(a | b);  }
        template<typename T, typename U>  IntegerTemplate operator() (const T& a, const U& b) const  { return IntegerTemplate();  }
    };

    struct Integer_xor : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>              IntegerTemplate operator() (const T& a, const T& b) const  { return IntegerTemplate(a ^ b);  }
        template<typename T, typename U>  IntegerTemplate operator() (const T& a, const U& b) const  { return IntegerTemplate();  }
    };

    struct Integer_and : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>              IntegerTemplate operator() (const T& a, const T& b) const  { return IntegerTemplate(a & b);  }
        template<typename T, typename U>  IntegerTemplate operator() (const T& a, const U& b) const  { return IntegerTemplate();  }
    };

    struct Integer_less : public boost::static_visitor<bool>    {
        template<typename T>              bool operator() (const T& a, const T& b) const  { return a < b;  }
        template<typename T, typename U>  bool operator() (const T& a, const U& b) const  { return false;  }
    };

    struct Integer_lesseq : public boost::static_visitor<bool>    {
        template<typename T>              bool operator() (const T& a, const T& b) const  { return a <= b;  }
        template<typename T, typename U>  bool operator() (const T& a, const U& b) const  { return false;   }
    };

    struct Integer_equals : public boost::static_visitor<bool>    {
        template<typename T>              bool operator() (const T& a, const T& b) const  { return a == b;  }
        template<typename T, typename U>  bool operator() (const T& a, const U& b) const  { return false;   }
    };

    struct Integer_plusaffect : public boost::static_visitor<>    {
        template<typename T>              void operator() ( T& a, const T& b) const  { a += b;  }
        template<typename T, typename U>  void operator() ( T& a, const U& b) const  {   }
    };

    struct Integer_xoraffect : public boost::static_visitor<>    {
        template<typename T>              void operator() ( T& a, const T& b) const  { a ^= b;  }
        template<typename T, typename U>  void operator() ( T& a, const U& b) const  {   }
    };

    struct Integer_compl : public boost::static_visitor<IntegerTemplate>    {
        template<typename T>  IntegerTemplate operator() (const T& a)  { return IntegerTemplate(~a);  }};

    template<typename Result, typename Arg>
    struct Visitor : public boost::static_visitor<Result>
    {
        Visitor (Arg a=Arg()) : arg(a) {}
        Arg arg;
    };

    template<typename Result, typename Arg1, typename Arg2>
    struct Visitor2Args : public boost::static_visitor<Result>
    {
        Visitor2Args (Arg1 a1=Arg1(), Arg2 a2=Arg2()) : arg1(a1), arg2(a2) {}
        Arg1 arg1;
        Arg2 arg2;
    };

    struct Integer_hdf5 : public Visitor<hid_t,bool&>   {
        Integer_hdf5 (bool& c) : Visitor<IntegerTemplate,bool&>(c) {}
        template<typename T>  hid_t operator() (const T& a)  { return a.hdf5 (this->arg);  }};

    struct Integer_mult : public Visitor<IntegerTemplate,const int>    {
        Integer_mult (const int& c) : Visitor<IntegerTemplate,const int>(c) {}
        template<typename T>  IntegerTemplate operator() (const T& a) const  { return IntegerTemplate(a*this->arg);  }};

    struct Integer_div : public Visitor<IntegerTemplate,const u_int32_t>    {
        Integer_div (const u_int32_t& c) : Visitor<IntegerTemplate,const u_int32_t>(c) {}
        template<typename T>  IntegerTemplate operator() (const T& a) const  { return IntegerTemplate(a/this->arg);  }};

    struct Integer_mod : public Visitor<u_int32_t,const u_int32_t>    {
        Integer_mod (const u_int32_t& c) : Visitor<u_int32_t,const u_int32_t>(c) {}
        template<typename T>  u_int32_t operator() (const T& a) const  { return (a%this->arg);  }};

    struct Integer_shiftLeft : public Visitor<IntegerTemplate,const int>    {
        Integer_shiftLeft (const int& c) : Visitor<IntegerTemplate,const int>(c) {}
        template<typename T>  IntegerTemplate operator() (const T& a) const  { return IntegerTemplate (a >> this->arg);  }};

    struct Integer_shiftRight : public Visitor<IntegerTemplate,const int>    {
        Integer_shiftRight (const int& c) : Visitor<IntegerTemplate,const int>(c) {}
        template<typename T>  IntegerTemplate operator() (const T& a) const  { return IntegerTemplate (a << this->arg);  }};

    struct Integer_revcomp : public Visitor<IntegerTemplate,size_t>    {
        Integer_revcomp (const size_t& c) : Visitor<IntegerTemplate,size_t>(c) {}
        template<typename T>  IntegerTemplate operator() (const T& a) const  { return IntegerTemplate (revcomp(a,this->arg));  }};

    struct Integer_hash1 : public Visitor<u_int64_t,u_int64_t>    {
        Integer_hash1 (const u_int64_t& c) : Visitor<u_int64_t,u_int64_t>(c) {}
        template<typename T>  u_int64_t operator() (const T& a) const  { return (hash1(a,this->arg));  }};

    struct Integer_oahash : public boost::static_visitor<u_int64_t>    {
        template<typename T>  u_int64_t operator() (const T& a) const  { return (oahash(a));  }};

    struct Integer_simplehash16 : public Visitor<u_int64_t,int>    {
        Integer_simplehash16 (const int& c) : Visitor<u_int64_t,int>(c) {}
        template<typename T>  u_int64_t operator() (const T& a) const  { return (simplehash16(a,this->arg));  }};

    struct Integer_fastLexiMinimizer : public Visitor2Args<u_int32_t, int, bool&>    {
        Integer_fastLexiMinimizer (int b, bool& c) : Visitor2Args<u_int32_t,int, bool&>(b,c) {}
        template<typename T>  u_int32_t operator() (const T& a) const  { return (fastLexiMinimizer (a,this->arg1, this->arg2));  }};

    struct Integer_value_at : public Visitor<u_int8_t,size_t>   {
        Integer_value_at (size_t idx) : Visitor<u_int8_t,size_t>(idx) {}
        template<typename T>  u_int8_t operator() (const T& a) const { return a[this->arg];  }};

    struct Integer_toString : public Visitor<std::string,size_t>   {
        Integer_toString (size_t c) : Visitor<std::string,size_t>(c) {}
        template<typename T>  std::string operator() (const T& a) const  { return a.toString(this->arg);  }};

private:

    /** We instantiate the boost variant. */
    Type v;

          Type& operator *()       { return v; }
    const Type& operator *() const { return v; }

    /** Now, we define the Apply structure that allows to find the correct implementation of LargeInt
     * according to the given kmerSize (at runtime). */

    /** My initial guess didn't work, although it should have (pb with boost::mpl ?)... I went back on an implementation
     * similar to http://www.developpez.net/forums/d1193120/c-cpp/cpp/bibliotheques/boost/vecteur-vide-boost-mpl */

    /** Template definition. */
    template<template<size_t> class Functor, class Parameter, class T, bool empty> struct Apply  {};

    /** Template specialization for a vector of types. */
    template<template<size_t> class Functor, class Parameter, class T>
    struct Apply<Functor, Parameter, T, false>
    {
        static void execute (size_t kmerSize, Parameter params)
        {
            /** Shortcut : we get the current kmer size threshold. */
            static const size_t K = boost::mpl::front<T>::type::value;

            /** We check whether the kmerSize parameter falls into the current K threshold.
             * If yes, we run the functor and leave. */
            if (kmerSize < K)  { Functor<K>() (params);  return; }

            typedef typename boost::mpl::pop_front<T>::type tail;
            typedef typename boost::mpl::empty<tail>::type  empty;

            /** The current K threshold doesn't work, try the next kmer value. */
            Apply<Functor, Parameter, tail, empty::value>::execute (kmerSize, params);
        }
    };

    /** Template specialization for an empty type. */
    template<template<size_t> class Functor, class Parameter, class T>
    struct Apply<Functor, Parameter, T, true>
    {
        static void execute (size_t kmerSize, Parameter params)
        {
            throw system::Exception ("Failure because of unhandled kmer size %d", kmerSize);
        }
    };
};

// this is just for benchmarking. It helped me see that indeed, instantiating a class that constains a boost::variant takes significant run-time :(
template <typename IntegerList>
class IntegerTemplateDummy
{
public:

    typedef typename boost::variant<LargeInt<1>, LargeInt<2>, LargeInt<3>, LargeInt<4> > Type;

private:
    /** We instantiate the boost variant. */
    Type v;
};

/********************************************************************************/

/** We define a mpl::vector holding the int_<K> types, one per kmer size value chosen by the user. */
typedef boost::mpl::vector<KSIZE_LIST_TYPE>::type  IntegerList;

/** We specialize IntegerTemplate class based on the list of kmer size values chosen by the user. */
typedef  IntegerTemplate <IntegerList> Integer;
typedef  IntegerTemplateDummy<IntegerList> IntegerDummy;

/********************************************************************************/
}}}};
/********************************************************************************/

/* would enable an Integer to be hashed in a unordered_map */
/* well, for some reason this didn't work with unordered_map, i still got the compile error:
 /usr/include/c++/6.1.1/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<const gatb::core::tools::math::IntegerTemplate<boost::mpl::vector1<mpl_::int_<32> > > >) (const gatb::core::tools::math::IntegerTemplate<boost::mpl::vector1<mpl_::int_<32> > >&)’ */
/*namespace std {
  template <typename IntegerList>
  struct hash< gatb::core::tools::math::IntegerTemplate<IntegerList>>
  {
    std::size_t operator()(const gatb::core::tools::math::IntegerTemplate<IntegerList>& k) const
    {
        return oahash(k);
    }
  };
};
*/

#endif /* _GATB_CORE_TOOLS_MATH_INTEGER_HPP_ */


//          Copyright Rein Halbersma 2019-2021.
// 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)

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 50

#include <tabula/compass.hpp>           // basic_compass
#include <tabula/games.hpp>             // draughts, stratego
#include <tabula/grids.hpp>             // basic_rectangle, chequered_rectangle
#include <boost/mpl/vector.hpp>         // vector
#include <boost/test/unit_test.hpp>     // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE_TEMPLATE, BOOST_CHECK, BOOST_CHECK_EQUAL

using namespace tabula;

BOOST_AUTO_TEST_SUITE(Compass)

using grid_types = boost::mpl::vector
<       basic_rectangle<1, 1>
,       basic_rectangle<1, 2>
,       basic_rectangle<2, 1>
,       basic_rectangle<2, 2>
,       basic_rectangle<2, 3>
,       basic_rectangle<3, 2>
,       basic_rectangle<2, 4>
,       basic_rectangle<4, 2>
,       basic_rectangle<3, 3>
,       basic_rectangle<3, 5>
,       basic_rectangle<5, 3>
,       chequered_rectangle<1, 1, 0>
,       chequered_rectangle<1, 2, 0>
,       chequered_rectangle<2, 1, 0>
,       chequered_rectangle<2, 2, 0>
,       chequered_rectangle<2, 3, 0>
,       chequered_rectangle<3, 2, 0>
,       chequered_rectangle<2, 4, 0>
,       chequered_rectangle<4, 2, 0>
,       chequered_rectangle<3, 3, 0>
,       chequered_rectangle<3, 5, 0>
,       chequered_rectangle<5, 3, 0>
,       chequered_rectangle<1, 1, 1>
,       chequered_rectangle<1, 2, 1>
,       chequered_rectangle<2, 1, 1>
,       chequered_rectangle<2, 2, 1>
,       chequered_rectangle<2, 3, 1>
,       chequered_rectangle<3, 2, 1>
,       chequered_rectangle<2, 4, 1>
,       chequered_rectangle<4, 2, 1>
,       chequered_rectangle<3, 3, 1>
,       chequered_rectangle<3, 5, 1>
,       chequered_rectangle<5, 3, 1>
>;

BOOST_AUTO_TEST_CASE_TEMPLATE(IsCardinal, T, grid_types)
{
        using compass = basic_compass<T>;
        for (auto p : { compass::N, compass::E, compass::S, compass::W }) {
                BOOST_CHECK(compass::points[p].is_cardinal());
        }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(IsOrdinal, T, grid_types)
{
        using compass = basic_compass<T>;
        for (auto p : { compass::NE, compass::SE, compass::SW, compass::NW }) {
                BOOST_CHECK(compass::points[p].is_ordinal());
        }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(IsReverse, T, grid_types)
{
        using compass = basic_compass<T>;
        for (auto p : compass::points) {
                auto const r = p.reverse();
                BOOST_CHECK((p != r));
                BOOST_CHECK_EQUAL(p.is_left() ,  r.is_right());
                BOOST_CHECK_EQUAL(p.is_right(),  r.is_left() );
                BOOST_CHECK_EQUAL(p.is_up()   ,  r.is_down() );
                BOOST_CHECK_EQUAL(p.is_down() ,  r.is_up()   );
        }
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright (c) 2018-2019 The Llamacoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>

#include <uint256.h>
#include <test/util/setup_common.h>
#include <vector>

#include <boost/test/unit_test.hpp>
#include <rapidcheck/boost_test.h>
#include <rapidcheck/gen/Arbitrary.h>
#include <rapidcheck/Gen.h>

#include <test/gen/crypto_gen.h>

BOOST_FIXTURE_TEST_SUITE(key_properties, BasicTestingSetup)

/** Check CKey uniqueness */
RC_BOOST_PROP(key_uniqueness, (const CKey& key1, const CKey& key2))
{
    RC_ASSERT(!(key1 == key2));
}

/** Verify that a private key generates the correct public key */
RC_BOOST_PROP(key_generates_correct_pubkey, (const CKey& key))
{
    CPubKey pubKey = key.GetPubKey();
    RC_ASSERT(key.VerifyPubKey(pubKey));
}

/** Create a CKey using the 'Set' function must give us the same key */
RC_BOOST_PROP(key_set_symmetry, (const CKey& key))
{
    CKey key1;
    key1.Set(key.begin(), key.end(), key.IsCompressed());
    RC_ASSERT(key1 == key);
}

/** Create a CKey, sign a piece of data, then verify it with the public key */
RC_BOOST_PROP(key_sign_symmetry, (const CKey& key, const uint256& hash))
{
    std::vector<unsigned char> vchSig;
    key.Sign(hash, vchSig, 0);
    const CPubKey& pubKey = key.GetPubKey();
    RC_ASSERT(pubKey.Verify(hash, vchSig));
}
BOOST_AUTO_TEST_SUITE_END()


#include <empiricalcpp/src/interpolation.hpp>
#include <Eigen/Dense>

using empirical::Scalar;
using empirical::Mesh1D;
using namespace empirical::interpolation;

namespace {
    class EigenBasedScalarInterpolator1D : public ScalarInterpolator1D {

        std::vector<const Scalar> solutionPoints;
        std::vector<std::weak_ptr<evaluation_type>> originalBasisFunctions;

    public:
        EigenBasedScalarInterpolator1D(const Mesh1D&) {

        }

        virtual ~EigenBasedScalarInterpolator1D() {}

        void addBasis(evaluation_type&) {

        }

        virtual size_t N() const {
            return solutionPoints.size();
        }

        virtual Scalar getMaximumNorm() const {
            return std::numeric_limits<Scalar>::infinity();
        }

        virtual Scalar getLastCorrectionScale() const {
            return std::numeric_limits<Scalar>::infinity();
        }

        virtual interpolation_function operator()(interpolation_function testFunction) const {
            return testFunction;
        }

        virtual const std::vector<const Scalar>& exactSolutionPoints() const {
            return solutionPoints;
        }

        virtual const std::vector<std::weak_ptr<evaluation_type>>& basisEvaluations() const {
            return originalBasisFunctions;
        }

        virtual Scalar maxError(evaluation_type&) const {
            return std::numeric_limits<Scalar>::infinity();
        }

        virtual void addBasis(std::shared_ptr<evaluation_type>&) {

        }
    };
}

namespace empirical {
    namespace interpolation {

        ScalarInterpolator1D* ScalarInterpolator1D::create(const Mesh1D& points) {
            return new EigenBasedScalarInterpolator1D(points);
        }

#ifndef EMPIRICAL_NO_OSTREAM_DEFINITIONS
        std::ostream& operator<<(std::ostream& os, const ScalarInterpolator1D& interp) {
            os << "ScalarInterpolator1D {";
            os << std::fixed << std::setw(11) << std::setprecision(6);
            os << " N: " << interp.N() << ", ";
            os << " correctionScale: " << interp.getLastCorrectionScale() << ", ";
            os << " maximumNorm: " << interp.getMaximumNorm();
            return os << "}";
        }
#endif
    }
}


/***************************************************************************************************************************
 * swarm_estimator.cpp
 *
 * Author: Qyp
 *
 * Update Time: 2020.10.18
 *
***************************************************************************************************************************/

//头文件
#include <ros/ros.h>

#include <iostream>
#include <Eigen/Eigen>
#include "state_from_mavros.h"
#include "math_utils.h"
#include "prometheus_control_utils.h"
#include "message_utils.h"

using namespace std;
#define TRA_WINDOW 1000
#define TIMEOUT_MAX 0.05
#define NODE_NAME "swarm_estimator"
//---------------------------------------相关参数-----------------------------------------------
int input_source; //0:使用mocap数据作为定位数据 1:使用laser数据作为定位数据
float rate_hz;
Eigen::Vector3f pos_offset;
float yaw_offset;
string uav_name,object_name;
//---------------------------------------vicon定位相关------------------------------------------
Eigen::Vector3d pos_drone_mocap; //无人机当前位置 (vicon)
Eigen::Quaterniond q_mocap;
Eigen::Vector3d Euler_mocap; //无人机当前姿态 (vicon)
//---------------------------------------gazebo真值相关------------------------------------------
Eigen::Vector3d pos_drone_gazebo;
Eigen::Quaterniond q_gazebo;
Eigen::Vector3d Euler_gazebo;
//---------------------------------------发布相关变量--------------------------------------------
ros::Publisher vision_pub;
ros::Publisher drone_state_pub;
ros::Publisher message_pub;
prometheus_msgs::Message message;
ros::Publisher odom_pub;
ros::Publisher trajectory_pub;
prometheus_msgs::DroneState Drone_State;
nav_msgs::Odometry Drone_odom;
std::vector<geometry_msgs::PoseStamped> posehistory_vector_;
ros::Time last_timestamp;
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>函数声明<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void send_to_fcu();
void pub_to_nodes(prometheus_msgs::DroneState State_from_fcu);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>回调函数<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void mocap_cb(const geometry_msgs::PoseStamped::ConstPtr &msg)
{
    //位置 -- optitrack系 到 ENU系
    //Frame convention 0: Z-up -- 1: Y-up (See the configuration in the motive software)
    int optitrack_frame = 0;
    if (optitrack_frame == 0)
    {
        // Read the Drone Position from the Vrpn Package [Frame: Vicon]  (Vicon to ENU frame)
        pos_drone_mocap = Eigen::Vector3d(msg->pose.position.x, msg->pose.position.y, msg->pose.position.z);
        // Read the Quaternion from the Vrpn Package [Frame: Vicon[ENU]]
        q_mocap = Eigen::Quaterniond(msg->pose.orientation.w, msg->pose.orientation.x, msg->pose.orientation.y, msg->pose.orientation.z);
    }
    else
    {
        // Read the Drone Position from the Vrpn Package [Frame: Vicon]  (Vicon to ENU frame)
        pos_drone_mocap = Eigen::Vector3d(-msg->pose.position.x, msg->pose.position.z, msg->pose.position.y);
        // Read the Quaternion from the Vrpn Package [Frame: Vicon[ENU]]
        q_mocap = Eigen::Quaterniond(msg->pose.orientation.w, msg->pose.orientation.x, msg->pose.orientation.z, msg->pose.orientation.y); //Y-up convention, switch the q2 & q3
    }

    // Transform the Quaternion to Euler Angles
    Euler_mocap = quaternion_to_euler(q_mocap);
    last_timestamp = msg->header.stamp;
}

void gazebo_cb(const nav_msgs::Odometry::ConstPtr &msg)
{
    if (msg->header.frame_id == "world")
    {
        pos_drone_gazebo = Eigen::Vector3d(msg->pose.pose.position.x, msg->pose.pose.position.y, msg->pose.pose.position.z);
        // pos_drone_gazebo[0] = msg->pose.pose.position.x + pos_offset[0];
        // pos_drone_gazebo[1] = msg->pose.pose.position.y + pos_offset[1];
        // pos_drone_gazebo[2] = msg->pose.pose.position.z + pos_offset[2];

        q_gazebo = Eigen::Quaterniond(msg->pose.pose.orientation.w, msg->pose.pose.orientation.x, msg->pose.pose.orientation.y, msg->pose.pose.orientation.z);
        Euler_gazebo = quaternion_to_euler(q_gazebo);
        // Euler_gazebo[2] = Euler_gazebo[2] + yaw_offset;
        // q_gazebo = quaternion_from_rpy(Euler_gazebo);
    }
    else
    {
        pub_message(message_pub, prometheus_msgs::Message::NORMAL, NODE_NAME, "wrong gazebo ground truth frame id.");
    }
}

void timerCallback(const ros::TimerEvent &e)
{
    pub_message(message_pub, prometheus_msgs::Message::NORMAL, NODE_NAME, "Program is running.");
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>主 函 数<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int main(int argc, char **argv)
{
    ros::init(argc, argv, "swarm_estimator");
    ros::NodeHandle nh("~");

    //读取参数表中的参数
    nh.param<string>("uav_name", uav_name, "/uav0");
    // 动作捕捉设备中设定的刚体名字
    nh.param<string>("object_name", object_name, "UAV");
    //　程序执行频率
    nh.param<float>("rate_hz", rate_hz, 20);
    // 定位数据输入源 0 for vicon， 1 for 激光SLAM, 2 for gazebo ground truth, 3 for T265
    nh.param<int>("input_source", input_source, 0);
    //　定位设备偏移量
    nh.param<float>("offset_x", pos_offset[0], 0);
    nh.param<float>("offset_y", pos_offset[1], 0);
    nh.param<float>("offset_z", pos_offset[2], 0);
    nh.param<float>("offset_yaw", yaw_offset, 0);

    // 【订阅】optitrack估计位置
    ros::Subscriber optitrack_sub = nh.subscribe<geometry_msgs::PoseStamped>("/vrpn_client_node/"+ object_name + "/pose", 1000, mocap_cb);

    // 【订阅】gazebo仿真真值
    ros::Subscriber gazebo_sub = nh.subscribe<nav_msgs::Odometry>(uav_name + "/prometheus/ground_truth/p300_basic", 100, gazebo_cb);

    // 【发布】无人机位置和偏航角 坐标系 ENU系
    //  本话题要发送飞控(通过mavros_extras/src/plugins/vision_pose_estimate.cpp发送), 对应Mavlink消息为VISION_POSITION_ESTIMATE(#102), 对应的飞控中的uORB消息为vehicle_vision_position.msg 及 vehicle_vision_attitude.msg
    vision_pub = nh.advertise<geometry_msgs::PoseStamped>(uav_name + "/mavros/vision_pose/pose", 100);

    drone_state_pub = nh.advertise<prometheus_msgs::DroneState>(uav_name + "/prometheus/drone_state", 10);

    //【发布】无人机odometry
    odom_pub = nh.advertise<nav_msgs::Odometry>(uav_name + "/prometheus/drone_odom", 10);

    trajectory_pub = nh.advertise<nav_msgs::Path>(uav_name + "/prometheus/drone_trajectory", 10);

    // 【发布】提示消息
    message_pub = nh.advertise<prometheus_msgs::Message>(uav_name + "/prometheus/message/main", 10);

    // 10秒定时打印，以确保程序在正确运行
    ros::Timer timer = nh.createTimer(ros::Duration(10.0), timerCallback);

    // 用于与mavros通讯的类，通过mavros接收来至飞控的消息【飞控->mavros->本程序】
    state_from_mavros _state_from_mavros;

    // 频率
    ros::Rate rate(rate_hz);

    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Main Loop<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (ros::ok())
    {
        //回调一次 更新传感器状态
        ros::spinOnce();

        // 将采集的机载设备的定位信息及偏航角信息发送至飞控，根据参数input_source选择定位信息来源
        send_to_fcu();

        // 发布无人机状态至其他节点，如px4_pos_controller.cpp节点
        pub_to_nodes(_state_from_mavros._DroneState);

        rate.sleep();
    }

    return 0;
}

void send_to_fcu()
{
    geometry_msgs::PoseStamped vision;

    //vicon
    if (input_source == 0)
    {
        vision.pose.position.x = pos_drone_mocap[0];
        vision.pose.position.y = pos_drone_mocap[1];
        vision.pose.position.z = pos_drone_mocap[2];

        vision.pose.orientation.x = q_mocap.x();
        vision.pose.orientation.y = q_mocap.y();
        vision.pose.orientation.z = q_mocap.z();
        vision.pose.orientation.w = q_mocap.w();
        // 此处时间主要用于监测动捕，T265设备是否正常工作
        if( prometheus_control_utils::get_time_in_sec(last_timestamp) > TIMEOUT_MAX)
        {
            pub_message(message_pub, prometheus_msgs::Message::ERROR, NODE_NAME, "Mocap Timeout.");
        }

    }
    else if (input_source == 2)
    {
        vision.pose.position.x = pos_drone_gazebo[0];
        vision.pose.position.y = pos_drone_gazebo[1];
        vision.pose.position.z = pos_drone_gazebo[2];

        vision.pose.orientation.x = q_gazebo.x();
        vision.pose.orientation.y = q_gazebo.y();
        vision.pose.orientation.z = q_gazebo.z();
        vision.pose.orientation.w = q_gazebo.w();
    }
    else
    {
        pub_message(message_pub, prometheus_msgs::Message::NORMAL, NODE_NAME, "Wrong input_source.");
    }

    vision.header.stamp = ros::Time::now();
    vision_pub.publish(vision);
}

void pub_to_nodes(prometheus_msgs::DroneState State_from_fcu)
{
    // 发布无人机状态，具体内容参见 prometheus_msgs::DroneState
    Drone_State = State_from_fcu;
    Drone_State.header.stamp = ros::Time::now();
    drone_state_pub.publish(Drone_State);

    // 发布无人机当前odometry,用于导航及rviz显示
    nav_msgs::Odometry Drone_odom;
    Drone_odom.header.stamp = ros::Time::now();
    Drone_odom.header.frame_id = "world";
    Drone_odom.child_frame_id = "base_link";

    Drone_odom.pose.pose.position.x = Drone_State.position[0];
    Drone_odom.pose.pose.position.y = Drone_State.position[1];
    Drone_odom.pose.pose.position.z = Drone_State.position[2];

    // 导航算法规定 高度不能小于0
    if (Drone_odom.pose.pose.position.z <= 0)
    {
        Drone_odom.pose.pose.position.z = 0.01;
    }

    Drone_odom.pose.pose.orientation = Drone_State.attitude_q;
    Drone_odom.twist.twist.linear.x = Drone_State.velocity[0];
    Drone_odom.twist.twist.linear.y = Drone_State.velocity[1];
    Drone_odom.twist.twist.linear.z = Drone_State.velocity[2];
    odom_pub.publish(Drone_odom);

    // 发布无人机运动轨迹，用于rviz显示
    geometry_msgs::PoseStamped drone_pos;
    drone_pos.header.stamp = ros::Time::now();
    drone_pos.header.frame_id = "world";
    drone_pos.pose.position.x = Drone_State.position[0];
    drone_pos.pose.position.y = Drone_State.position[1];
    drone_pos.pose.position.z = Drone_State.position[2];

    drone_pos.pose.orientation = Drone_State.attitude_q;

    //发布无人机的位姿 和 轨迹 用作rviz中显示
    posehistory_vector_.insert(posehistory_vector_.begin(), drone_pos);
    if (posehistory_vector_.size() > TRA_WINDOW)
    {
        posehistory_vector_.pop_back();
    }

    nav_msgs::Path drone_trajectory;
    drone_trajectory.header.stamp = ros::Time::now();
    drone_trajectory.header.frame_id = "world";
    drone_trajectory.poses = posehistory_vector_;
    trajectory_pub.publish(drone_trajectory);
}


#include "WSPRLog.hxx"

#include <boost/format.hpp>
#include <string>
#include <iostream>
#include <fstream>
#include <list>
#include <math.h>

class myWSPRLog : public WSPRLog {
public:
  myWSPRLog() : WSPRLog() {
    last_time = 0;
  }

  bool processEntry(WSPRLogEntry * ent) {

    if((ent != NULL) && (last_time == 0)) last_time = ent->dtime;

    if((ent != NULL) && (ent->dtime == last_time)) {
      // add entry to tx list
      addEntry(ent);
      return true;
    }
    else {
      // we are done.  Dump the pairs, if any
      dumpPairs();
      clearMap();
      if(ent != NULL) {
	last_time = ent->dtime;
	addEntry(ent);
      }
      return false;
    }

    return false;
  }

  void addToHistogram(int diff) {
    if(histo_map.find(diff) != histo_map.end()) {
      histo_map[diff] += 1;
    }
    else {
      histo_map[diff] = 1;
    }
  }

  void dumpPairs() {
    for(auto & mapent : pair_map) {
      if(mapent.second.size() > 1) {
	mapent.second.sort(WSPRLogEntry::compareSNR);
	double base_freq = mapent.second.front()->freq;
	// two reporters in the same segment
	WSPRLogEntry * fle = mapent.second.front();
	fle->calcDiff(fle);
	std::cout << "\n";

	std::string prefix = (boost::format("tx: %10s %6s time: %ld  dist: %d  pwr: %g ")
			      % fle->txcall % fle->txgrid % fle->dtime % fle->dist % fle->power).str();
	for(auto & le : mapent.second) {
	  le->calcDiff(fle);
	  double fdiff = 0.0;
	  le->getField(WSPRLogEntry::DRIFT, fdiff);
	  addToHistogram((int) floor(fdiff * 1e6 + 0.5));

	  le->print(std::cout);
	}
      }
    }
  }

  void clearMap() {
    // empty maps, and free up records.
    for(auto & mapent : pair_map) {
      while(!pair_map[mapent.first].empty()) {
	delete pair_map[mapent.first].front();
	pair_map[mapent.first].pop_front();
      }
    }
    pair_map.clear();
  }

  void addEntry(WSPRLogEntry * ent) {
    std::string key = ent->txcall + "," + ent->rxcall;
    pair_map[key].push_back(ent);
  }

  void writeHisto(std::ostream & os) {
    int sum = 0;
    for(auto hent : histo_map) {
      if(hent.first != 0) sum += hent.second;
    }

    float fsum = float(sum);
    for(auto hent : histo_map) {
      // there is one ZERO offset report for every image report.
      // don't count it...
      int count = (hent.first == 0) ? 0 : hent.second;
      float fcount = float(count);
      if(hent.first == 0) continue;
      os << boost::format("%d  %g\n") % hent.first % (fcount / fsum);
    }
  }
private:
  unsigned long last_time;
  std::map<std::string, std::list<WSPRLogEntry *> > pair_map;
  std::map<int, int> histo_map;
};

int main(int argc, char * argv[])
{
  std::string fna(argv[1]);
  myWSPRLog wlog;

  std::ifstream inf(fna);
  wlog.readLog(inf);

  std::string hna(argv[2]);
  std::ofstream os(hna);
  wlog.writeHisto(os);
}


/*
 * test_AF_EqualWeight.cpp
 *
 *  Created on: 2018-2-10
 *      Author: fasiondog
 */

#ifdef TEST_ALL_IN_ONE
    #include <boost/test/unit_test.hpp>
#else
    #define BOOST_TEST_MODULE test_hikyuu_trade_sys_suite
    #include <boost/test/unit_test.hpp>
#endif

#include <hikyuu/StockManager.h>
#include <hikyuu/trade_manage/crt/crtTM.h>
#include <hikyuu/trade_sys/system/crt/SYS_Simple.h>
#include <hikyuu/trade_sys/selector/crt/SE_Fixed.h>
#include <hikyuu/trade_sys/allocatefunds/crt/AF_EqualWeight.h>

using namespace hku;

/**
 * @defgroup test_AllocateFunds test_AllocateFunds
 * @ingroup test_hikyuu_trade_sys_suite
 * @{
 */

/** @par 检测点，不自动调仓 */
BOOST_AUTO_TEST_CASE( test_AF_EqualWeight_not_adjust_hold) {
    StockManager& sm = StockManager::instance();

    SEPtr se = SE_Fixed();
    AFPtr af = AF_EqualWeight();
    TMPtr tm = crtTM(Datetime(200101010000L), 100000);
    TMPtr subtm = crtTM(Datetime(200101010000L), 0);
    SYSPtr sys = SYS_Simple(subtm->clone());

    SystemList se_list;
    SystemList hold_list;
    SystemList ac_list;
    SystemWeightList sw_list;

    af->setTM(tm);
    af->setParam<bool>("adjust_hold_sys", false);

    /** @arg 当前选中系统数等于最大持仓系统数，已持仓系统数为0 */
    af->setParam<bool>("adjust_hold_sys", false);
    af->setParam<int>("max_sys_num", 1);
    tm->reset();
    se->clear();
    sys->setTM(subtm->clone());
    se->addStock(sm["sh600000"], sys);
    se_list = se->getSelectedSystemList(Datetime(201802100000L));
    BOOST_CHECK(se_list.size() == 1);
    BOOST_CHECK(hold_list.size() == 0);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 0);

    sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
    ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
    BOOST_CHECK(sw_list.size() == 1);
    BOOST_CHECK(sw_list[0].getSYS()->getStock() == sm["sh600000"]);
    BOOST_CHECK(sw_list[0].getWeight() == 1);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 100000);
    BOOST_CHECK(tm->currentCash() == 0);
    BOOST_CHECK(ac_list.size() == 1);
    BOOST_CHECK(ac_list[0] == sw_list[0].getSYS());

    /** @arg 当前选中系统数大于最大持仓系统数，已持仓系统数为0 */
    af->setParam<int>("max_sys_num", 1);
    tm->reset();
    se->clear();
    sys->setTM(subtm->clone());
    se->addStock(sm["sh600000"], sys);
    se->addStock(sm["sh600004"], sys);
    se_list = se->getSelectedSystemList(Datetime(201802100000L));
    BOOST_CHECK(se_list.size() > af->getParam<int>("max_sys_num"));
    BOOST_CHECK(hold_list.size() == 0);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(se_list[1]->getTM()->currentCash() == 0);
    BOOST_CHECK(tm->currentCash() == 100000);

    sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
    ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
    BOOST_CHECK(sw_list.size() == 2);
    BOOST_CHECK(sw_list[0].getSYS()->getStock() == sm["sh600000"]);
    BOOST_CHECK(sw_list[0].getWeight() == 1);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(tm->currentCash() == 0);
    BOOST_CHECK(ac_list.size() == 1);
    BOOST_CHECK(ac_list[0] == sw_list[1].getSYS());
    BOOST_CHECK(ac_list[0]->getTM()->currentCash() == 100000);

    /** @arg 当前选中系统数不为0，已持仓系统数等于最大持仓数 */
    af->setParam<int>("max_sys_num", 2);
    tm->reset();
    se->clear();
    sys->setTM(subtm->clone());
    se->addStock(sm["sh600000"], sys);
    se_list = se->getSelectedSystemList(Datetime(201802100000L));
    se->clear();
    se->addStock(sm["sh600004"], sys);
    se->addStock(sm["sz000001"], sys);
    hold_list = se->getSelectedSystemList(Datetime(201802100000L));
    BOOST_CHECK(se_list.size() != 0);
    BOOST_CHECK(hold_list.size() == af->getParam<int>("max_sys_num"));
    BOOST_CHECK(tm->currentCash() == 100000);

    sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
    ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
    BOOST_CHECK(sw_list.size() == 1);
    BOOST_CHECK(sw_list[0].getSYS()->getStock() == sm["sh600000"]);
    BOOST_CHECK(sw_list[0].getWeight() == 1);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(tm->currentCash() == 100000);
    BOOST_CHECK(ac_list.size() == 2);
    BOOST_CHECK(ac_list[0]->getStock() == sm["sh600004"]);
    BOOST_CHECK(ac_list[1]->getStock() == sm["sz000001"]);
    BOOST_CHECK(ac_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(ac_list[1]->getTM()->currentCash() == 0);

    /** @arg 当前选中系统数不为0，已持仓系统数大于最大持仓数 */
    af->setParam<int>("max_sys_num", 1);
    tm->reset();
    se->clear();
    sys->setTM(subtm->clone());
    se->addStock(sm["sh600000"], sys);
    se_list = se->getSelectedSystemList(Datetime(201802100000L));
    se->clear();
    se->addStock(sm["sh600004"], sys);
    se->addStock(sm["sz000001"], sys);
    hold_list = se->getSelectedSystemList(Datetime(201802100000L));
    BOOST_CHECK(se_list.size() != 0);
    BOOST_CHECK(hold_list.size() > af->getParam<int>("max_sys_num"));
    BOOST_CHECK(tm->currentCash() == 100000);
    BOOST_CHECK(hold_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(hold_list[1]->getTM()->currentCash() == 0);

    sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
    ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
    BOOST_CHECK(sw_list.size() == 1);
    BOOST_CHECK(sw_list[0].getSYS()->getStock() == sm["sh600000"]);
    BOOST_CHECK(sw_list[0].getWeight() == 1);
    BOOST_CHECK(se_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(tm->currentCash() == 100000);
    BOOST_CHECK(ac_list.size() == 2);
    BOOST_CHECK(ac_list[0]->getStock() == sm["sh600004"]);
    BOOST_CHECK(ac_list[1]->getStock() == sm["sz000001"]);
    BOOST_CHECK(ac_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(ac_list[1]->getTM()->currentCash() == 0);

    /** @arg 当前选中系统数不为0且不包含持仓系统，持仓系统数不为零且小于最大持仓数 */
    af->setParam<int>("max_sys_num", 4);
    tm->reset();
    se->clear();
    sys->setTM(subtm->clone());
    se->addStock(sm["sh600000"], sys);
    se->addStock(sm["sh600005"], sys);
    se->addStock(sm["sz000002"], sys);
    se->addStock(sm["sz000004"], sys);
    se->addStock(sm["sz000005"], sys);
    se_list = se->getSelectedSystemList(Datetime(201802100000L));
    se->clear();
    se->addStock(sm["sh600004"], sys);
    se->addStock(sm["sz000001"], sys);
    hold_list = se->getSelectedSystemList(Datetime(201802100000L));
    BOOST_CHECK(se_list.size() != 0);
    BOOST_CHECK(hold_list.size() < af->getParam<int>("max_sys_num"));
    BOOST_CHECK(tm->currentCash() == 100000);
    BOOST_CHECK(hold_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(hold_list[1]->getTM()->currentCash() == 0);

    sw_list = af->_allocateWeight(Datetime(201802100000L), se_list);
    ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list);
    BOOST_CHECK(sw_list.size() == 5);
    BOOST_CHECK(tm->currentCash() == 0);
    BOOST_CHECK(ac_list.size() == 4);
    std::set<Stock> stk_set;

    for (auto iter = ac_list.begin(); iter != ac_list.end(); ++iter) {
        stk_set.insert((*iter)->getStock());
    }
    BOOST_CHECK(stk_set.find(sm["sh600004"]) != stk_set.end());
    BOOST_CHECK(stk_set.find(sm["sz000001"]) != stk_set.end());
    BOOST_CHECK(ac_list[0] == hold_list[0]);
    BOOST_CHECK(ac_list[1] == hold_list[1]);
    BOOST_CHECK(ac_list[0]->getTM()->currentCash() == 0);
    BOOST_CHECK(ac_list[1]->getTM()->currentCash() == 0);

    /*
    std::cout << "current cash: " << tm->currentCash() << std::endl;
    std::cout << "sw_list:===============>" << std::endl;
    for (auto iter = sw_list.begin(); iter != sw_list.end(); ++iter) {
        std::cout << *iter << std::endl;;
    }

    std::cout << std::fixed;
    std::cout.precision(4);

    std::cout << "ac_list:===============>" << std::endl;
    for (auto iter = ac_list.begin(); iter != ac_list.end(); ++iter) {
        std::cout << (*iter)->getTM()->currentCash() << std::endl;
        std::cout << (*iter)->getStock() << std::endl;
    }

    std::cout.unsetf(std::ostream::floatfield);
    std::cout.precision();
    */
}

/** @par 检测点，自动调仓 */
//BOOST_AUTO_TEST_CASE( test_AF_EqualWeight_adjust_hold) {
//TODO test_AF_EqualWeight_adjust_hold
//}

/** @} */


#include "AnalysisGraph.hpp"
#include <range/v3/all.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <sqlite3.h>
#include <unistd.h>

using namespace std;
using namespace delphi::utils;
using Eigen::VectorXd;
using fmt::print;

/*
 ============================================================================
 Private: Synthetic Data Experiment
 ============================================================================
*/

/*
 ============================================================================
 Public: Synthetic data experiment
 ============================================================================
*/

AnalysisGraph AnalysisGraph::generate_random_CAG(unsigned int num_nodes,
                                                 unsigned int num_extra_edges) {
  AnalysisGraph G;

  G.initialize_random_number_generator();

  // Get all adjectives.
  vector<string> adjectives;
  AdjectiveResponseMap adjective_response_map =
      G.construct_adjective_response_map(G.rand_num_generator, G.uni_dist, G.norm_dist, 100);
  boost::range::copy(adjective_response_map | boost::adaptors::map_keys,
              back_inserter(adjectives));

  vector<int> cag_nodes = {0};
  vector<int> rand_node(2);
  vector<string> rand_adjectives(2);
  int polarity = 0;
  string source = "";
  string target = "";
  int src_idx = 0;

  for (rand_node[1] = 1; rand_node[1] < num_nodes; rand_node[1]++) {
    rand_node.clear();
    sample(cag_nodes.begin(), cag_nodes.end(), rand_node.begin(), 1, G.rand_num_generator);
    cag_nodes.push_back(rand_node[1]);

    src_idx = G.uni_dist(G.rand_num_generator) < 0.5? 0 : 1;
    source = to_string(rand_node[src_idx]);
    target = to_string(rand_node[1 - src_idx]);

    sample(adjectives.begin(), adjectives.end(), rand_adjectives.begin(), 2, G.rand_num_generator);
    polarity = G.uni_dist(G.rand_num_generator) < 0.5 ? 1 : -1;

    auto causal_fragment =
        CausalFragment({rand_adjectives[0], 1, source},
                       {rand_adjectives[1], polarity, target});
    G.add_edge(causal_fragment);
  }

  num_extra_edges = min(num_extra_edges, (num_nodes - 1) * (num_nodes - 1));

  pair<EdgeDescriptor, bool> edge;

  for (int _ = 0; _ < num_extra_edges; _++) {
    edge.second = true;
    while (edge.second) {
      sample(cag_nodes.begin(), cag_nodes.end(), rand_node.begin(), 2, G.rand_num_generator);
      src_idx = G.uni_dist(G.rand_num_generator) < 0.5? 0 : 1;
      source = to_string(rand_node[src_idx]);
      target = to_string(rand_node[1 - src_idx]);
      edge = boost::edge(G.get_vertex_id(source),
                   G.get_vertex_id(target), G.graph);
    }

    sample(adjectives.begin(), adjectives.end(), rand_adjectives.begin(), 2, G.rand_num_generator);
    polarity = G.uni_dist(G.rand_num_generator) < 0.5 ? 1 : -1;

    auto causal_fragment =
        CausalFragment({rand_adjectives[0], 1, source},
                       {rand_adjectives[1], polarity, target});
    G.add_edge(causal_fragment);
  }

  RNG::release_instance();
  return G;
}

/**
 * TODO: This is very similar to initialize_parameters() method defined in
 * parameter_initialization.cpp. Might be able to merge the two
 * @param kde_kernels Number of KDE kernels to use when constructing beta prior distributions
 * @param initial_beta How to initialize betas
 * @param initial_derivative How to initialize derivatives
 * @param use_continuous Whether to use matrix exponential or not
 */
void AnalysisGraph::initialize_random_CAG(unsigned int num_obs,
                                          unsigned int kde_kernels,
                                          InitialBeta initial_beta,
                                          InitialDerivative initial_derivative,
                                          bool use_continuous) {
  this->initialize_random_number_generator();
  this->set_default_initial_state(initial_derivative);
  this->n_kde_kernels = kde_kernels;
  this->construct_theta_pdfs();
  this->init_betas_to(initial_beta);
  this->pred_timesteps = num_obs + 1;
  this->continuous = use_continuous;
  this->find_all_paths();
  this->set_transition_matrix_from_betas();
  this->transition_matrix_collection.clear();
  this->initial_latent_state_collection.clear();
  // TODO: We are using this variable for two different purposes.
  // create another variable.
  this->res = 1;
  this->transition_matrix_collection = vector<Eigen::MatrixXd>(this->res);
  this->initial_latent_state_collection = vector<Eigen::VectorXd>(this->res);
  this->transition_matrix_collection[0] = this->A_original;
  this->initial_latent_state_collection[0] = this->s0;
}

void AnalysisGraph::generate_synthetic_data(unsigned int num_obs,
                                            double noise_variance,
                                            unsigned int kde_kernels,
                                            InitialBeta initial_beta,
                                            InitialDerivative initial_derivative,
                                            bool use_continuous) {
  this->initialize_random_CAG(num_obs, kde_kernels, initial_beta, initial_derivative, use_continuous);
  vector<int> periods = {2, 3, 4, 6, 12};
  vector<int> rand_period(1);
  uniform_real_distribution<double> centers_dist(-100, 100);
  int max_samples_per_period = 12;
  for (int v : this->head_nodes) {
    Node& n = (*this)[v];
    sample(periods.begin(), periods.end(), rand_period.begin(), 1, this->rand_num_generator);
    n.period = rand_period[0];
    int gap_size = max_samples_per_period / n.period;
    int offset = 0; // 0 <= offset < period
    vector<int> filled_months;
    for (int p = 0; p < n.period; p++) {
      double center = centers_dist(this->rand_num_generator);
      double spread = this->norm_dist(this->rand_num_generator) * 5;
      n.centers.push_back(center);
      n.spreads.push_back(spread);
      int month = offset + gap_size * p;
      n.generated_monthly_latent_centers_for_a_year[month] = center;
      n.generated_monthly_latent_spreads_for_a_year[month] = spread;
      filled_months.push_back(month);
    }
    this->interpolate_missing_months(filled_months, n);
    print("{0} - {1}\n", n.name, n.period);
  }

  for (int v = 0; v < this->num_vertices(); v++) {
    Node& n = (*this)[v];
    n.add_indicator("ind_" + n.name, "synthetic");
    n.mean = centers_dist(this->rand_num_generator);
    while (n.mean == 0) {
      n.mean = centers_dist(this->rand_num_generator);
    }
  }

  this->generate_latent_state_sequences(0);
  this->generate_observed_state_sequences();

  this->observed_state_sequence.clear();
  this->observation_timestep_gaps.clear();
  this->n_timesteps = num_obs;

  // Access (concept is a vertex in the CAG)
  // [ timestep ][ concept ][ indicator ][ observation ]
  this->observed_state_sequence = ObservedStateSequence(this->n_timesteps);
  this->observation_timestep_gaps = vector<double>(this->n_timesteps, 1);
  this->observation_timestep_gaps[0] = 0;

  int num_verts = this->num_vertices();
  // Fill in observed state sequence
  // NOTE: This code is very similar to the implementations in
  // set_observed_state_sequence_from_data and get_observed_state_from_data
  for (int ts = 0; ts < this->n_timesteps; ts++) {
    this->observed_state_sequence[ts] = vector<vector<vector<double>>>(num_verts);

    for (int v = 0; v < num_verts; v++) {
      Node& n = (*this)[v];
      this->observed_state_sequence[ts][v] = vector<vector<double>>(n.indicators.size());

      for (int i = 0; i < n.indicators.size(); i++) {
        this->observed_state_sequence[ts][v][i] = vector<double>();
        this->observed_state_sequence[ts][v][i].push_back(
            this->predicted_observed_state_sequences[0][ts + 1][v][i] +
            noise_variance * this->norm_dist(this->rand_num_generator));
      }
    }
  }
  RNG::release_instance();
}

void AnalysisGraph::interpolate_missing_months(vector<int> &filled_months, Node &n) {
  sort(filled_months.begin(), filled_months.end());

  // Interpolate values for the missing months
  if (filled_months.size() > 1) {
    for (int i = 0; i < filled_months.size(); i++) {
      int month_start = filled_months[i];
      int month_end = filled_months[(i + 1) % filled_months.size()];

      int num_missing_months = 0;
      if (month_end > month_start) {
        num_missing_months = month_end - month_start - 1;
      }
      else {
        num_missing_months = (11 - month_start) + month_end;
      }

      for (int month_missing = 1;
      month_missing <= num_missing_months;
      month_missing++) {
        n.generated_monthly_latent_centers_for_a_year
        [(month_start + month_missing) % 12] =
            ((num_missing_months - month_missing + 1) *
            n.generated_monthly_latent_centers_for_a_year
            [month_start] +
            (month_missing)*n
            .generated_monthly_latent_centers_for_a_year
            [month_end]) /
            (num_missing_months + 1);

        n.generated_monthly_latent_spreads_for_a_year
        [(month_start + month_missing) % 12] =
            ((num_missing_months - month_missing + 1) *
            n.generated_monthly_latent_spreads_for_a_year
            [month_start] +
            (month_missing)*n
            .generated_monthly_latent_spreads_for_a_year
            [month_end]) /
            (num_missing_months + 1);
      }
    }
  } else if (filled_months.size() == 1) {
    for (int month = 0; month < n.generated_monthly_latent_centers_for_a_year.size(); month++) {
      n.generated_monthly_latent_centers_for_a_year[month] =
          n.generated_monthly_latent_centers_for_a_year
          [filled_months[0]];
      n.generated_monthly_latent_spreads_for_a_year[month] =
          n.generated_monthly_latent_spreads_for_a_year
          [filled_months[0]];
    }
  }
}


#include <boost/qvm/mat_operations3.hpp>


// -----------------------------------------------------------------------
//
// Copyright (C) 2020  - David Fernández Castellanos
//
// This file is part of the MEPLS software. You can use it, redistribute
// it, and/or modify it under the terms of the Creative Commons Attribution
// 4.0 International Public License. The full text of the license can be
// found in the file LICENSE at the top level of the MEPLS distribution.
//
// -----------------------------------------------------------------------

#include <example.h>
#include <mepls/utils.h>
#include <mepls/solver.h>
#include <mepls/system.h>
#include <mepls/event.h>
#include <mepls/history.h>

// MEPLS built-in dynamics
#include <mepls/dynamics.h>

// to parse command line arguments
#include <cmdparser.hpp>

// to parse input parameters files
#include <deal.II/base/parameter_handler.h>
#include <deal.II/base/path_search.h>

// to save output data in JSON format
#include <boost/property_tree/json_parser.hpp>

struct Parameters
{
	// these are the parameters and we will use in the simulation, initialized with some default values
	unsigned int seed = 1234567;
	unsigned int Nx = 32;
	unsigned int Ny = 32;
	double G = 30.;
	double nu = 0.3;
	double gamma = 0.05;
	double k = 6.;
	double strain_limit = 0.05;
	double lambda_init = 1.;
	double lambda_renew = 1.;
	std::string filename = "out.json";

	void declare_entries(dealii::ParameterHandler &prm)
	{
		// We declare the entries of the parameters text file. Each entry matches the name of a
		// simulation parameters (although it doesn't need to) and has a default value. We use as
		// the default value is the same value of the variables declared above

		prm.enter_subsection("Section1");

		prm.declare_entry("seed", mepls::utils::str::to_string(seed), dealii::Patterns::Integer(0),
						  "");
		prm.declare_entry("Nx", mepls::utils::str::to_string(Nx), dealii::Patterns::Integer(0), "");
		prm.declare_entry("Ny", mepls::utils::str::to_string(Ny), dealii::Patterns::Integer(0), "");
		prm.declare_entry("G", mepls::utils::str::to_string(G), dealii::Patterns::Double(0.0), "");
		prm.declare_entry("nu", mepls::utils::str::to_string(nu), dealii::Patterns::Double(0.0),
						  "");
		prm.declare_entry("gamma", mepls::utils::str::to_string(gamma),
						  dealii::Patterns::Double(0.0), "");
		prm.declare_entry("strain_limit", mepls::utils::str::to_string(strain_limit),
						  dealii::Patterns::Double(0.0), "");
		prm.declare_entry("lambda_init", mepls::utils::str::to_string(lambda_init),
						  dealii::Patterns::Double(0.0), "");
		prm.declare_entry("lambda_renew", mepls::utils::str::to_string(lambda_renew),
						  dealii::Patterns::Double(0.0), "");
		prm.declare_entry("k", mepls::utils::str::to_string(k), dealii::Patterns::Double(0.0), "");
		prm.declare_entry("filename", filename, dealii::Patterns::FileName(), "");

		prm.leave_subsection();
	}

	void load_entries(dealii::ParameterHandler &prm)
	{
		prm.enter_subsection("Section1");

		// We define how each parameter gets its value from a parameters file entry

		seed = prm.get_integer("seed");
		Nx = prm.get_integer("Nx");
		Ny = prm.get_integer("Ny");
		G = prm.get_double("G");
		nu = prm.get_double("nu");
		gamma = prm.get_double("gamma");
		k = prm.get_double("k");
		strain_limit = prm.get_double("strain_limit");
		lambda_init = prm.get_double("lambda_init");
		lambda_renew = prm.get_double("lambda_renew");
		filename = prm.get("filename");

		prm.leave_subsection();
	}

	void load_file(const std::string &filename)
	{
		// load a parameters file

		dealii::ParameterHandler prm;
		declare_entries(prm);
		prm.parse_input(filename);
		load_entries(prm);
	}

	void generate_file(const std::string &filename)
	{
		// generate a parameters file template

		std::ofstream outfile(filename);
		dealii::ParameterHandler prm;
		declare_entries(prm);
		prm.print_parameters(outfile, dealii::ParameterHandler::OutputStyle::Text);
	}

};

template<int dim>
void write_data(
	const mepls::history::History<dim> &sim_history, const Parameters &p)
{
	boost::property_tree::ptree data_tree;

	// write some metadata, such as a simulation name and description
	data_tree.put("Name", "Step4");
	data_tree.put("Description", "System driven in the athermal quasistatic limit");

	// write the simulation parameters
	data_tree.put("Parameters.dim", 2);
	data_tree.put("Parameters.seed", p.seed);
	data_tree.put("Parameters.Nx", p.Nx);
	data_tree.put("Parameters.Ny", p.Ny);
	data_tree.put("Parameters.G", p.G);
	data_tree.put("Parameters.nu", p.nu);
	data_tree.put("Parameters.gamma", p.gamma);
	data_tree.put("Parameters.lambda_renew", p.lambda_renew);
	data_tree.put("Parameters.lambda_init", p.lambda_init);
	data_tree.put("Parameters.k", p.k);
	data_tree.put("Parameters.strain_limit", p.strain_limit);

	// write some metadata, such as a simulation name and description
	data_tree.put("Name", "Step4");
	data_tree.put("Description", "System driven in the athermal quasistatic limit");

	// write the simulation parameters
	data_tree.put("Parameters.dim", 2);
	data_tree.put("Parameters.seed", p.seed);
	data_tree.put("Parameters.Nx", p.Nx);
	data_tree.put("Parameters.Ny", p.Ny);
	data_tree.put("Parameters.G", p.G);
	data_tree.put("Parameters.nu", p.nu);
	data_tree.put("Parameters.gamma", p.gamma);
	data_tree.put("Parameters.lambda_renew", p.lambda_renew);
	data_tree.put("Parameters.lambda_init", p.lambda_init);
	data_tree.put("Parameters.k", p.k);
	data_tree.put("Parameters.strain_limit", p.strain_limit);

	// We write the event histories with CSV format to a string.
	// Here, we write only the columns of interest, but there are more available (see the
	// documentation).
	std::ostringstream plastic_events_csv;
	plastic_events_csv << "index,element,eigenstrain_00,eigenstrain_11,eigenstrain_01\n";
	for(auto &row : sim_history.plastic)
		plastic_events_csv << row.index << "," << row.element << "," << row.eigenstrain_00 << ","
						   << row.eigenstrain_11 << "," << row.eigenstrain_01 << "\n";

	data_tree.put("Data.plastic_events", plastic_events_csv.str());

	std::ostringstream driving_events_csv;
	driving_events_csv << "index,dext_stress,dtotal_strain\n";
	for(auto &row : sim_history.driving)
		driving_events_csv << row.index << "," << row.dext_stress << "," << row.dtotal_strain
						   << "\n";

	data_tree.put("Data.driving_events", driving_events_csv.str());

	std::ostringstream macro_evolution_csv;
	macro_evolution_csv << "index,ext_stress,total_strain,time,av_vm_stress,av_vm_plastic_strain\n";
	for(auto &row : sim_history.macro_evolution)
		macro_evolution_csv << row.index << "," << row.ext_stress << "," << row.total_strain << ","
							<< row.time << "," << row.av_vm_stress << ","
							<< row.av_vm_plastic_strain << "\n";

	data_tree.put("Data.macro_evolution", macro_evolution_csv.str());

	// write the data tree to a JSON file
	std::ofstream output_file(p.filename);
	boost::property_tree::json_parser::write_json(output_file, data_tree);
	output_file.close();
}

void run(const Parameters &p)
{
	//----- SETUP ------

	// we do the same as in the previous tutorial, but this time we use the
	// parameters from the input Parameters object

	constexpr unsigned dim = 2;
	std::mt19937 generator(p.seed);

	dealii::SymmetricTensor<4, dim> C = mepls::utils::tensor::make_isotropic_stiffness<dim>(p.G,
																							p.nu);

	mepls::element::Vector<dim> elements;

	// we also store the elements into a vector that knows the derived class, so we can access
	// the example::element::Scalar<dim>::conf struct later (this member cannot be accessed
	// through the pointers to the base mepls::element::Element<dim>)
	std::vector<example::element::Scalar<dim> *> elements_scalar;

	for(double n = 0; n < p.Nx * p.Ny; ++n)
	{
		example::element::Scalar<dim>::Config conf;
		conf.number = n;
		conf.gamma = p.gamma;
		conf.lambda = p.lambda_init;
		conf.k = p.k;

		auto element = new example::element::Scalar<dim>(conf, generator);
		element->C(C);

		elements_scalar.push_back(element);
		elements.push_back(element);
	}

	mepls::elasticity_solver::LeesEdwards<dim> solver(p.Nx, p.Ny);
	for(auto &element : elements)
		solver.set_elastic_properties(element->number(), element->C());
	solver.setup_and_assembly();

	mepls::element::calculate_ext_stress_coefficients(elements, solver);
	mepls::element::calculate_local_stress_coefficients_central(elements, solver);

	mepls::system::Standard<dim> system(elements, solver, generator);

	mepls::history::History<dim> sim_history("Simulation_history");

	system.set_history(sim_history);

	// when the elements were created, their slip systems were initialized with thresholds from a
	// Weibull distribution with scale lambda_init. We change it now to lambda_renew. Therefore,
	// when plastic deformation occurs, the renew local thresholds will have a different
	// average
	for(auto &element : elements_scalar)
		element->conf.lambda = p.lambda_renew;

	//----- DYNAMICS ------

	// This object will allow us to check for different conditions by which
	// the simulation might stop. Different conditions might be checked, but as long as one of
	// them evaluates to false, when continue_simulation() is called it will return false
	mepls::utils::ContinueSimulation continue_simulation;

	sim_history.add_macro(system);

	// run while the continue_simulation object says so
	while(continue_simulation())
	{
		std::cout << system.macrostate["total_strain"] << " " << system.macrostate["ext_stress"]
				  << std::endl;

		// these are the same dynamics we implemented in the previous tutorial, but using MEPLS built-in

		// apply an external strain increment of 0.01%
		mepls::dynamics::finite_extremal_dynamics_step(1e-4, system);
		sim_history.add_macro(system);

		// perform and avalanche of slip events. By passing the continue_simulation object,
		// the relaxation function can set its own condition for stopping the simulation.
		// Specifically, it will check if the avalanche size overcomes a certain maximum upper limit
		mepls::dynamics::relaxation(system, continue_simulation);
		sim_history.add_macro(system);

		// check if the strain has reached the strain limit. If it has, the next time continue_simulation() is called
		// it will return false, so we will exit the main loop
		continue_simulation(system.macrostate["total_strain"] < p.strain_limit,
							"total strain limit reached");

	}

	// print the message of the stopping condition that was met
	std::cout << continue_simulation << std::endl;

	for(auto &element : elements)
		delete element;

	write_data(sim_history, p);
}

int main(int argc, char *argv[])
{
	// Read the command line arguments. We define the -f 'filename' to pass the
	// path to the parameters file
	cli::Parser parser(argc, argv);
	parser.set_optional<std::string>("f", "file", "./default.prm",
									 "Name of the input configuration file");
	parser.run_and_exit_if_error();
	// you can check https://github.com/FlorianRappl/CmdParser for a further documentation of cli::Parser

	// Create the parameters object
	Parameters p;

	// We try to load the parameters file, but if it doesn't exist, we generate a new one with the name
	// default.prm and default values
	try
	{
		p.load_file(parser.get<std::string>("f"));
	}
	catch(dealii::PathSearch::ExcFileNotFound &)
	{
		p.generate_file(parser.get<std::string>("f"));
		std::cout << "Configuration file " << parser.get<std::string>("f") << " created"
				  << std::endl;
		return 1;
	}

	// run the simulation
	run(p);

	return 0;
}


#include <cppunit/CompilerOutputter.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/TextTestProgressListener.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/ui/text/TestRunner.h>

#include <algorithm>
#include <boost/foreach.hpp>
#include <iterator>

#include "CalibFormats/SiStripObjects/interface/SiStripGain.h"

#ifndef TestSiStripGain_cc
#define TestSiStripGain_cc

class TestSiStripGain : public CppUnit::TestFixture {
public:
  TestSiStripGain() {}
  void setUp() {
    detId = 436282904;

    apvGain1 = new SiStripApvGain;
    std::vector<float> theSiStripVector;
    theSiStripVector.push_back(1.);
    theSiStripVector.push_back(0.8);
    theSiStripVector.push_back(1.2);
    theSiStripVector.push_back(2.);
    fillApvGain(apvGain1, detId, theSiStripVector);

    apvGain2 = new SiStripApvGain;
    theSiStripVector.clear();
    theSiStripVector.push_back(1.);
    theSiStripVector.push_back(1. / 0.8);
    theSiStripVector.push_back(1. / 1.2);
    theSiStripVector.push_back(2.);
    fillApvGain(apvGain2, detId, theSiStripVector);
  }

  void tearDown() {
    delete apvGain1;
    delete apvGain2;
  }

  void testConstructor() {
    // Test with normalization factor = 1
    apvGainsTest(1.);
    // Test with normalization factor != 1
    apvGainsTest(2.);
  }

  void testMultiply() {
    // Test with norm = 1
    multiplyTest(1., 1.);

    // Test with norm != 1
    multiplyTest(2., 3.);
  }

  void multiplyTest(const float &norm1, const float &norm2) {
    std::pair<std::string, std::string> recordLabelPair1("gainRcd1", "");
    std::pair<std::string, std::string> recordLabelPair2("gainRcd2", "");
    std::vector<std::pair<std::string, std::string>> recordLabelPairVector;
    recordLabelPairVector.push_back(recordLabelPair1);
    recordLabelPairVector.push_back(recordLabelPair2);
    std::vector<float> normVector;
    normVector.push_back(norm1);
    normVector.push_back(norm2);

    SiStripGain gain(*apvGain1, norm1, recordLabelPair1);
    gain.multiply(*apvGain2, norm2, recordLabelPair2);
    SiStripApvGain::Range range = gain.getRange(detId);

    // Check multiplication
    CPPUNIT_ASSERT(float(gain.getApvGain(0, range)) == float(1. / norm1 * 1. / norm2));
    CPPUNIT_ASSERT(float(gain.getApvGain(1, range)) == float(0.8 / norm1 * 1. / (0.8 * norm2)));
    CPPUNIT_ASSERT(float(gain.getApvGain(2, range)) == float(1.2 / norm1 * 1. / (1.2 * norm2)));
    CPPUNIT_ASSERT(float(gain.getApvGain(3, range)) == float(2. / norm1 * 2. / norm2));

    checkTag(gain, normVector, normVector.size(), recordLabelPairVector);
  }

  void apvGainsTest(const float &norm) {
    SiStripGain gain(*apvGain1, norm);
    SiStripApvGain::Range range = gain.getRange(detId);
    CPPUNIT_ASSERT(float(gain.getApvGain(0, range)) == float(1. / norm));
    CPPUNIT_ASSERT(float(gain.getApvGain(1, range)) == float(0.8 / norm));
    CPPUNIT_ASSERT(float(gain.getApvGain(2, range)) == float(1.2 / norm));
    CPPUNIT_ASSERT(float(gain.getApvGain(3, range)) == float(2. / norm));
    checkTag(gain, norm, "", "");

    SiStripGain gain2(*apvGain2, norm);
    SiStripApvGain::Range range2 = gain2.getRange(detId);
    CPPUNIT_ASSERT(float(gain2.getApvGain(0, range2)) == float(1. / norm));
    CPPUNIT_ASSERT(float(gain2.getApvGain(1, range2)) == float(1. / (norm * 0.8)));
    CPPUNIT_ASSERT(float(gain2.getApvGain(2, range2)) == float(1. / (norm * 1.2)));
    CPPUNIT_ASSERT(float(gain2.getApvGain(3, range2)) == float(2. / norm));
    checkTag(gain2, norm, "", "");
  }

  void checkTag(const SiStripGain &gain, const float &norm, const std::string &rcdName, const std::string &labelName) {
    std::vector<float> normVector;
    normVector.push_back(norm);
    std::vector<std::pair<std::string, std::string>> recordLabelPairVector;
    recordLabelPairVector.push_back(std::make_pair(rcdName, labelName));
    checkTag(gain, normVector, 1, recordLabelPairVector);
  }
  void checkTag(const SiStripGain &gain,
                const std::vector<float> &norm,
                const uint32_t tagNum,
                const std::vector<std::pair<std::string, std::string>> &recordLabelPair) {
    CPPUNIT_ASSERT(gain.getNumberOfTags() == tagNum);
    for (unsigned int i = 0; i < tagNum; ++i) {
      CPPUNIT_ASSERT(float(gain.getTagNorm(i)) == norm[i]);
      CPPUNIT_ASSERT(gain.getRcdName(i) == recordLabelPair[i].first);
      CPPUNIT_ASSERT(gain.getLabelName(i) == recordLabelPair[i].second);
    }
  }

  void fillApvGain(SiStripApvGain *apvGain, const uint32_t detId, const std::vector<float> &theSiStripVector) {
    SiStripApvGain::Range range(theSiStripVector.begin(), theSiStripVector.end());
    apvGain->put(detId, range);
  }

  SiStripApvGain *apvGain1;
  SiStripApvGain *apvGain2;
  uint32_t detId;

  // Declare and build the test suite
  CPPUNIT_TEST_SUITE(TestSiStripGain);
  CPPUNIT_TEST(testConstructor);
  CPPUNIT_TEST(testMultiply);
  CPPUNIT_TEST_SUITE_END();
};

// Register the test suite in the registry.
// This way we will have to only pass the registry to the runner
// and it will contain all the registered test suites.
CPPUNIT_TEST_SUITE_REGISTRATION(TestSiStripGain);

#endif


#ifdef _WIN32
#pragma warning(disable:4996)
#define  _D_SCL_SECURE_NO_WARNINGS
#endif

#include "VersionStorage.h"
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <string>
#include <vector>
#include <qtextstream.h>

VersionStorage::VersionStorage(QString versionString)
{
    std::remove_if(versionString.begin(), versionString.end(), versionFilter);
    std::vector<std::string> strs;
    std::string versionSTD(versionString.toStdString());
    boost::split(strs,versionSTD, boost::is_any_of(".,"));
    if (strs.size() != 3)
    {
        m_isLegal = false;
        return;
    }
    m_major = stoi(strs[0]);
    m_minor = stoi(strs[1]);
    m_revision = stoi(strs[2]);

    m_isLegal = true;
}

VersionStorage::~VersionStorage()
{
}

bool VersionStorage::versionFilter(QChar c)
{
    return !(c == '.' || (c >= '0' && c <= '9'));
}

bool VersionStorage::operator<=(const VersionStorage& other) const
{
    if (m_major > other.m_major)
        return false;
    else if (m_major < other.m_major)
        return true;

    if (m_minor > other.m_minor)
        return false;
    else if (m_minor < other.m_minor)
        return true;

    if (m_revision > other.m_revision)
        return false;
   return true;
}

bool VersionStorage::operator==(const VersionStorage& other) const
{
    return m_major == other.m_major && m_minor == other.m_minor && m_revision == other.m_revision;
}

VersionStorage::operator QString() const
{
    QString rc;
    if (m_isLegal)
    {
        QTextStream ss(&rc);
        ss << 'V' << m_major << '.' << m_minor << '.' << m_revision;
    }
    return rc;
}


#include <boost/pending/disjoint_sets.hpp>


// Copyright DEWETRON GmbH 2019

#include <algorithm>
#include <cctype>
#include <string>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>

namespace odk
{
namespace framework
{

    std::vector<int> splitVersionString(const std::string& ver)
    {
        std::vector<int> tuple;

        boost::char_separator<char> sep(".");
        boost::tokenizer< boost::char_separator<char>> tok(ver, sep);

        for (auto t : tok)
        {
            try
            {
                tuple.push_back(boost::lexical_cast<int>(t));
            }
            catch (const boost::bad_lexical_cast&)
            {
                break;
            }
        }

        return tuple;
    }

    int compareVersionStrings(const std::string& a, const std::string& b)
    {
        auto a_tuple = splitVersionString(a);
        auto b_tuple = splitVersionString(b);

        if (a_tuple.size() < b_tuple.size())
        {
            a_tuple.resize(b_tuple.size(), 0);
        }
        if (b_tuple.size() < a_tuple.size())
        {
            b_tuple.resize(a_tuple.size(), 0);
        }

        for (size_t i = 0; i < a_tuple.size(); ++i)
        {
            if (a_tuple[i] < b_tuple[i])
            {
                return -1;
            }
            else if (a_tuple[i] > b_tuple[i])
            {
                return 1;
            }
        }
        return 0;
    }

}
}


#include <boost/numeric/ublas/vector_proxy.hpp>


/*
Copyright 2017 InitialDLab

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <cstdlib>

#include <boost/geometry/geometries/box.hpp>
#include <boost/timer/timer.hpp>
#include <boost/lexical_cast.hpp>

#include "rtree/rtree.h"
#include "rtree/tests/integrity_checker.h"

#include "level_sampling/level_sampling.h"

#include "mongo_types.h"
#include "hilbert/hilbert.h"

std::string const DATASET = "geo_with_alt_debug";
int const LEVEL_SAMPLING_MIN_MEM_LEVEL = 4;
int const REPEAT = 100;
int const SAMPLE_SIZE = 10000;

struct AVG
{
    void feed(long double x) {
        sum += x;
        sum_sq += x*x;
        ++ count;
    }

    long double mean(void) const { return sum / count; }
    long double variation(void) const {
        return sum_sq / (count - 1) - sum * sum / count / (count - 1);
    }
    long double standard_deviation(void) const { return std::sqrt(variation()); }
    long double confidence_interval_95(void) const { return 1.96l * std::sqrt(variation() / count); }

    long double sum = 0.0l;
    long double sum_sq = 0.0l;
    size_t count = 0;

    struct Feeder
    {
        Feeder(AVG & avg) : avg(avg) { }
        Feeder& operator++() { return *this; }
        Feeder& operator++(int) { return *this; }
        Feeder& operator*() { return *this; }
        template<typename T>
        void operator= (T const& t) { avg.feed(t.altitude); }
        AVG & avg;
    };
    Feeder get_feeder (void) {
        return Feeder(*this);
    }
};

struct entry_with_alt
    : mongo_types::entry
{
    entry_with_alt() { }
    entry_with_alt(float x, float y, int timestamp, int altitude, std::string const& id)
        : mongo_types::entry(x, y, timestamp, id)
        , altitude(altitude)
    { }
    int altitude;
};

struct sample_entry_with_alt
    : mongo_types::sample_entry
{
    sample_entry_with_alt () { }
    sample_entry_with_alt (entry_with_alt const& e)
        : mongo_types::sample_entry(e)
        , altitude(e.altitude)
    { }

    int altitude;
};

namespace std {
    template <>
    struct hash<sample_entry_with_alt>
    {
        std::size_t operator () (sample_entry_with_alt const& e) const {
            return std::hash<mongo_types::sample_entry>()(e);
        }
    };
}

/*
 * cache system disk cache
 */
const char * clear_cache_cmd = "/uusoc/scratch/magpie17/robertc/clearCache > /dev/null";
void clear_cache()
{
    return;
    auto unused = system(clear_cache_cmd);
    // TODO clear block manager cache when implemented
}

namespace bg = boost::geometry;
namespace bt = boost::timer;

struct Exp
{
    // how many sample queries to perform
    using entry = entry_with_alt;
    using sample_entry = sample_entry_with_alt;
    using point = mongo_types::point3d;
    using box = mongo_types::box3d;

    using rtree_t = rtree::rtree<
        entry,
        sample_entry,
        box,
        256, // node sample count
        16  // max fanout
    >;

    using ls_t = level_sampling::level_sampling<
        entry,
        sample_entry,
        box,
        16 // max fanout
    >;

    Exp(std::string const& rtree_name, std::string const& ls_name)
        : rtree_name(rtree_name)
        , ls_name(ls_name)
    { }

    std::vector<entry_with_alt> raw_data;
    void build() {
        load_raw_data();
        //build_rtree();
        build_ls();
        std::cerr << "Done." << std::endl;
    }

    void load_raw_data() {
        std::default_random_engine rng;
        static const char * id_list = "0123456789abcdef";
        std::uniform_int_distribution<int> id_dist(0, 15);
        char id_buffer[2 * mongo_types::OID::oid_len + 1];
        id_buffer[2*mongo_types::OID::oid_len] = 0;

        for(int i = 0; i < 1000000; ++i)
        {
            for(int j = 0; j < 2 * mongo_types::OID::oid_len; ++j)
            {
                id_buffer[j] = id_list[id_dist(rng)];
            }
            raw_data.emplace_back(i, i, i, i, id_buffer);
        }
    }

    void build_rtree(void) {
        std::cerr << "building rtree io layers" << std::endl;
        rtree_t::build_io_layers(raw_data.begin(), raw_data.end(), rtree_name);
        std::cerr << "generating rtree samples and saving rtree mem nodes" << std::endl;
        std::unique_ptr<rtree_t>(new rtree_t(rtree_name))->save_mem_nodes();
    }

    void build_ls(void) {
        std::cerr << "building level sampling" << std::endl;
        ls_t::build(raw_data.begin(), raw_data.end(), ls_name);
    }

    void load_rtree (bool load_mem_nodes = false) {
        bt::cpu_timer timer;
        rtree_ptr.reset(new rtree_t(rtree_name, false, load_mem_nodes));
        std::cout << "tree size: " << rtree_ptr->size() << std::endl;

        auto stats = rtree_ptr->get_stats();
        std::cout << "leaf node count: " << stats.leaf_nodes << std::endl;
        std::cout << "internal node count: " << stats.internal_nodes << std::endl;
        std::cout << "tree building time(ms): " << ' ' << timer.elapsed().wall/1000000.0 << std::endl;

        std::cout << "bbox: ";
        dump_box(std::cout, rtree_ptr->bbox());
        std::cout << std::endl;
    }

    void load_ls (void) {
        bt::cpu_timer timer;
        ls_ptr.reset(new ls_t(ls_name, LEVEL_SAMPLING_MIN_MEM_LEVEL));
        std::cout << "min mem level: " << ls_ptr->min_mem_level << std::endl;
        std::cout << "max level: " << ls_ptr->max_level << std::endl;
    }

    template<typename Cursor, typename Timer>
    void test_cursor(Cursor & cursor, Timer & timer) {
        AVG avg;
        auto feeder = avg.get_feeder();
        std::cout << "No Time(ms) IO ~mean diff sd ci95" << std::endl;
        for(int i = 0; i < REPEAT; ++i)
        {
            timer.resume();
            cursor.get_samples(SAMPLE_SIZE, feeder);
            timer.stop();

            auto stats = cursor.get_stats();
            std::cout << i
                << " " << (timer.elapsed().wall / 1000000.0)
                << " " << cursor.get_io_cost()
                << " " << avg.mean()
                << " " << (avg.mean() - exact_mean)
                << " " << avg.standard_deviation()
                << " " << avg.confidence_interval_95()
                << std::endl;
        }
    }

    void run() {
        load_rtree(true);
        load_ls();

        std::vector<sample_entry> buffer;
        box query;

        box geo_query1(
            point(38.74731445, 109.6753998, 263181920),
            point(41.16065598, 123.3114014, 278206016)
        );
        box geo_query2(
            point(1.044023991, -39.27259827, 155019328),
            point(39.85100937, 179.9969482, 396609536)
        );

        //query = geo_query2;
        query = rtree_ptr->bbox();

        if(rtree_ptr)
            rtree_ptr->get_block_manager().set_cache_capacity(0);

        { // range report (exact)
            AVG avg;
            auto feeder = avg.get_feeder();
            rtree_ptr->range_report(query, feeder);
            std::cout << "COUNT " << avg.count << std::endl;
            std::cout << "AVG " << avg.mean() << std::endl;
            std::cout << "VAR " << avg.variation() << std::endl;
            std::cout << "SD " << avg.standard_deviation() << std::endl;
            //std::cout << "SUM " << avg.sum << std::endl;
            //std::cout << "SUM_SQ " << avg.sum_sq << std::endl;
            exact_mean = avg.mean();
        }

        if(0)
        { // baseline
            rtree_ptr->flush_cache();
            clear_cache();
            bt::cpu_timer timer;
            auto cursor = rtree_ptr->naive_sample_query(query);
            timer.stop();
            test_cursor(cursor, timer);
        }

        { // rs-tree
            rtree_ptr->flush_cache();
            clear_cache();
            bt::cpu_timer timer;
            auto cursor = rtree_ptr->sample_query(query);
            timer.stop();
            test_cursor(cursor, timer);
        }

        if(0)
        { // level sampling
            ls_ptr->flush_cache();
            clear_cache();
            bt::cpu_timer timer;
            auto cursor = ls_ptr->sample_query(query);
            timer.stop();
            test_cursor(cursor, timer);
        }
    }

    void insert() {
        load_ls();

        std::default_random_engine rng;
        static const char * id_list = "0123456789abcdef";
        std::uniform_int_distribution<int> id_dist(0, 15);
        char id_buffer[2 * mongo_types::OID::oid_len + 1];
        id_buffer[2*mongo_types::OID::oid_len] = 0;

        bt::cpu_timer timer;
        for(int i = 0; i < 100; ++i)
        {
            for(int j = 0; j < 100; ++j)
            {
                for(int j = 0; j < 2 * mongo_types::OID::oid_len; ++j)
                {
                    id_buffer[j] = id_list[id_dist(rng)];
                }
                entry_with_alt e(i, i, i, i, id_buffer);
                ls_ptr->insert(e);
            }

            std::cout << i
                << " " << (timer.elapsed().wall / 1000000.0)
                << " " << ls_ptr->io_cost
                << std::endl;
        }
    }

    std::unique_ptr<rtree_t> rtree_ptr;
    std::unique_ptr<ls_t> ls_ptr;
    std::string rtree_name, ls_name;
    double exact_mean;
};

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

    if(argc != 2)
    {
        std::cerr << "argument: build | run" << std::endl;
        return -1;
    }
    std::string path = "/uusoc/scratch/magpie21/luwang/data/built/" + DATASET;
    Exp exp(path + "_tree", path + "_level");

    std::string cmd(argv[1]);
    if(cmd == "build")
        exp.build();
    else if (cmd == "run")
        exp.run();
    else if (cmd == "insert")
        exp.insert();
    else
        std::cerr << "unknown cmd: " << cmd << std::endl;

    return 0;
}


// test_math_fwd.cpp

//  Copyright John Maddock 2010.
//  Copyright Paul A. Bristow 2010.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// Basic sanity check that special functions forward declaration header
// <boost/math/special_functions/math_fwd.hpp>
// and distributions forward declarations header
// <boost/math/distributions/fwd.hpp>
// #includes all the files that it needs to.
//
#include <boost/math/special_functions/math_fwd.hpp>
#include <boost/math/special_functions/beta.hpp>
// using boost::math::beta;

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/distributions/normal.hpp>
// using boost::math::normal_distribution;

int main()
{
  // Special functions.
  // Call functions, discarding any result.
  using boost::math::beta;
  beta(1.,2.);

  // Distributions.
  using boost::math::normal_distribution;
  using boost::math::normal;

  // Construct some distributions.
  normal myf1(1., 2); // Using typedef.
  normal n01; // Use default values for mean and standard deviation).
  normal_distribution<> n01d(1., 2); // Using default RealType double.
  normal_distribution<float> n01f; // Using float type, and defaults.
  normal_distribution<float> myf22(0.f, 2.f); // Using explicit RealType float.

  return 0;
}

/*

VS2010

------ Build started: Project: test_math_fwd, Configuration: Debug Win32 ------
  test_math_fwd.cpp
  test_math_fwd.vcxproj -> J:\Cpp\MathToolkit\test\Math_test\Debug\test_math_fwd.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

------ Build started: Project: test_math_fwd, Configuration: Release Win32 ------
  test_math_fwd.cpp
  Generating code
  Finished generating code
  test_math_fwd.vcxproj -> J:\Cpp\MathToolkit\test\Math_test\Release\test_math_fwd.exe

*/


#include <geom_poly_serialize.h>

#include <fstream>
#include <iostream>

#include <boost/serialization/vector.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/tracking.hpp>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

// it seems that the serialize stuff doesn't like having both types of polygon declared.
namespace boost{
    namespace serialization{

        template<class Archive>
        inline void serialize(Archive & ar, point &point, const unsigned int file_version)
        {
            ar & const_cast<double &>(point.x());
            ar & const_cast<double &>(point.y());
        }

        template<class Archive>
        inline void serialize(Archive & ar, ring &ring, const unsigned int file_version)
        {
            //std::cout << "Ring: Serializing ring" << std::endl;
            ar & static_cast<std::vector<point>& >(ring);
        }

        template<class Archive>
        inline void serialize(Archive & ar, polygon &t, const unsigned int file_version)
        {
            //std::cout << "Polygon: Serializing outer ring" << std::endl;
            ar & t.outer();

            //std::cout << "Polygon: Serializing inner rings" << std::endl;
            ar & t.inners();
        }

        template<class Archive>
        inline void serialize(Archive & ar, multi_polygon &polys, const unsigned int file_version)
        {
            //std::cout << "Multi: Serializing polys" << std::endl;
            ar & static_cast<std::vector<polygon>& >(polys);
        }

    }
}

void write_polys(std::string filename, const multi_polygon &polys)
{
    std::ofstream ofs(filename);
    boost::archive::text_oarchive oa(ofs);

    oa << polys;
    // for (polygon poly : polys) {
    //     oa << poly;
    // }

}

void read_polys(std::string filename, multi_polygon &polys)
{
    polys.clear();

    std::ifstream ifs(filename);
    boost::archive::text_iarchive ia(ifs);
    ia >> polys;
}


// MIT License

// Copyright (c) 2019 Edward Liu

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "common/octree.h"
#define BOOST_TEST_MODULE OctreeTest
#include <boost/test/unit_test.hpp>

namespace static_map {
namespace common {

BOOST_AUTO_TEST_CASE(InitWithPointCloud) {
  OctreeConfig config;
  config.max_depth = 10;
  config.max_num_points_in_voxel = 4;
  config.max_size = 10.;
  config.center << 0, 0, 0;

  Octree octree(config);

  Eigen::MatrixXd points(12, 3);
  // clang-format off
  points <<
    0., 0., 0.,
    1., 1., 1.,
    2., 2., 2.,
    -1., -1., -1.,
    1.5, 1., 0.,
    2., 1., 3.,
    -1., 3., 1.,
    5., 4., 2.,
    2., -5., -4.,
    -4., 2., -2.,
    1., 7., 1.,
    3., 1., -2.;
  // clang-format on
  octree.InitWithPointCloud(points.transpose());
  BOOST_CHECK_EQUAL(2, octree.GetMaxDepth());
  // octree.RecursiveOutput();
}
}  // namespace common
}  // namespace static_map