#include <boost/iterator/iterator_facade.hpp>
#include <boost/operators.hpp>

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/cat.hpp>

#include <boost/type_traits/is_pointer.hpp>

#include <boost/mpl/aux_/lambda_support.hpp>
#include <boost/mpl/apply.hpp>

#include <boost/timer.hpp>

#include <cstdlib>
#include <ctime>
#include <iostream>

namespace with_iterator_lib
{

  template<typename Value>
  class bidir_ptr_iterator:
      public boost::iterator_facade<
          bidir_ptr_iterator<Value>,
          Value,
          boost::bidirectional_traversal_tag>
  {
   public:
      bidir_ptr_iterator(){}
      explicit bidir_ptr_iterator(Value* ptr_):ptr(ptr_){}

   protected:
      Value& dereference()const{return *ptr;}
      bool equal(const bidir_ptr_iterator& x)const{return ptr==x.ptr;}
      void increment(){++ptr;}
      void decrement(){--ptr;}

   private:
      friend class boost::iterator_core_access;

      Value* ptr;
   public:
      typedef bidir_ptr_iterator<Value> type;
      BOOST_MPL_AUX_LAMBDA_SUPPORT(1, bidir_ptr_iterator, (Value));
  };
}


namespace with_operator_lib
{
  
  template<typename Value>
  class bidir_ptr_iterator:
      public boost::bidirectional_iterator_helper<
          bidir_ptr_iterator<Value>,
          Value,
          std::ptrdiff_t,
          Value*,
          Value&>
  {
   public:
      bidir_ptr_iterator(){}
      explicit bidir_ptr_iterator(Value* ptr_):ptr(ptr_){}

      Value& operator*()const{return *ptr;}
      friend bool operator==(
          const bidir_ptr_iterator& x,const bidir_ptr_iterator& y)
      {return x.ptr==y.ptr;}
      bidir_ptr_iterator& operator++(){++ptr;return *this;}
      bidir_ptr_iterator& operator--(){--ptr;return *this;}

   private:
      Value* ptr;
   public:
      typedef bidir_ptr_iterator<Value> type;
      BOOST_MPL_AUX_LAMBDA_SUPPORT(1, bidir_ptr_iterator, (Value));
  };
}

#define BUFFER_SIZE 1024
#define ITERATIONS 10000

#define BASIC_TYPES (int)(char)(double)(short)(long)
#define ADD_POINTER(r, data, T) (T*)
#define POINTEES BASIC_TYPES BOOST_PP_SEQ_FOR_EACH(ADD_POINTER, _, BASIC_TYPES)
#define ALL_TYPES BASIC_TYPES BOOST_PP_SEQ_FOR_EACH(ADD_POINTER, _, POINTEES)

// #define REMOVE_POINTER_SPECIALIZATION(r, data, T) BOOST_TT_BROKEN_COMPILER_SPEC(T**)
// BOOST_PP_SEQ_FOR_EACH(REMOVE_POINTER_SPECIALIZATION, _, BASIC_TYPES)

template <class T,int>
struct random_buffer;

template <class T>
inline void init(T*& x, int)
{
    x = random_buffer<T,0>::instance + rand() % BUFFER_SIZE;
    
}

template <class T>
inline void init(T& x, ...)
{
    x = static_cast<T>(rand() % 100);
    
}

template <class T,int>
struct random_buffer
{
    static T* create()
    {
        typedef typename boost::remove_pointer<T>::type pointee;
        using namespace std;
        srand(time(0));
    
        T* x = (T*)malloc(BUFFER_SIZE * sizeof(T));
        
        for (std::size_t i = 0; i < BUFFER_SIZE; ++i)
            init(x[i],0);
        return x;
    }

    static T* instance;
};

template <class T,int N>
T* random_buffer<T,N>::instance = random_buffer<T,N>::create();

template <class T>
inline T* accumulate(T* v, T* base, int)
{
    return random_buffer<T,1>::instance
        + (BUFFER_SIZE/2) + (v - base) % (BUFFER_SIZE/2);
    
}

template <class T>
inline T accumulate(T v, T base, ...)
{
    return static_cast<T>(
        static_cast<unsigned long>(v) - static_cast<unsigned long>(base)
    );
}

// Test iterators over T*
template <class MakeIterator, class T>
inline void test(T* buf0, T* buf1 BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(MakeIterator))
{
    typedef typename boost::mpl::apply1<MakeIterator,T>::type iterator;
    T base = buf1[0];

    for (int i = 0; i < ITERATIONS; ++i)
    {
        for(iterator it0(buf0),it0_end(buf0+BUFFER_SIZE),it1(buf1);
            it0!=it0_end;
            ++it0,it1++
        )
        {
            T v = *it0;
            *it1 = accumulate(v, base, 0);
        }
    }
}


#define TEST_ONE(r, MakeIterator, T)                                            \
    test<MakeIterator>(random_buffer<T,0>::instance, random_buffer<T,1>::instance);

#define TEST_ALL(MakeIterator) BOOST_PP_SEQ_FOR_EACH(TEST_ONE, MakeIterator, ALL_TYPES)


template <class MakeIterator>
double test_all(BOOST_EXPLICIT_TEMPLATE_TYPE(MakeIterator) )
{
    TEST_ALL(MakeIterator)
    TEST_ALL(MakeIterator)
        
    boost::timer time;
    TEST_ALL(MakeIterator)
    return time.elapsed();
}

template <class T>
double prevent_dead_code_elimination(BOOST_EXPLICIT_TEMPLATE_TYPE(T) )
{
    double x = 0;
    for (std::size_t i = 0; i < BUFFER_SIZE; ++i)
    {
        x += random_buffer<T,1>::instance[i] - random_buffer<T,1>::instance[(i+1) % BUFFER_SIZE];
    }
    return x;
}


int main()
{
    double operator_time = test_all<with_operator_lib::bidir_ptr_iterator<boost::mpl::_> >();
    double iterator_time = test_all<with_iterator_lib::bidir_ptr_iterator<boost::mpl::_> >();
    std::cout << "with operator library: " << operator_time << std::endl;
    std::cout << "with iterator library: " << iterator_time << std::endl;
    double undead = 0;
#define PREVENT_DEAD_CODE(r, data, T) undead += prevent_dead_code_elimination<T>();
    BOOST_PP_SEQ_FOR_EACH(PREVENT_DEAD_CODE, _, ALL_TYPES);

    return undead == .5;
}

