//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2015-2016.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/move for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_MOVE_TEST_ORDER_TYPE_HPP
#define BOOST_MOVE_TEST_ORDER_TYPE_HPP

#include <boost/config.hpp>
#include <boost/move/core.hpp>
#include <boost/move/detail/iterator_traits.hpp>
#include <cstddef>
#include <cstdio>

struct order_perf_type
{
   public:
   std::size_t key;
   std::size_t val;

   order_perf_type()
      : key(), val()
   {
      ++num_elements;
   }

   order_perf_type(const order_perf_type& other)
      : key(other.key), val(other.val)
   {
      ++num_elements;
      ++num_copy;
   }

   order_perf_type & operator=(const order_perf_type& other)
   {
      ++num_copy;
      key = other.key;
      val = other.val;
      return *this;
   }

   ~order_perf_type ()
   {
      --num_elements;
   }

   static void reset_stats()
   {
      num_compare=0;
      num_copy=0;
   }

   friend bool operator< (const order_perf_type& left, const order_perf_type& right)
   {  ++num_compare; return left.key < right.key;  }

   static boost::ulong_long_type num_compare;
   static boost::ulong_long_type num_copy;
   static boost::ulong_long_type num_elements;
};

boost::ulong_long_type order_perf_type::num_compare = 0;
boost::ulong_long_type order_perf_type::num_copy = 0;
boost::ulong_long_type order_perf_type::num_elements = 0;


struct order_move_type
{
   BOOST_MOVABLE_BUT_NOT_COPYABLE(order_move_type)

   public:
   std::size_t key;
   std::size_t val;

   static const std::size_t moved_constr_mark = std::size_t(-1);
   static const std::size_t moved_assign_mark = std::size_t(-2);

   order_move_type()
      : key(0u), val(0u)
   {}

   order_move_type(BOOST_RV_REF(order_move_type) other)
      : key(other.key), val(other.val)
   {
      assert(this != &other);
      other.key = other.val = std::size_t(-1);
   }

   order_move_type & operator=(BOOST_RV_REF(order_move_type) other)
   {
      assert(this != &other);
      key = other.key;
      val = other.val;
      other.key = other.val = std::size_t(-2);
      return *this;
   }

   friend bool operator< (const order_move_type& left, const order_move_type& right)
   {  return left.key < right.key;  }

   ~order_move_type ()
   {
      key = val = std::size_t(-3);
   }
};

struct order_type_less
{
   template<class T, class U>
   bool operator()(const T &a, U const &b) const
   {  return a < b;   }
};

template<class T>
inline bool is_order_type_ordered(T *elements, std::size_t element_count, bool stable = true)
{
   for(std::size_t i = 1; i < element_count; ++i){
      if(order_type_less()(elements[i], elements[i-1])){
         std::printf("\n Ord KO !!!!");
         return false;
      }
      if( stable && !(order_type_less()(elements[i-1], elements[i])) && (elements[i-1].val > elements[i].val) ){
         std::printf("\n Stb KO !!!! ");
         return false;
      }
   }
   return true;
}

namespace boost {
namespace movelib {

inline bool is_sorted(::order_perf_type *first, ::order_perf_type *last, ::order_type_less)
{
   if (first != last) {
      const order_perf_type *next = first, *cur(first);
      while (++next != last) {
         if (!(cur->key < next->key || (cur->key == next->key && cur->val < next->val)))
            return false;
         cur = next;
      }
   }
   return true;
}

inline bool is_sorted(::order_move_type* first, ::order_move_type* last, ::order_type_less)
{
   if (first != last) {
      const order_move_type* next = first, * cur(first);
      while (++next != last) {
         if (!(cur->key < next->key || (cur->key == next->key && cur->val < next->val)))
            return false;
         cur = next;
      }
   }
   return true;
}

}} //boost::movelib

template<class T>
inline bool is_key(T *elements, std::size_t element_count)
{
   for(std::size_t i = 1; i < element_count; ++i){
      if(elements[i].key >= element_count){
         std::printf("\n Key.key KO !!!!");
         return false;
      }
      if(elements[i].val != std::size_t(-1)){
         std::printf("\n Key.val KO !!!!");
         return false;
      }
   }
   return true;
}

template<class T>
inline bool is_buffer(T *elements, std::size_t element_count)
{
   for(std::size_t i = 1; i < element_count; ++i){
      if(elements[i].key != std::size_t(-1)){
         std::printf("\n Buf.key KO !!!!");
         return false;
      }
      if(elements[i].val >= element_count){
         std::printf("\n Buf.val KO !!!!");
         return false;
      }
   }
   return true;
}


//size_type iterator
template <class T, class D>
class randit
{
   public:
   typedef std::random_access_iterator_tag   iterator_category;
   typedef T                                 value_type;
   typedef D                                 difference_type;
   typedef T*                                pointer;
   typedef T&                                reference;

   private:
   T* m_ptr;

   public:
   explicit randit(T* ptr)
      : m_ptr(ptr)
   {}

   public:

   //Constructors
   randit()
      : m_ptr()   //Value initialization to achieve "null iterators" (N3644)
   {}

   randit(const randit& other)
      :  m_ptr(other.m_ptr)
   {}

   randit & operator=(const randit& other)
   {  m_ptr = other.m_ptr;   return *this;  }

   //T* like operators
   reference operator*()   const
   {  return *m_ptr;  }

   pointer operator->()  const
   {  return m_ptr;  }

   reference operator[](difference_type off) const
   {  return m_ptr[off];  }

   //Increment / Decrement
   randit& operator++()
   {  ++m_ptr;  return *this; }

   randit operator++(int)
   {  return randit(m_ptr++); }

   randit& operator--()
   {  --m_ptr; return *this;  }

   randit operator--(int)
   {  return randit(m_ptr--); }

   //Arithmetic
   randit& operator+=(difference_type off)
   {  m_ptr += off; return *this;   }

   randit& operator-=(difference_type off)
   {  m_ptr -= off; return *this;   }

   friend randit operator+(const randit &x, difference_type off)
   {  return randit(x.m_ptr+off);  }

   friend randit operator+(difference_type off, randit right)
   {  right.m_ptr += off;  return right; }

   friend randit operator-(randit left, difference_type off)
   {  left.m_ptr -= off;  return left; }

   friend difference_type operator-(const randit &left, const randit& right)
   {  return difference_type(left.m_ptr - right.m_ptr);   }

   //Comparison operators
   friend bool operator==   (const randit& l, const randit& r)
   {  return l.m_ptr == r.m_ptr;  }

   friend bool operator!=   (const randit& l, const randit& r)
   {  return l.m_ptr != r.m_ptr;  }

   friend bool operator<    (const randit& l, const randit& r)
   {  return l.m_ptr < r.m_ptr;  }

   friend bool operator<=   (const randit& l, const randit& r)
   {  return l.m_ptr <= r.m_ptr;  }

   friend bool operator>    (const randit& l, const randit& r)
   {  return l.m_ptr > r.m_ptr;  }

   friend bool operator>=   (const randit& l, const randit& r)
   {  return l.m_ptr >= r.m_ptr;  }
};

struct less_int
{
   bool operator()(int l, int r)
   {  return l < r;  }
};


#endif   //BOOST_MOVE_TEST_ORDER_TYPE_HPP
