/**
   @file dynamic_perfect_hash.hpp
   @author Kevin Hu
   @date 2009.11.24
 */
#ifndef DYNAMIC_PERFECT_HASHING_HPP
#define DYNAMIC_PERFECT_HASHING_HPP

#include <types.h>
#include <util/log.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <time.h>
#include <am/am.h>

using namespace std;

NS_IZENELIB_AM_BEGIN
/**
 *@class DynamicPerfectHash
 * This work is based on Martin's paper 'Dynammic Perfect Hashing: Upper and Lower Bonds' in 1990.
 *
 **/
template<
  class KeyType = uint32_t,
  class ValueType = uint32_t,//string,
  uint32_t c = 2500000,
  uint32_t SM_O = 1
  >
class DynamicPerfectHash : public AccessMethod<KeyType, ValueType>
{
  typedef boost::archive::text_iarchive iarchive;
  typedef boost::archive::text_oarchive oarchive;

  typedef DynamicPerfectHash<KeyType, ValueType, c, SM_O> SelfType;

  class subtable_cell;

  class table_cell
  {
  public:
    uint64_t b_;
    uint64_t s_;
    uint64_t m_;
    uint64_t k_;
    uint64_t u_;
    uint64_t p_;
    subtable_cell* pSub_;

    table_cell()
    {
      b_ = s_ = m_ = 0;
      pSub_ = NULL;
      SelfType::random_param(k_, u_, p_);
    }

  friend ostream& operator << ( ostream& os, const table_cell& cell)
    {
      os<<"["<<cell.b_<<","<<cell.s_<<","<<cell.m_<<","<<cell.k_<<", "<<cell.u_<<", "<<cell.p_<<"]";
      return os;
    }

    bool save(FILE* f)
    {
      uint64_t arr[6] =
        { b_, s_, m_ ,k_, u_, p_
        };

      if (fwrite(arr, sizeof(arr), 1, f) != 1)
        return false;

      return true;
    }

    bool load(FILE* f)
    {
      uint64_t arr[6];

      if (fread(arr, sizeof(arr), 1, f) != 1)
        return false;

      b_ = arr[0];
      s_ = arr[1];
      m_ = arr[2];
      k_ = arr[3];
      u_ = arr[4];
      p_ = arr[5];

      return true;
    }

  }
    ;

  class subtable_cell
  {
  public:
    uint64_t data_;
    uint64_t valueIndex_;

    subtable_cell()
    {
      data_ = 0;
      valueIndex_ = -1;
    }

    subtable_cell(uint64_t data, uint64_t valueIdx)
    {
      data_ = data;
      valueIndex_ = valueIdx;
    }

  friend ostream& operator << ( ostream& os, const subtable_cell& cell)
    {
      os<<"("<<cell.data_<<"|"<<cell.valueIndex_<<")";
      return os;
    }

    bool save(FILE* f)
    {
      if (fwrite(&data_, sizeof(KeyType), 1, f) != 1)
        return false;

      if (fwrite(&valueIndex_, sizeof(uint64_t), 1, f) != 1)
        return false;

      return true;
    }

    bool load(FILE* f)
    {
      if (fread(&data_, sizeof(KeyType), 1, f) != 1)
        return false;

      if (fread(&valueIndex_, sizeof(uint64_t), 1, f) != 1)
        return false;

      return true;
    }

  }
    ;
public:

  inline DynamicPerfectHash()
  {
    sum_s_ = M_ = count_ = update_count_ = mainT_size_ = 0;
    pMainT_ = NULL;
    //condition_time_ = insert_time1_ = insert_time2_ = adjust_time_ = rehash_time_ = 0;
  }

  inline ~DynamicPerfectHash()
  {
    deleteAll();
  }

  uint64_t getCount() const
  {
    return count_;
  }

  uint64_t getUpdateCount() const
  {
    return update_count_;
  }

  uint64_t getSize() const
  {
    return mainT_size_;
  }

  int num_items() const
  {
    return count_;
  }

  //  void print_time()
  // {
//     cout<<"Adjusting subtable takes "<<(double)adjust_time_ / CLOCKS_PER_SEC<<" seconds!\n";
//     cout<<"Rehashing all takes "<<(double)rehash_time_ / CLOCKS_PER_SEC<<" seconds!\n";
//     cout<<"Inserting1 takes "<<(double)insert_time1_ / CLOCKS_PER_SEC<<" seconds!\n";
//     cout<<"Inserting2 takes "<<(double)insert_time2_ / CLOCKS_PER_SEC<<" seconds!\n";
//     cout<<"condition takes "<<(double)condition_time_ / CLOCKS_PER_SEC<<" seconds!\n";

  // }

  //bool insert(const KeyType& x, const ValueType& data)
//   virtual bool insert(const KeyType& key, const ValueType& value){
// 		DataType<KeyType,ValueType> data(key, value);
//     	return insert(data);
// 	}
  using AccessMethod<KeyType, ValueType>::insert;

  virtual bool insert(const DataType<KeyType,ValueType>& data)
  {
    uint64_t x = str_hash(data.key);

    uint64_t valueIdx = dataVec_.size();
    dataVec_.push_back(data.value);

    update_count_++;

    if (update_count_>M_)
    {
      //      finish = clock();
      //      insert_time1_ += finish-start;
      return rehashAll(x, valueIdx);
    }

    uint64_t j = hashFunc(main_k_, main_u_, main_p_, mainT_size_, x);
    if (pMainT_[j].pSub_!=NULL)
    {
      //cout<<"insert: if (pMainT_[j].pSub_!=NULL)\n";
      uint64_t sub_j = hashFunc(pMainT_[j].k_, pMainT_[j].u_, pMainT_[j].p_, pMainT_[j].s_, x);

      if (pMainT_[j].pSub_!=NULL && pMainT_[j].pSub_[sub_j].data_==x)
      {
        //cout<<"insert: if (pMainT_[j].pSub_!=NULL && pMainT_[j].pSub_[sub_j].data_==x)\n";
        pMainT_[j].pSub_[sub_j].valueIndex_ = valueIdx;
        //        finish = clock();
        //        insert_time1_ += finish-start;
        return true;
      }

      if (pMainT_[j].b_ + 1<=pMainT_[j].m_)// size of Tj sufficient
      {
        if (pMainT_[j].pSub_[sub_j].valueIndex_ == (uint64_t)-1)
        {
          pMainT_[j].pSub_[sub_j] = subtable_cell(x, valueIdx);
          count_ ++;
          pMainT_[j].b_ ++;
          return true;
        }

        adjustSubTable(j, subtable_cell(x, valueIdx));
        //finish = clock();
        //insert_time1_ += finish-start;
        return true;
      }
    }

    //start = clock();
    //// size of Tj is not sufficient
    //cout<<"insert: size of Tj is not sufficient\n";
    pMainT_[j].m_ = 2* (1<pMainT_[j].m_? pMainT_[j].m_ : 1);
    uint64_t tmp_s = pMainT_[j].s_;
    pMainT_[j].s_ = 2*pMainT_[j].m_*(pMainT_[j].m_ -1);
    sum_s_ = sum_s_ - tmp_s + pMainT_[j].s_;

    if (condition())
    {
      //double capaciy of Tj
      subtable_cell* sub = pMainT_[j].pSub_;
      pMainT_[j].pSub_ = new subtable_cell[pMainT_[j].s_];

      memcpy(pMainT_[j].pSub_, sub, tmp_s*sizeof(subtable_cell));
      delete sub;
      adjustSubTable(j, subtable_cell(x, valueIdx));
      //      finish = clock();
      //      insert_time2_ += finish-start;
      return true;
    }
    else
    {// level-1 hash function is bad.
      //cout<<"insert: level-1 hash function is bad.\n";
      //      finish = clock();
      //      insert_time2_ += finish-start;
      return rehashAll(x, valueIdx);
    }
  }

  virtual bool del(const KeyType& k)
  {
    uint64_t x = str_hash(k);

    update_count_++;
    uint64_t j = hashFunc(main_k_, main_u_,main_p_, mainT_size_, x);
    if (pMainT_[j].pSub_ ==NULL)
      return false;

    uint64_t sub_j = hashFunc(pMainT_[j].k_, pMainT_[j].u_, pMainT_[j].p_, pMainT_[j].s_, x);

    if (pMainT_[j].pSub_!=NULL && pMainT_[j].pSub_[sub_j].data_==x)
    {
      pMainT_[j].pSub_[sub_j].valueIndex_ = -1;
      count_--;
      pMainT_[j].b_--;
    }
    else
      return false;

    if (update_count_>M_)
      return rehashAll(0,-1);//(subtable_cell(0, -1));

    return true;
  }

  using AccessMethod<KeyType, ValueType>::update;//bool update(const KeyType& x, const ValueType& data)
  virtual bool update(const DataType<KeyType,ValueType>& data)
  {
    uint64_t x = str_hash(data.key);

    uint64_t j = hashFunc(main_k_, main_u_,main_p_, mainT_size_, x);

    if (pMainT_[j].pSub_ ==NULL)
      return false;

    uint64_t sub_j = hashFunc(pMainT_[j].k_, pMainT_[j].u_, pMainT_[j].p_, pMainT_[j].s_, x);

    if (pMainT_[j].pSub_!=NULL && pMainT_[j].pSub_[sub_j].data_==x)
    {
      dataVec_[pMainT_[j].pSub_[sub_j].valueIndex_] = data.value;
      return true;
    }

    return false;
  }

  virtual ValueType* find(const KeyType& k)
  {
    uint64_t x = str_hash(k);
    uint64_t j = hashFunc(main_k_, main_u_,main_p_, mainT_size_, x);

    if (pMainT_[j].pSub_ ==NULL || pMainT_[j].b_ == 0)
      return NULL;

    uint64_t sub_j = hashFunc(pMainT_[j].k_, pMainT_[j].u_, pMainT_[j].p_, pMainT_[j].s_, x);

    if (pMainT_[j].pSub_!=NULL && pMainT_[j].pSub_[sub_j].data_==x && pMainT_[j].pSub_[sub_j].valueIndex_!=(uint64_t) -1)
      return &(dataVec_[pMainT_[j].pSub_[sub_j].valueIndex_]);

    return NULL;
  }

friend ostream& operator << ( ostream& os, const SelfType& node)
  {
    for (uint64_t i=0; i<node.mainT_size_; i++)
    {
      os<<"\n-------- "<<i<<" --------\n";
      os<<node.pMainT_[i]<<"==>";
      if (node.pMainT_[i].pSub_ == NULL)
        continue;

      subtable_cell* sub = node.pMainT_[i].pSub_;
      for (uint64_t j=0; j<node.pMainT_[i].s_; j++)
      {
        if (sub[j].valueIndex_ == (uint64_t)-1)
          continue;
        os<<j<<":"<<sub[j]<<", ";
      }

      os<<endl;
    }

    os<<"\ncount_: "<<node.count_<<"  " ;
    os<<"update_count_: "<<node.update_count_<<"  ";
    os<<"M_: "<<node.M_<<"  ";
    os<<"mainT_size_: "<<node.mainT_size_<<"   ";
    os<<"main_k_: "<<node.main_k_<<endl;
    os<<"main_u_: "<<node.main_u_<<endl;

    return os;
  }

  bool save(const string& keyFilename, const string& valueFilename)
  {
    FILE* f = fopen(keyFilename.c_str(), "w+");
    if (f ==NULL)
    {
      cout<<"\nCan't open file: "<<keyFilename<<endl;
      return false;
    }

    uint64_t k = 0;
    uint64_t arr[8] =
      {
        k, count_, update_count_, mainT_size_, M_, main_k_, main_u_, main_p_
      }
    ;

    if (fwrite(arr, sizeof (arr), 1, f)!=1)
      return false;

    for (uint64_t i=0; i<mainT_size_; i++)
    {
      if (pMainT_[i].pSub_ == NULL)
        continue;
      k++;

      if (fwrite(&i, sizeof (uint64_t), 1, f)!=1)
        return false;

      if (!pMainT_[i].save(f))
        return false;

      subtable_cell* sub = pMainT_[i].pSub_;
      for (uint64_t j=0; j<pMainT_[i].s_; j++)
      {
        if (sub[j].valueIndex_ == (uint64_t)-1)
          continue;
        if (!sub[j].save(f))
          return false;
      }

    }

    fseek(f, 0, SEEK_SET);
    if (fwrite(&k, sizeof (uint64_t), 1, f)!=1)
      return false;

    fclose(f);

    ofstream of(valueFilename.c_str());
    oarchive oa(of);
    size_t size = dataVec_.size();
    oa << size;

    for(typename vector<ValueType>::iterator i =dataVec_.begin(); i!=dataVec_.end(); i++)
    {
      oa<<(*i);
    }

    of.close();

    return true;
  }

  bool load(const string& keyFilename, const string& valueFilename)
  {
    FILE* f = fopen(keyFilename.c_str(), "r");
    if (f ==NULL)
    {
      cout<<"\nCan't open file: "<<keyFilename<<endl;
      return false;
    }

    uint64_t k = 0;
    uint64_t arr[8];
    if(fread(arr, sizeof(arr), 1, f)!=1)
      return false;

    k = arr[0];
    count_ = arr[1];
    update_count_ = arr[2];
    mainT_size_ = arr[3];
    M_ = arr[4];
    main_k_ = arr[5];
    main_u_ = arr[6];
    main_p_ = arr[7];

    if (pMainT_ != NULL)
      deleteAll();
    pMainT_ = new table_cell[mainT_size_];

    for (uint64_t i=0; i<k; i++)
    {
      uint64_t u = 0;
      if (fread(&u, sizeof (uint64_t), 1, f)!=1)
      {
        cout<<1<<endl;
        return false;
      }

      if (!pMainT_[u].load(f))
      {
        cout<<2<<endl;
        return false;
      }

      pMainT_[u].pSub_ = new subtable_cell[pMainT_[u].s_];

      for (uint64_t j=0; j<pMainT_[u].b_; j++)
      {
        subtable_cell d;
        if (!d.load(f))
        {
          cout<<3<<endl;
          return false;
        }

        uint64_t y = hashFunc(pMainT_[u].k_, pMainT_[u].u_, pMainT_[u].p_, pMainT_[u].s_, d.data_);
        pMainT_[u].pSub_[y] = d;
      }
      //cout<<*this;
    }

    fclose(f);

    dataVec_.clear();

    ifstream ifs(valueFilename.c_str());
    iarchive ia(ifs);
    size_t size;
    ia>>size;

    for (size_t i =0; i<size; i++)
    {
      ValueType v;
      ia>>v;
      //cout<<v;
      dataVec_.push_back(v);
    }

    ifs.close();
    return true;
  }

protected:
  /**
   *RehashAll(x) is either called by insert(x) or del(x), and then x=-1. rehashAll(x) build a new table for all elements currently in table.
   **/
  bool rehashAll(uint64_t x, uint64_t valueIdx)
  {
    subtable_cell* L = NULL;
    uint64_t idx = 0;

    if (valueIdx != (uint64_t)-1)
      L = new subtable_cell[count_+1];
    else
      L = new subtable_cell[count_];

    //copy T to L
    for (uint64_t i=0; i<mainT_size_; i++)
    {
      if (pMainT_[i].b_== 0)
        continue;

      if (pMainT_[i].pSub_ ==NULL)
      {
        LDBG_<<"rehashAll() 1:"<<x<<valueIdx;
        return false;
      }

      subtable_cell* sub = pMainT_[i].pSub_;
      uint64_t s = pMainT_[i].s_;
      for (uint64_t j=0; j<s; j++)
      {
        if (sub[j].valueIndex_ == (uint64_t)-1)
          continue;

        L[idx] = sub[j];
        idx++;
      }

    }

    if (L==NULL || idx != count_)
    {
      LDBG_<<"rehashAll() 2:"<<x<<valueIdx;
      return false;
    }

    ////////////////////////////////////////////////////////////

    if (valueIdx != (uint64_t)-1)
    {
      L[idx] = subtable_cell(x, valueIdx);
      idx++;
    }
    update_count_ = count_ = idx;

    M_ = (1+c)*(count_>4? count_: 4);

    if (pMainT_ != NULL)
      deleteAll();
    mainT_size_ = M_*SM_O;
    pMainT_ = new table_cell[mainT_size_];

    int t = 0;
    do
    {
      t++;

      if (t>10)
      {
        LDBG_<<"rehashAll() 3:"<<x<<valueIdx;
        t = 0;//warnings:
      }

      random_param(main_k_, main_u_, main_p_);

      for (uint64_t i=0; i<mainT_size_; i++)
        pMainT_[i].b_ = 0;

      for (uint64_t i=0; i<count_; i++)
      {
        uint64_t j= hashFunc(main_k_, main_u_, main_p_, mainT_size_, L[i].data_);
        pMainT_[j].b_++;
        pMainT_[j].m_ = pMainT_[j].b_ *2;
        uint64_t s = pMainT_[j].s_;
        pMainT_[j].s_ = 2*pMainT_[j].m_*(pMainT_[j].m_ - 1);
        sum_s_ = sum_s_ - s + pMainT_[j].s_;
      }
    }while (!condition());

    for (uint64_t i=0; i<mainT_size_; i++)
        pMainT_[i].b_ = 0;

    uint64_t count = count_;
    count_ = 0;
    for (uint64_t i=0; i<count; i++)
    {
      uint64_t j= hashFunc(main_k_, main_u_, main_p_, mainT_size_, L[i].data_);

      if (pMainT_[j].pSub_ == NULL)
      {
        pMainT_[j].pSub_ = new subtable_cell[pMainT_[j].s_];
      }

      subtable_cell* sub = pMainT_[j].pSub_;
      uint64_t u = hashFunc(pMainT_[j].k_, pMainT_[j].u_,pMainT_[j].p_, pMainT_[j].s_, L[i].data_);
      if (sub[u].valueIndex_ != (uint64_t)-1)
      {
        adjustSubTable(j, L[i]);
      }
      else
      {
        sub[u] = L[i];
        pMainT_[j].b_++;
        count_++;
      }

    }

    if (count_ != count)
    {
      LDBG_<<"rehashAll() 4:"<<count<<count_;
      return false;
    }

    delete L;

//     finish = clock();
//     rehash_time_ += finish-start;

    return true;
  }

  bool condition()
  {
    //clock_t start, finish;
    //start= clock();

    if (pMainT_==NULL)return false;

//     uint64_t s = 0;

//     for (uint64_t i=0; i<mainT_size_; i++)
//       s += pMainT_[i].s_;

//     finish = clock();
//     if (sum_s_ != s)
//     {
//       LDBG_<<"condition ERROR: "<<sum_s_<<s;
//     }

    //condition_time_ += finish-start;

    return sum_s_<= (32*M_*M_/mainT_size_ + 4*M_);
  }

  uint64_t str_hash(const string& x)
  {
    uint32_t convkey = 0;
    const char* str = (const char*)x.c_str();
    for (size_t i = 0; i < x.size(); i++)
      convkey = 37*convkey + *str++;

    return convkey;
  }

  uint64_t str_hash(uint64_t x)
  {
    return x;
  }

  uint64_t hashFunc(uint64_t a,uint64_t b, uint64_t p, uint64_t s, const uint64_t& x)
  {
    return ((a*x+b)%p)%s;
  }

  /**
     @brief adjust table when collision happened.
   */
  void adjustSubTable(uint64_t idx, const subtable_cell& x = subtable_cell(0,-1))
  {
    //cout<<idx<<" adjustSubTable()\n";
    //    clock_t start, finish;
    //    start = clock();

    if (idx>=mainT_size_ || idx==(uint64_t)-1 || pMainT_[idx].pSub_ == NULL)
      return;

    subtable_cell* L;
    if (x.valueIndex_ != (uint64_t)-1)
      L = new subtable_cell[pMainT_[idx].b_ + 1];
    else
      L = new subtable_cell[pMainT_[idx].b_];

    uint64_t u = 0;
    subtable_cell* sub = pMainT_[idx].pSub_;
    //////////////copy sub_j to list L;
    for (uint64_t i=0; i<pMainT_[idx].s_; i++)
    {
      if (sub[i].valueIndex_ == (uint64_t)-1)
        continue;

      L[u] = sub[i];
      u++;
      sub[i] = subtable_cell(0, -1);
    }

    if (u != pMainT_[idx].b_)
    {
      LDBG_<<"adjustSubTable() 1:"<<pMainT_[idx].b_<<u;
      return;
    }

    if (x.valueIndex_ != (uint64_t)-1)
    {
      L[u] = x;
      u++;
      count_++;
      pMainT_[idx].b_++;
    }

    bool well = false;
    int t = 0;
    while (!well)
    {
      t++;
      if (t>10)
      {
        LDBG_<<"adjustSubTable() 2: Warning..."<<idx;
        t = 0;//there are warnings
      }

      random_param(pMainT_[idx].k_, pMainT_[idx].u_, pMainT_[idx].p_);

      for (uint64_t i=0; i<pMainT_[idx].s_; i++)
        sub[i] = subtable_cell(0, -1);

      uint64_t i=0;
      for (; i<u; i++)
      {
        uint64_t j= hashFunc(pMainT_[idx].k_, pMainT_[idx].u_, pMainT_[idx].p_, pMainT_[idx].s_, L[i].data_ );
        //cout<<u<<"  "<<pMainT_[idx].k_<<" "<<pMainT_[idx].u_<<" "<<sub[j].data_<<" "<<L[i].data_<<endl;
        if(sub[j].valueIndex_ != (uint64_t)-1)
        {
          break;
        }

        sub[j] = L[i];
      }

      if (i==u)
        well = true;
    }

    delete L;

    //    finish = clock();
    //    adjust_time_ += finish - start;

  }

  void deleteAll()
  {
    for (uint64_t i=0; i<mainT_size_; i++)
      deleteSubT(i);

    delete pMainT_;
    pMainT_ = NULL;
  }

  void deleteSubT(uint64_t idx)
  {
    if( pMainT_[idx].pSub_!=NULL)
      {
        delete pMainT_[idx].pSub_;
        pMainT_[idx].pSub_ = NULL;
      }

  }

  static void random_param(uint64_t& a, uint64_t& b, uint64_t& p)
  {
    p = rand();
    a = rand()%p;
    b = rand()%p;
  }

protected:
  table_cell* pMainT_;//!< entry pointer
  uint64_t count_; //!< record count
  uint64_t update_count_;//!< times of updates
  uint64_t M_; //!< used for upper bounds of update times
  uint64_t mainT_size_;//!< main table size
  uint64_t main_k_;//!< 3 parameters for main table
  uint64_t main_u_;//!< 3 parameters for main table
  uint64_t main_p_;//!< 3 parameters for main table
  vector<ValueType> dataVec_;//!< used for storing value field
  uint64_t sum_s_;//!< sumary of 2 parameters.

//   clock_t adjust_time_;
//   clock_t rehash_time_;
//   clock_t insert_time1_;
//   clock_t insert_time2_;
//   clock_t condition_time_;
}
  ;

NS_IZENELIB_AM_END
#endif


// Copyright 2009 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Authors: Nicholas Edmonds
//           Andrew Lumsdaine

#include <boost/random.hpp>
#include <boost/test/minimal.hpp>

#include <boost/graph/rmat_graph_generator.hpp>
#include <boost/graph/small_world_generator.hpp>
#include <boost/graph/ssca_graph_generator.hpp>
#include <boost/graph/erdos_renyi_generator.hpp>
#include <boost/graph/mesh_graph_generator.hpp>

#include <boost/graph/adjacency_list.hpp>

using namespace boost;

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

  typedef rand48 RandomGenerator;

  typedef adjacency_list<vecS, vecS, directedS> Graph;

  RandomGenerator gen;

  size_t N = 100;
  size_t M = 1000;
  double p = 0.05;

  // Test Erdos-Renyi generator
  {
    erdos_renyi_iterator<RandomGenerator, Graph> start(gen, N, p);
    erdos_renyi_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  {
    sorted_erdos_renyi_iterator<RandomGenerator, Graph> start(gen, N, p);
    sorted_erdos_renyi_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  // Test Small World generator
  {
    small_world_iterator<RandomGenerator, Graph> start(gen, N, M, p);
    small_world_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  // Test SSCA generator
  {
    ssca_iterator<RandomGenerator, Graph> start(gen, N, 5, 0.5, 5, p);
    ssca_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  // Test Mesh generator
  {
    mesh_iterator<Graph> start(N, N);
    mesh_iterator<Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  // Test R-MAT generator
  double a = 0.57, b = 0.19, c = 0.19, d = 0.05;

  {
    rmat_iterator<RandomGenerator, Graph> start(gen, N, M, a, b, c, d);
    rmat_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  {
    unique_rmat_iterator<RandomGenerator, Graph> start(gen, N, M, a, b, c, d);
    unique_rmat_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  {
    sorted_unique_rmat_iterator<RandomGenerator, Graph> start(gen, N, M, a, b, c, d);
    sorted_unique_rmat_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  {
    sorted_unique_rmat_iterator<RandomGenerator, Graph> start(gen, N, M, a, b, c, d, true);
    sorted_unique_rmat_iterator<RandomGenerator, Graph> end;

    while (start != end) ++start;

    BOOST_CHECK(start == end);
  }

  return 0;
}


// -----------------------------------------------------------------------------
// Fern © Geoneric
//
// This file is part of Geoneric Fern which is available under the terms of
// the GNU General Public License (GPL), version 2. If you do not want to
// be bound by the terms of the GPL, you may purchase a proprietary license
// from Geoneric (http://www.geoneric.eu/contact).
// -----------------------------------------------------------------------------
#define BOOST_TEST_MODULE fern algorithm statistic unary_min
#include <boost/test/unit_test.hpp>
#include "fern/core/data_customization_point/scalar.h"
#include "fern/feature/core/data_customization_point/array.h"
#include "fern/feature/core/data_customization_point/masked_array.h"
#include "fern/feature/core/data_customization_point/masked_scalar.h"
#include "fern/algorithm/core/test/test_utils.h"
#include "fern/algorithm/statistic/unary_min.h"

namespace f = fern;
namespace fa = f::algorithm;
namespace ft = f::test;

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_0d_0d(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    int result_we_got{-9};
    fa::statistic::unary_min<>(execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_0d_0d(
    ExecutionPolicy& execution_policy)
{
    {
        int value{5};
        int result_we_want{5};
        verify_0d_0d(execution_policy, value, result_we_want);
    }

    {
        int value{-5};
        int result_we_want{-5};
        verify_0d_0d(execution_policy, value, result_we_want);
    }

    {
        int value{0};
        int result_we_want{0};
        verify_0d_0d(execution_policy, value, result_we_want);
    }
}

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_0d_0d_masked(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    using InputNoDataPolicy = fa::InputNoDataPolicies<
        fa::DetectNoDataByValue<bool>>;
    using OutputNoDataPolicy = fa::MarkNoDataByValue<bool>;

    f::MaskedScalar<int> result_we_got{-9};

    InputNoDataPolicy input_no_data_policy{{value.mask(), true}};
    OutputNoDataPolicy output_no_data_policy{result_we_got.mask(), true};

    fa::statistic::unary_min<>(input_no_data_policy, output_no_data_policy,
        execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_0d_0d_masked(
    ExecutionPolicy& execution_policy)
{
    // Regular case.
    {
        f::MaskedScalar<int> value{5};
        f::MaskedScalar<int> result_we_want{5};
        verify_0d_0d_masked(execution_policy, value, result_we_want);
    }

    // Mask a value.
    {
        f::MaskedScalar<int> value{5, true};
        f::MaskedScalar<int> result_we_want{-9, true};
        verify_0d_0d_masked(execution_policy, value, result_we_want);
    }
}

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_1d_0d(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    int result_we_got{-9};
    fa::statistic::unary_min<>(execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_1d_0d(
    ExecutionPolicy& execution_policy)
{
    // Regular case.
    {
        f::Array<int, 1> value(ft::nr_elements_1d);
        std::iota(value.data(), value.data() + ft::nr_elements_1d, 5);
        int result_we_want{5};
        verify_1d_0d(execution_policy, value, result_we_want);
    }

    // Empty.
    {
        f::Array<int, 1> value(0);
        int result_we_want{-9};
        verify_1d_0d(execution_policy, value, result_we_want);
    }
}

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_1d_0d_masked(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    using InputNoDataPolicy = fa::InputNoDataPolicies<
        fa::DetectNoDataByValue<f::Mask<1>>>;
    using OutputNoDataPolicy = fa::MarkNoDataByValue<bool>;

    f::MaskedScalar<int> result_we_got{-9};

    InputNoDataPolicy input_no_data_policy{{value.mask(), true}};
    OutputNoDataPolicy output_no_data_policy(result_we_got.mask(), true);

    fa::statistic::unary_min<>(input_no_data_policy, output_no_data_policy,
        execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_1d_0d_masked(
    ExecutionPolicy& execution_policy)
{
    // Regular case.
    {
        f::MaskedArray<int, 1> value(ft::nr_elements_1d);
        std::iota(value.data(), value.data() + ft::nr_elements_1d, 5);
        f::MaskedScalar<int> result_we_want{5};
        verify_1d_0d_masked(execution_policy, value, result_we_want);
    }

    // Mask a value.
    {
        f::MaskedArray<int, 1> value(ft::nr_elements_1d);
        std::iota(value.data(), value.data() + ft::nr_elements_1d, 5);
        get(value.mask(), 5) = true;
        f::MaskedScalar<int> result_we_want{5};
        verify_1d_0d_masked(execution_policy, value, result_we_want);
    }

    // Mask all values.
    {
        f::MaskedArray<int, 1> value(ft::nr_elements_1d);
        value.mask().fill(true);
        f::MaskedScalar<int> result_we_want{-9, true};
        verify_1d_0d_masked(execution_policy, value, result_we_want);
    }

    // Empty.
    {
        f::MaskedArray<int, 1> value(0);
        f::MaskedScalar<int> result_we_want{-9, true};
        verify_1d_0d_masked(execution_policy, value, result_we_want);
    }
}

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_2d_0d(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    int result_we_got{-9};
    fa::statistic::unary_min<>(execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_2d_0d(
    ExecutionPolicy& execution_policy)
{
    // Regular case.
    {
        f::Array<int, 2> value(f::extents[ft::nr_rows][ft::nr_cols]);
        std::iota(value.data(), value.data() + ft::nr_elements_2d, 5);
        int result_we_want{5};
        verify_2d_0d(execution_policy, value, result_we_want);
    }

    // Empty.
    {
        f::Array<int, 2> value(f::extents[0][0]);
        int result_we_want{-9};
        verify_2d_0d(execution_policy, value, result_we_want);
    }
}

template<
    typename ExecutionPolicy,
    typename Argument,
    typename Result>
void verify_2d_0d_masked(
    ExecutionPolicy& execution_policy,
    Argument const& value,
    Result const& result_we_want)
{
    using InputNoDataPolicy = fa::InputNoDataPolicies<
        fa::DetectNoDataByValue<f::Mask<2>>>;
    using OutputNoDataPolicy = fa::MarkNoDataByValue<bool>;

    f::MaskedScalar<f::value_type<Result>> result_we_got{-9};

    InputNoDataPolicy input_no_data_policy{{value.mask(), true}};
    OutputNoDataPolicy output_no_data_policy(result_we_got.mask(), true);

    fa::statistic::unary_min<>(input_no_data_policy, output_no_data_policy,
        execution_policy, value, result_we_got);
    BOOST_CHECK_EQUAL(result_we_got, result_we_want);
}

template<
    typename ExecutionPolicy>
void test_2d_0d_masked(
    ExecutionPolicy& execution_policy)
{
    // Regular case.
    {
        f::MaskedArray<int, 2> value(f::extents[ft::nr_rows][ft::nr_cols]);
        std::iota(value.data(), value.data() + ft::nr_elements_2d, 5);
        f::MaskedScalar<int> result_we_want{5};
        verify_2d_0d(execution_policy, value, result_we_want);
    }

    // Mask a value.
    {
        f::MaskedArray<int, 2> value(f::extents[ft::nr_rows][ft::nr_cols]);
        std::iota(value.data(), value.data() + ft::nr_elements_2d, 5);
        get(value.mask(), 5) = true;
        f::MaskedScalar<int> result_we_want{5};
        verify_2d_0d_masked(execution_policy, value, result_we_want);
    }

    // Mask all values.
    {
        f::MaskedArray<int, 2> value(f::extents[ft::nr_rows][ft::nr_cols]);
        value.mask().fill(true);
        f::MaskedScalar<int> result_we_want{-9, true};
        verify_2d_0d_masked(execution_policy, value, result_we_want);
    }

    // Empty.
    {
        f::MaskedArray<int, 2> value(f::extents[0][0]);
        f::MaskedScalar<int> result_we_want{-9, true};
        verify_2d_0d_masked(execution_policy, value, result_we_want);
    }
}

FERN_UNARY_AGGREGATE_TEST_CASES()

BOOST_AUTO_TEST_CASE(gh59)
{
    fa::SequentialExecutionPolicy sequential;

    f::MaskedArray<int, 2> value = {
        { 0, 0 },
        { 5, 0 },
    };
    value.mask() = {
        {  true, true },
        { false, true },
    };

    f::MaskedScalar<int> result_we_want{5};
    verify_2d_0d_masked(sequential, value, result_we_want);
}

BOOST_AUTO_TEST_CASE(gh61)
{
    fa::SequentialExecutionPolicy sequential;

    f::MaskedArray<int, 2> value = {
        { -9, -9, -9, -9, -9, -9 },
        { -9, -9,  8,  9, -9, -9 },
        { -9, 13, 14, 15, 16, -9 },
        { 18, 19, 20, 21, 22, 23 },
        { -9, 25, 26, 27, 28, -9 },
        { -9, -9, 32, 33, -9, -9 },
        { -9, -9, 38, -9, -9, -9 },
    };
    value.mask() = {
        { true,  true,  true,  true,  true,  true  },
        { true,  true,  false, false, true,  true  },
        { true,  false, false, false, false, true  },
        { false, false, false, false, false, false },
        { true,  false, false, false, false, true  },
        { true,  true,  false, false, true,  true  },
        { true,  true,  false, true,  true,  true  },
    };

    {
        f::MaskedScalar<int> result_we_want{8};
        verify_2d_0d_masked(sequential, value, result_we_want);
    }

    for(size_t n = 1; n <= 8; ++n) {
        f::MaskedScalar<int> result_we_want{8};
        fa::ParallelExecutionPolicy parallel{n};
        verify_2d_0d_masked(parallel, value, result_we_want);
    }
}


/// \file   test_avellaneda_stoikov.cpp
///
/// \brief
///
/// \authors    Maarten P. Scholl
/// \date       2020-06-24
/// \copyright  Copyright 2017-2019 The Institute for New Economic Thinking,
///             Oxford Martin School, University of Oxford
///
///             Licensed under the Apache License, Version 2.0 (the "License");
///             you may not use this file except in compliance with the License.
///             You may obtain a copy of the License at
///
///                 http://www.apache.org/licenses/LICENSE-2.0
///
///             Unless required by applicable law or agreed to in writing,
///             software distributed under the License is distributed on an "AS
///             IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
///             express or implied. See the License for the specific language
///             governing permissions and limitations under the License.
///
///             You may obtain instructions to fulfill the attribution
///             requirements in CITATION.cff
///

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE avellaneda_stoikov

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

#include <esl/economics/markets/order_book/avellaneda_stoikov.hpp>
using namespace esl::economics::markets::order_book;

#include <esl/economics/currencies.hpp>

///
/// \detail Use strict comparison because the chosen vanlues are far enough
///         apart
///
BOOST_AUTO_TEST_SUITE(ESL)

    BOOST_AUTO_TEST_CASE(bid_ask)
    {

        for(auto m: std::vector<double>{0.01, 1.00, 100.00, 1'000'000'000.00}){

            auto ba_ = esl::economics::markets::order_book::avellaneda_stoikov( esl::economics::price(m, esl::economics::currencies::USD)
                , esl::quantity(0)
                , esl::quantity(0)
                , 0.
                , 0.20 * m
                , 0.01
                , 1.5 );

            BOOST_CHECK_LT(ba_.first, ba_.second);
        }
    }

    BOOST_AUTO_TEST_CASE(volatility)
    {
        auto middle_ = esl::economics::price(100.00, esl::economics::currencies::USD);

        auto low_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.
            , 0.10 * double(middle_)       // <----
            , 0.01
            , 1.5 );

        auto high_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.
            , 0.30 * double(middle_)      // <----
            , 0.01
            , 1.5 );

        BOOST_CHECK_GT(low_.first, high_.first);
        BOOST_CHECK_LT(low_.second, high_.second);
    }

    BOOST_AUTO_TEST_CASE(risk_aversion)
    {
        auto middle_ = esl::economics::price(100.00, esl::economics::currencies::USD);

        auto low_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.
            , 0.20 * double(middle_)
            , 0.01                                 // <----
            , 1.5 );

        auto high_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.
            , 0.20 * double(middle_)
            , 0.1                                 // <----
            , 1.5 );

        BOOST_CHECK_GT(low_.first, high_.first);
        BOOST_CHECK_LT(low_.second, high_.second);
    }

    BOOST_AUTO_TEST_CASE(end_of_session)
    {
        auto middle_ = esl::economics::price( 100.00
                                            , esl::economics::currencies::USD);

        auto low_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.1                         // <----
            , 0.20 * double(middle_)
            , 0.01
            , 1.5 );

        auto high_ = esl::economics::markets::order_book::avellaneda_stoikov( middle_
            , esl::quantity(0)
            , esl::quantity(0)
            , 0.9                         // <----
            , 0.20 * double(middle_)
            , 0.01
            , 1.5 );

        BOOST_CHECK_LT(low_.first, high_.first);
        BOOST_CHECK_GT(low_.second, high_.second);
    }

BOOST_AUTO_TEST_SUITE_END()  // ESL


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/51-100/56/problem56.hpp"

BOOST_AUTO_TEST_SUITE( Problem56 )

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem56::solve();
        BOOST_CHECK_EQUAL(res, 972);
    }

BOOST_AUTO_TEST_SUITE_END()


/*
 * common.hpp
 *
 *  Created on: Aug 18, 2021
 *      Author: jelavice
 */

#pragma once
#include <Eigen/Core>

namespace tree_detection {

struct ClusterDimensions
{
  double dimX_;
  double dimY_;
  double dimZ_;
};

struct Cluster
{
  Eigen::Vector3d position_;
  ClusterDimensions clusterDimensions_;
};

struct RPY
{
  double roll_;
  double pitch_;
  double yaw_;
};

} // tree detection


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/arithmetic/include/functions/toints.hpp>
#include <boost/dispatch/functor/meta/call.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <boost/simd/include/constants/inf.hpp>
#include <boost/simd/include/constants/minf.hpp>
#include <boost/simd/include/constants/nan.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/constants/mone.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/valmin.hpp>
#include <boost/simd/include/functions/ldexp.hpp>

NT2_TEST_CASE_TPL ( toints_real,  BOOST_SIMD_REAL_TYPES)
{

  using boost::simd::toints;
  using boost::simd::tag::toints_;
  typedef typename boost::dispatch::meta::call<toints_(T)>::type      r_t;
  typedef typename boost::dispatch::meta::as_integer<T, signed>::type wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
  NT2_TEST_EQUAL(toints(T(2)*boost::simd::Valmax<r_t>()),  boost::simd::Valmax<r_t>());
  NT2_TEST_EQUAL(toints(T(2)*boost::simd::Valmin<r_t>()),  boost::simd::Valmin<r_t>());
  NT2_TEST_EQUAL(toints(T(1.5)*boost::simd::Valmax<r_t>()),  boost::simd::Valmax<r_t>());
  NT2_TEST_EQUAL(toints(T(1.5)*boost::simd::Valmin<r_t>()),  boost::simd::Valmin<r_t>());

  NT2_TEST_EQUAL(toints(boost::simd::Inf<T>()),  boost::simd::Inf<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Minf<T>()), boost::simd::Minf<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Mone<T>()), boost::simd::Mone<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Nan<T>()),  boost::simd::Zero<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::One<T>()),  boost::simd::One<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Zero<T>()), boost::simd::Zero<r_t>());

  T v = T(1);
  wished_r_t iv = 1;
  int N = sizeof(T)*8-1;
  for(int i=0; i < N ; i++, v*= 2, iv <<= 1)
  {
    NT2_TEST_EQUAL(toints(v), iv);
    NT2_TEST_EQUAL(toints(-v), -iv);
  }
  NT2_TEST_EQUAL(toints(boost::simd::ldexp(boost::simd::One<T>(), N)), boost::simd::Valmax<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::ldexp(boost::simd::One<T>(), N+1)), boost::simd::Valmax<r_t>());
  NT2_TEST_EQUAL(toints(-boost::simd::ldexp(boost::simd::One<T>(), N+1)), boost::simd::Valmin<r_t>());
  NT2_TEST_EQUAL(toints(-boost::simd::ldexp(boost::simd::One<T>(), N+1)), boost::simd::Valmin<r_t>());

} // end of test for floating_

NT2_TEST_CASE_TPL ( toints_unsigned_int,  BOOST_SIMD_UNSIGNED_TYPES)
{

  using boost::simd::toints;
  using boost::simd::tag::toints_;
  typedef typename boost::dispatch::meta::call<toints_(T)>::type      r_t;
  typedef typename boost::dispatch::meta::as_integer<T, signed>::type wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
  NT2_TEST_EQUAL(toints(boost::simd::One<T>()),  boost::simd::One<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Zero<T>()), boost::simd::Zero<r_t>());
} // end of test for unsigned_int_

NT2_TEST_CASE_TPL ( toints_signed,  BOOST_SIMD_INTEGRAL_SIGNED_TYPES)
{

  using boost::simd::toints;
  using boost::simd::tag::toints_;
  typedef typename boost::dispatch::meta::call<toints_(T)>::type      r_t;
  typedef typename boost::dispatch::meta::as_integer<T, signed>::type wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
  NT2_TEST_EQUAL(toints(boost::simd::Mone<T>()), boost::simd::Mone<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::One<T>()),  boost::simd::One<r_t>());
  NT2_TEST_EQUAL(toints(boost::simd::Zero<T>()), boost::simd::Zero<r_t>());
} // end of test for signed_int_


// Copyright (c) 2015
// Author: Chrono Law
#include <std.hpp>
using namespace std;

#include <boost/lexical_cast.hpp>
using namespace boost;

//////////////////////////////////////////

void case1()
{
    int  x      = lexical_cast<int>("100");
    long y      = lexical_cast<long>("2000");
    float pai   = lexical_cast<float>("3.14159e5");
    double e    = lexical_cast<double>("2.71828");
    double r    = lexical_cast<double>("1.414,xyz", 5);

    cout << x << y << pai << e << r << endl;

    string str = lexical_cast<string>(456);
    cout << str << endl;

    cout << lexical_cast<string>(0.618) << endl;
    cout << lexical_cast<string>(0x10) << endl;

    cout << lexical_cast<bool>("1") << endl;

    //lexical_cast<int>("0x100");
    //lexical_cast<int>("123L");
}

//////////////////////////////////////////

template<typename T>
bool num_valid(const char *str)
{
    T tmp;
    return conversion::try_lexical_convert(str, tmp);
}
//try
//{
//    lexical_cast<T>(str);
//    return true;
//}
//catch(bad_lexical_cast&)
//{
//    return false;
//};

void case2()
try
{
    assert( num_valid<double>("3.14"));
    assert(!num_valid<int>("3.14"));
    assert( num_valid<int>("65535"));

    int x;
    assert(!conversion::try_lexical_convert("0x100", x));

    cout << lexical_cast<int>("0x100");
    cout << lexical_cast<double>("HelloWorld");
    cout << lexical_cast<long>("1000L");
    cout << lexical_cast<bool>("false") << endl;
}
catch (bad_lexical_cast& e)
{
        cout << "error:" << e.what() << endl;
}

//////////////////////////////////////////
template<typename T>
struct outable
{
    friend std::ostream& operator<<(std::ostream& os, const T& x)
    {
        os << typeid(T).name();
        return os;
    }
};

class demo_class : public outable<demo_class>
{
//    friend std::ostream& operator<<(std::ostream& os, const demo_class& x)
//    {
//        os << "demo_class's Name";
//        return os;
//    }
};
void case3()
{
    cout << lexical_cast<string>(demo_class()) << endl;
}

//////////////////////////////////////////

void case4()
try
{
    assert(stoi(" 42 ") == 42);
    assert(stol("100L") == 100L);
    assert(stol("1000 9") == 1000L);
    assert(stod("3.14ispai") == 3.14);

    assert(to_string(776ul) == "776");

    //cout << stoul("100L");
    //cout << stoul("x100");
    cout << stoi("9999999999");
}
catch(std::exception& e)
{
    cout << "error:" << e.what() << endl;
}

//////////////////////////////////////////
template<typename T>
T std_lexical_cast(const std::string& s);

template<>
int std_lexical_cast<int>(const std::string& s)
{
    return stoi(s);
}

template<>
long std_lexical_cast<long>(const std::string& s)
{
    return stol(s);
}

void case5()
{
    assert(std_lexical_cast<int>(" 10 ") == 10);
    assert(std_lexical_cast<long>("100L") == 100L);
}

//////////////////////////////////////////

int main()
{
    case1();
    case2();
    case3();
    case4();
    case5();
}


#include <hops/FileReader/CsvReader.hpp>
#include <Eigen/Core>
#include <Eigen/Sparse>

template<typename VectorType>
VectorType hops::CsvReader::readVector(const std::string &file) {
    std::ifstream fileStream(file);
    if (fileStream.fail()) {
        throw std::runtime_error("Could not access file: " + file);
    }
    std::vector<std::string> cells;
    std::string line;
    while (std::getline(fileStream, line)) {
        auto const position = line.find_last_of(',');
        std::string cell = line.substr(position + 1);
        if (!cell.empty()) {
            cells.push_back(cell);
        }
    }

    size_t startIndex = 0;
    try {
        std::stod(cells[0]);
    }
    catch (std::invalid_argument &) {
        startIndex = 1;
    }
    VectorType result(cells.size() - startIndex);
    for (size_t i = startIndex; i < cells.size(); ++i) {
        result(i - startIndex) = std::stod(cells[i]);
    }
    return result;
}

template Eigen::VectorXi hops::CsvReader::readVector(const std::string &file);

template Eigen::Matrix<long, Eigen::Dynamic, 1> hops::CsvReader::readVector(const std::string &file);

template Eigen::VectorXf hops::CsvReader::readVector(const std::string &file);

template Eigen::VectorXd hops::CsvReader::readVector(const std::string &file);

template<typename MatrixType>
MatrixType hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames) {

    std::ifstream fileStream(file);
    if (fileStream.fail()) {
        throw std::runtime_error("Could not access file: " + file);
    }
    std::string line;
    std::vector<std::vector<std::string>> stringRepresentationOfMatrix;
    while (std::getline(fileStream, line)) {
        std::stringstream lineStream(line);
        std::vector<std::string> cells;
        std::string cell;
        while (std::getline(lineStream, cell, ',')) {
            cells.push_back(cell);
        }
        stringRepresentationOfMatrix.emplace_back(cells);
    }

    size_t numberOfRows = hasColumnAndRowNames ? stringRepresentationOfMatrix.size() - 1
                                               : stringRepresentationOfMatrix.size();
    size_t numberOfColumns = hasColumnAndRowNames ? stringRepresentationOfMatrix.at(0).size() - 1
                                                  : stringRepresentationOfMatrix.at(0).size();
    Eigen::SparseMatrix<typename MatrixType::Scalar> result(numberOfRows, numberOfColumns);
    std::vector<Eigen::Triplet<typename MatrixType::Scalar>> triplets;

    for (size_t i = 0; i < numberOfRows; ++i) {
        for (size_t j = 0; j < numberOfColumns; ++j) {
            size_t row = hasColumnAndRowNames ? i + 1 : i;
            size_t col = hasColumnAndRowNames ? j + 1 : j;
            typename MatrixType::Scalar value = std::stod(stringRepresentationOfMatrix.at(row).at(col));
            if (value != 0) {
                triplets.emplace_back(i, j, value);
            }
        }
    }
    result.setFromTriplets(triplets.begin(), triplets.end());
    result.makeCompressed();
    return result;
}

template Eigen::MatrixXi hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::Matrix<long, Eigen::Dynamic, Eigen::Dynamic>
hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::MatrixXf hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::MatrixXd hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::SparseMatrix<int> hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::SparseMatrix<long> hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::SparseMatrix<float> hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);

template Eigen::SparseMatrix<double> hops::CsvReader::readMatrix(const std::string &file, bool hasColumnAndRowNames);


#include <boost/numeric/conversion/sign_mixture.hpp>


// Copyright 2015-2018 Hans Dembinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <benchmark/benchmark.h>
#include <atomic>
#include <boost/histogram.hpp>
#include <boost/histogram/algorithm/sum.hpp>
#include <chrono>
#include <functional>
#include <numeric>
#include <thread>
#include <vector>

namespace bh = boost::histogram;

template <typename T>
class copyable_atomic : public std::atomic<T> {
public:
  using std::atomic<T>::atomic;

  // zero-initialize the atomic T
  copyable_atomic() noexcept : std::atomic<T>(T()) {}

  // this is potentially not thread-safe, see below
  copyable_atomic(const copyable_atomic& rhs) : std::atomic<T>() { this->operator=(rhs); }

  // this is potentially not thread-safe, see below
  copyable_atomic& operator=(const copyable_atomic& rhs) {
    if (this != &rhs) { std::atomic<T>::store(rhs.load()); }
    return *this;
  }
};

template <class Histogram>
void fill_histogram(Histogram& h, unsigned ndata) {
  using namespace std::chrono_literals;
  for (unsigned i = 0; i < ndata; ++i) {
    std::this_thread::sleep_for(10ns); // simulate some work
    h(double(i) / ndata);
  }
}

static void NoThreads(benchmark::State& state) {
  auto h =
      bh::make_histogram_with(std::vector<unsigned>(), bh::axis::regular<>(100, 0, 1));
  const unsigned ndata = state.range(0);
  for (auto _ : state) {
    state.PauseTiming();
    h.reset();
    state.ResumeTiming();
    fill_histogram(h, ndata);
    state.PauseTiming();
    assert(ndata == std::accumulate(h.begin(), h.end(), 0.0));
    state.ResumeTiming();
  }
}

static void AtomicStorage(benchmark::State& state) {
  auto h = bh::make_histogram_with(std::vector<copyable_atomic<unsigned>>(),
                                   bh::axis::regular<>(100, 0, 1));

  const unsigned nthreads = state.range(0);
  const unsigned ndata = state.range(1);
  for (auto _ : state) {
    state.PauseTiming();
    h.reset();
    std::vector<std::thread> pool;
    pool.reserve(nthreads);
    state.ResumeTiming();
    for (unsigned i = 0; i < nthreads; ++i)
      pool.emplace_back(fill_histogram<decltype(h)>, std::ref(h), ndata / nthreads);
    for (auto&& t : pool) t.join();
    state.PauseTiming();
    assert(ndata == std::accumulate(h.begin(), h.end(), 0.0));
    state.ResumeTiming();
  }
}

BENCHMARK(NoThreads)->RangeMultiplier(2)->Range(8 << 3, 8 << 5);
BENCHMARK(AtomicStorage)->RangeMultiplier(2)->Ranges({{1, 4}, {8 << 3, 8 << 5}});


#include <utilities/log.h>
#include <utilities/containers.h>
#include <utilities/property-maps.h>

#include <boost/utility/enable_if.hpp>
#include <boost/utility.hpp>
#include <boost/foreach.hpp>

#include <boost/foreach.hpp>

#ifdef LOGGING
static rlog::RLogChannel* rlPersistence =                   DEF_CHANNEL("topology/persistence", rlog::Log_Debug);
#endif // LOGGING

#ifdef COUNTERS
static Counter*  cPersistencePair =                         GetCounter("persistence/pair");
static Counter*  cPersistencePairBoundaries =               GetCounter("persistence/pair/boundaries");
static Counter*  cPersistencePairCycleLength =              GetCounter("persistence/pair/cyclelength");
#endif // COUNTERS

template<class D, class CT, class OT, class E, class Cmp>
template<class Filtration>
void
StaticPersistence<D, CT, OT, E, Cmp>::
initialize(const Filtration& filtration)
{
    order_.assign(filtration.size(), OrderElement());
    rLog(rlPersistence, "Initializing persistence");

    OffsetMap<typename Filtration::Index, iterator>                         om(filtration.begin(), begin());
    for (typename Filtration::Index cur = filtration.begin(); cur != filtration.end(); ++cur)
    {
        Cycle z;
        BOOST_FOREACH(const typename Filtration::Simplex& s, std::make_pair(cur->boundary_begin(), cur->boundary_end()))
            z.push_back(index(om[filtration.find(s)]));
        z.sort(ocmp_);

        iterator ocur = om[cur];
        swap_cycle(ocur, z);
        set_pair(ocur,   ocur);
    }
}

template<class D, class CT, class OT, class E, class Cmp>
void
StaticPersistence<D, CT, OT, E, Cmp>::
pair_simplices(bool progress)
{
    if (progress)
    {
        PairVisitor visitor(size());
        pair_simplices<PairVisitor>(begin(), end(), false, visitor);
    }
    else
    {
        PairVisitorNoProgress visitor;
        pair_simplices<PairVisitorNoProgress>(begin(), end(), false, visitor);
    }
}

template<class D, class CT, class OT, class E, class Cmp>
template<class Visitor>
void
StaticPersistence<D, CT, OT, E, Cmp>::
pair_simplices(iterator bg, iterator end, bool store_negative, const Visitor& visitor)
{
#if LOGGING
    typename ContainerTraits::OutputMap outmap(order_);
#endif

    // FIXME: need sane output for logging
    rLog(rlPersistence, "Entered: pair_simplices");
    for (iterator j = bg; j != end; ++j)
    {
        visitor.init(j);
        rLog(rlPersistence, "Simplex %s", outmap(j).c_str());

        Cycle z;
        swap_cycle(j, z);
        rLog(rlPersistence, "  has boundary: %s", z.tostring(outmap).c_str());

        // Sparsify the cycle by removing the negative elements
        if (!store_negative)
        {
            typename OrderElement::Cycle zz;
            BOOST_FOREACH(OrderIndex i, z)
                if (i->sign())           // positive
                    zz.push_back(i);
            z.swap(zz);
        }
        // --------------------------

        CountNum(cPersistencePairBoundaries, z.size());
#ifdef COUNTERS
        Count(cPersistencePair);
#endif // COUNTERS

        while(!z.empty())
        {
            OrderIndex i = z.top(ocmp_);            // take the youngest element with respect to the OrderComparison
            rLog(rlPersistence, "  %s: %s", outmap(i).c_str(), outmap(i->pair).c_str());
            // TODO: is this even a meaningful assert?
            AssertMsg(!ocmp_(i, index(j)),
                      "Simplices in the cycle must precede current simplex: (%s in cycle of %s)",
                      outmap(i).c_str(), outmap(j).c_str());

            // i is not paired, so we pair j with i
            if (iterator_to(i->pair) == iterator_to(i))
            {
                rLog(rlPersistence, "  Pairing %s and %s with cycle %s",
                                   outmap(i).c_str(), outmap(j).c_str(),
                                   z.tostring(outmap).c_str());

                set_pair(i, j);
                swap_cycle(j, z);
                set_pair(j, i);

                CountNum(cPersistencePairCycleLength,   j->cycle.size());
                CountBy (cPersistencePairCycleLength,   j->cycle.size());
                break;
            }

            // update element
            z.add(i->pair->cycle, ocmp_);
            visitor.update(j, iterator_to(i));
            rLog(rlPersistence, "    new cycle: %s", z.tostring(outmap).c_str());
        }
        // if z was empty, so is (already) j->cycle, so nothing to do
        visitor.finished(j);
        rLog(rlPersistence, "Finished with %s: %s",
                            outmap(j).c_str(), outmap(j->pair).c_str());
    }
}

template<class D, class CT, class OT, class E, class Cmp>
template<class Filtration>
class StaticPersistence<D, CT, OT, E, Cmp>::SimplexMap
{
    public:
        typedef                             OrderIndex                              key_type;
        // typedef                             iterator                                key_type;
        typedef                             const typename Filtration::Simplex&     value_type;

                                            SimplexMap(const StaticPersistence& persistence,
                                                       const Filtration&        filtration):
                                                persistence_(persistence),
                                                filtration_(filtration)             {}

        value_type                          operator[](OrderIndex k) const          { return (*this)[persistence_.iterator_to(k)]; }
        value_type                          operator[](iterator i) const            { return filtration_.simplex(filtration_.begin() + (i - persistence_.begin())); }

    private:
        const StaticPersistence&            persistence_;
        const Filtration&                   filtration_;
};

/* Private */


#include "DependencyHelpers.hpp"
#include "MtspModel.hpp"
#include "TsplpExceptions.hpp"

#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/transitive_closure.hpp>

namespace
{
    struct CycleDetector : public boost::dfs_visitor<>
    {
        void back_edge(auto, auto)
        {
            throw tsplp::CyclicDependenciesException{};
        }
    };

    template <typename Graph>
    void ThrowOnCycle(const Graph& graph)
    {
        boost::depth_first_search(graph, visitor(CycleDetector{}));
    }
}

namespace tsplp
{
    xt::xtensor<int, 2> CreateTransitiveDependencies(xt::xtensor<int, 2> weights)
    {
        boost::adjacency_list<> dependencyGraph;
        for (const auto [v, u] : xt::argwhere(equal(weights, -1)))
            add_edge(u, v, dependencyGraph);

        ThrowOnCycle(dependencyGraph);

        boost::adjacency_list<> transitiveClosure;
        transitive_closure(dependencyGraph, transitiveClosure);

        for (const auto& e : make_iterator_range(edges(transitiveClosure)))
            weights(e.m_target, e.m_source) = -1;

        return weights;
    }

    DependencyGraph::DependencyGraph(const xt::xtensor<int, 2>& weights)
        : m_weights(weights)
    {
        const auto N = weights.shape(0);

        m_node2incomingSpanMap.reserve(N);
        m_node2outgoingSpanMap.reserve(N);

        for (size_t u = 0; u < N; ++u)
        {
            const auto rangeBegin = ssize(m_outgoing);

            for (size_t v = 0; v < N; ++v)
                if (weights(v, u) == -1)
                    m_outgoing.push_back(v);

            const auto rangeEnd = ssize(m_outgoing);
            m_node2outgoingSpanMap.emplace_back(m_outgoing.data() + rangeBegin, m_outgoing.data() + rangeEnd);
        }

        for (size_t u = 0; u < N; ++u)
        {
            const auto rangeBegin = ssize(m_incoming);

            for (size_t v = 0; v < N; ++v)
            {
                if (weights(u, v) == -1)
                {
                    m_incoming.push_back(v);
                    m_arcs.emplace_back(v, u);
                }
            }

            const auto rangeEnd = ssize(m_incoming);
            m_node2incomingSpanMap.emplace_back(m_incoming.data() + rangeBegin, m_incoming.data() + rangeEnd);
        }
    }
}


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "ASTExpressionTest"
#include <boost/test/unit_test.hpp>
#include "../ast/Expression.h"
#include "../ast/Function.h"

BOOST_AUTO_TEST_SUITE(ASTExpressionTest)

BOOST_AUTO_TEST_CASE(int_value) {
    Scope s;
    IntValue i;
    i.value = 30;
    BOOST_CHECK_EQUAL(i.evaluate(s), 30);
}

BOOST_AUTO_TEST_CASE(power_raising_to_one) {
    Scope s;
    PowerRaising p(42);
    BOOST_CHECK_EQUAL(p.evaluate(s), 42);
}

BOOST_AUTO_TEST_CASE(power_raising_to_x) {
    Scope s;
    PowerRaising p(2);
    p.power = std::make_unique<PowerRaising>(10);
    BOOST_CHECK_EQUAL(p.evaluate(s), 1024);
}

BOOST_AUTO_TEST_CASE(multiplication) {
    Scope s;
    Multiplication m(11);
    m.b = std::make_unique<Multiplication>(12);
    BOOST_CHECK_EQUAL(m.evaluate(s), 11*12);
}

BOOST_AUTO_TEST_CASE(division) {
    Scope s;
    Multiplication m(20);
    m.b = std::make_unique<Multiplication>(5);
    m.type = Multiplication::DIVIDE;
    BOOST_CHECK_EQUAL(m.evaluate(s), 20/5);
}

BOOST_AUTO_TEST_CASE(modulo) {
    Scope s;
    Multiplication m(8);
    m.b = std::make_unique<Multiplication>(3);
    m.type = Multiplication::MODULO;
    BOOST_CHECK_EQUAL(m.evaluate(s), 8%3);
}

BOOST_AUTO_TEST_CASE(addition) {
    Scope s;
    Addition a(5);
    a.b = std::make_unique<Addition>(7);
    BOOST_CHECK_EQUAL(a.evaluate(s), 5+7);
}

BOOST_AUTO_TEST_CASE(subtraction) {
    Scope s;
    Addition a(5);
    a.b = std::make_unique<Addition>(7);
    a.isSubtraction = true;
    BOOST_CHECK_EQUAL(a.evaluate(s), 5-7);
}

BOOST_AUTO_TEST_CASE(numeric_expression) {
    Scope s;
    NumericExpression n;
    n.a = std::make_unique<Addition>(10);
    BOOST_CHECK_EQUAL(n.evaluate(s), 10);
}

BOOST_AUTO_TEST_CASE(comparison_lt) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::LESS_THAN;
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(!c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(comparison_le) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::LESS_EQUAL;
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(comparison_gt) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::GREATER_THAN;
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(!c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(comparison_ge) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::GREATER_EQUAL;
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(comparison_eq) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::EQUAL;
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(!c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(comparison_ne) {
    Scope s;
    Comparison c;
    c.a = std::make_unique<IntValue>(10);
    c.b = std::make_unique<IntValue>(20);
    c.op = Comparison::NOT_EQUAL;
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(5);
    BOOST_ASSERT(c.evaluate(s));

    c.b = std::make_unique<IntValue>(10);
    BOOST_ASSERT(!c.evaluate(s));
}

BOOST_AUTO_TEST_CASE(negation) {
    Scope s;
    Negation n;
    n.isActuallyNegation = true;
    n.a = std::make_unique<Comparison>();
    n.a->a = std::make_unique<IntValue>(5);
}

std::unique_ptr<Negation> makeNegation(int value, bool isActuallyNegation = false) {
    auto n = std::make_unique<Negation>();
    n->isActuallyNegation = isActuallyNegation;
    n->a = std::make_unique<Comparison>();
    n->a->a = std::make_unique<IntValue>(value);
    return n;
}

BOOST_AUTO_TEST_CASE(conjunction) {
    Scope s;
    Conjunction c;
    c.a = std::move(makeNegation(0));
    c.b = std::make_unique<Conjunction>();
    c.b->a = std::move(makeNegation(0));
    BOOST_ASSERT(!c.evaluate(s));

    c.a->isActuallyNegation = true;
    BOOST_ASSERT(!c.evaluate(s));

    c.b->a->isActuallyNegation = true;
    BOOST_ASSERT(c.evaluate(s));
}

std::unique_ptr<Conjunction> makeConjunction(int a, int b = 1) {
    auto c = std::make_unique<Conjunction>();
    c->a = std::move(makeNegation(a));
    c->b = std::make_unique<Conjunction>();
    c->b->a = std::move(makeNegation(b));
    return c;
}

BOOST_AUTO_TEST_CASE(disjunction) {
    Scope s;
    Disjunction d;
    d.a = std::move(makeConjunction(1));
    d.b = std::make_unique<Disjunction>();
    d.b->a = std::move(makeConjunction(1));
    BOOST_ASSERT(d.evaluate(s));

    d.b->a = std::move(makeConjunction(0));
    BOOST_ASSERT(d.evaluate(s));

    d.a = std::move(makeConjunction(0));
    BOOST_ASSERT(!d.evaluate(s));
}

BOOST_AUTO_TEST_CASE(logical_expression) {
    Scope s;
    LogicalExpression l;

    l.a = std::make_unique<Disjunction>();
    l.a->a = std::move(makeConjunction(1));

    BOOST_ASSERT(l.evaluate(s));
}

BOOST_AUTO_TEST_SUITE_END()


//  Copyright (c) 2006-2009, Bernhard Reiter
//
//  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)

#include <boost/tree/algorithm.hpp>

//#define BOOST_TEST_MODULE cursor_algorithm test

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

using namespace boost::unit_test;

#include "fake_binary_tree.hpp"

using namespace boost::tree;

template <class T>
struct val_less
: public std::binary_function <T, int, bool>
{
    bool operator() (const T& x, const int y) const
    {
        return x.val < y;
    }
};

void lower_bound_test(int i)
{
    fake_binary_tree_fixture<int> f;
    fake_binary_tree<int>::cursor c(f.fbt1, 0), d(f.fbt1, 0);

    test_data_set mpo;
    mock_cursor_data(mpo);

    c = lower_bound(f.fbt1.root(), i);
    test_data_set::index<inorder>::type::const_iterator ci
    = std::lower_bound(mpo.get<inorder>().begin(), mpo.get<inorder>().end(), i
                     , val_less<test_data_set::value_type>());
    BOOST_CHECK_EQUAL(*c, ci->val);
}

void lower_bound_pred_test(int i)
{
    fake_binary_tree_fixture<int> f;
    fake_binary_tree<int>::cursor c(f.fbt1, 0), d(f.fbt1, 0);

    test_data_set mpo;
    mock_cursor_data(mpo);

    c = lower_bound(f.fbt1.root(), i, std::less<int>());
    test_data_set::index<inorder>::type::const_iterator ci
    = std::lower_bound(mpo.get<inorder>().begin(), mpo.get<inorder>().end(), i
                     , val_less<test_data_set::value_type>());
    BOOST_CHECK_EQUAL(*c, ci->val);
}

test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    int params[] = { 4, 7, 6, 8, 5 };
//    4: Left Leaf
//    7: Right Leaf
//    6: Non-Leaf
//    8: Non-Leaf, Root
//    5: Not in Tree

    framework::master_test_suite().
        add( BOOST_PARAM_TEST_CASE( &lower_bound_test, params, params+5 ) );
    framework::master_test_suite().
        add( BOOST_PARAM_TEST_CASE( &lower_bound_pred_test, params, params+5 ) );

    return 0;
}