//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2007-2012. 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/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/detail/type_traits.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/static_assert.hpp>

using namespace boost::interprocess;

class Base
{};

class Derived
   : public Base
{};

class VirtualDerived
   : public virtual Base
{};

bool test_types_and_conversions()
{
   typedef offset_ptr<int>                pint_t;
   typedef offset_ptr<const int>          pcint_t;
   typedef offset_ptr<volatile int>       pvint_t;
   typedef offset_ptr<const volatile int> pcvint_t;

   BOOST_STATIC_ASSERT((ipcdetail::is_same<pint_t::element_type, int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pcint_t::element_type, const int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pvint_t::element_type, volatile int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::element_type, const volatile int>::value));

   BOOST_STATIC_ASSERT((ipcdetail::is_same<pint_t::value_type,   int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pcint_t::value_type,  int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pvint_t::value_type,  int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::value_type, int>::value));
   int dummy_int = 9;

   {  pint_t pint(&dummy_int);   pcint_t  pcint(pint);
      if(pcint.get()  != &dummy_int)   return false;  }
   {  pint_t pint(&dummy_int);   pvint_t  pvint(pint);
      if(pvint.get()  != &dummy_int)   return false;  }
   {  pint_t pint(&dummy_int);   pcvint_t  pcvint(pint);
      if(pcvint.get()  != &dummy_int)  return false;  }
   {  pcint_t pcint(&dummy_int); pcvint_t  pcvint(pcint);
         if(pcvint.get()  != &dummy_int)  return false;  }
   {  pvint_t pvint(&dummy_int); pcvint_t  pcvint(pvint);
      if(pcvint.get()  != &dummy_int)  return false;  }

   pint_t   pint(0);
   pcint_t  pcint(0);
   pvint_t  pvint(0);
   pcvint_t pcvint(0);

   pint     = &dummy_int;
   pcint    = &dummy_int;
   pvint    = &dummy_int;
   pcvint   = &dummy_int;

   {   pcint  = pint;   if(pcint.get() != &dummy_int)   return false;  }
   {   pvint  = pint;   if(pvint.get() != &dummy_int)   return false;  }
   {   pcvint = pint;   if(pcvint.get() != &dummy_int)  return false;  }
   {   pcvint = pcint;  if(pcvint.get() != &dummy_int)  return false;  }
   {   pcvint = pvint;  if(pcvint.get() != &dummy_int)  return false;  }

   if(!pint)
      return false;

   pint = 0;
   if(pint)
      return false;

   if(pint != 0)
      return false;

   if(0 != pint)
      return false;

   pint = &dummy_int;
   if(0 == pint)
      return false;

   if(pint == 0)
      return false;

   pcint = &dummy_int;

   if( (pcint - pint) != 0)
      return false;

   if( (pint - pcint) != 0)
      return false;

   return true;
}

template<class BasePtr, class DerivedPtr>
bool test_base_derived_impl()
{
   typename DerivedPtr::element_type d;
   DerivedPtr pderi(&d);

   BasePtr pbase(pderi);
   pbase = pderi;
   if(pbase != pderi)
      return false;
   if(!(pbase == pderi))
      return false;
   if((pbase - pderi) != 0)
      return false;
   if(pbase < pderi)
      return false;
   if(pbase > pderi)
      return false;
   if(!(pbase <= pderi))
      return false;
   if(!(pbase >= pderi))
      return false;

   return true;
}

bool test_base_derived()
{
   typedef offset_ptr<Base>               pbase_t;
   typedef offset_ptr<const Base>         pcbas_t;
   typedef offset_ptr<Derived>            pderi_t;
   typedef offset_ptr<VirtualDerived>     pvder_t;

   if(!test_base_derived_impl<pbase_t, pderi_t>())
      return false;
   if(!test_base_derived_impl<pbase_t, pvder_t>())
      return false;
   if(!test_base_derived_impl<pcbas_t, pderi_t>())
      return false;
   if(!test_base_derived_impl<pcbas_t, pvder_t>())
      return false;

   return true;
}

bool test_arithmetic()
{
   typedef offset_ptr<int> pint_t;
   const int NumValues = 5;
   int values[NumValues];

   //Initialize p
   pint_t p = values;
   if(p.get() != values)
      return false;

   //Initialize p + NumValues
   pint_t pe = &values[NumValues];
   if(pe == p)
      return false;
   if(pe.get() != &values[NumValues])
      return false;

   //ptr - ptr
   if((pe - p) != NumValues)
      return false;
   //ptr - integer
   if((pe - NumValues) != p)
      return false;
   //ptr + integer
   if((p + NumValues) != pe)
      return false;
   //integer + ptr
   if((NumValues + p) != pe)
      return false;
   //indexing
   if(pint_t(&p[NumValues])   != pe)
      return false;
   if(pint_t(&pe[-NumValues]) != p)
      return false;

   //ptr -= integer
   pint_t p0 = pe;
   p0-= NumValues;
   if(p != p0)
      return false;
   //ptr += integer
   pint_t penew = p0;
   penew += NumValues;
   if(penew != pe)
      return false;

   //++ptr
   penew = p0;
   for(int j = 0; j != NumValues; ++j, ++penew);
   if(penew != pe)
      return false;
   //--ptr
   p0 = pe;
   for(int j = 0; j != NumValues; ++j, --p0);
   if(p != p0)
      return false;
   //ptr++
   penew = p0;
   for(int j = 0; j != NumValues; ++j){
      pint_t p_new_copy = penew;
      if(p_new_copy != penew++)
         return false;
   }
   //ptr--
   p0 = pe;
   for(int j = 0; j != NumValues; ++j){
      pint_t p0_copy = p0;
      if(p0_copy != p0--)
         return false;
   }

   return true;
}

bool test_comparison()
{
   typedef offset_ptr<int> pint_t;
   const int NumValues = 5;
   int values[NumValues];

   //Initialize p
   pint_t p = values;
   if(p.get() != values)
      return false;

   //Initialize p + NumValues
   pint_t pe = &values[NumValues];
   if(pe == p)
      return false;
   if(pe.get() != &values[NumValues])
      return false;

   //operators
   if(p == pe)
      return false;
   if(p != p)
      return false;
   if(!(p < pe))
      return false;
   if(!(p <= pe))
      return false;
   if(!(pe > p))
      return false;
   if(!(pe >= p))
      return false;

   return true;
}

bool test_pointer_traits()
{
   typedef offset_ptr<int> OInt;
   typedef boost::intrusive::pointer_traits< OInt > PTOInt;
   BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::element_type, int>::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::pointer, OInt >::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::difference_type, OInt::difference_type >::value));
   BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::rebind_pointer<double>::type, offset_ptr<double> >::value));
   int dummy;
   OInt oi(&dummy);
   if(boost::intrusive::pointer_traits<OInt>::pointer_to(dummy) != oi){
      return false;
   }
   return true;
}

int main()
{
   if(!test_types_and_conversions())
      return 1;
   if(!test_base_derived())
      return 1;
   if(!test_arithmetic())
      return 1;
   if(!test_comparison())
      return 1;
   if(!test_pointer_traits())
      return 1;
   return 0;
}

#include <boost/interprocess/detail/config_end.hpp>

/*
//Offset ptr benchmark
#include <vector>
#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/timer.hpp>
#include <cstddef>

template<class InIt,
   class Ty> inline
   Ty accumulate2(InIt First, InIt Last, Ty Val)
   {   // return sum of Val and all in [First, Last)
   for (; First != Last; ++First) //First = First + 1)
      Val = Val + *First;
   return (Val);
   }

template <typename Vector>
void time_test(const Vector& vec, std::size_t iterations, const char* label) {
  // assert(!vec.empty())
  boost::timer t;
  typename Vector::const_iterator first = vec.begin();
  typename Vector::value_type result(0);
  while (iterations != 0) {
    result = accumulate2(first, first + vec.size(), result);
    --iterations;
  }
  std::cout << label << t.elapsed() << " " << result << std::endl;
}

int main()
{
   using namespace boost::interprocess;
   typedef allocator<double, managed_shared_memory::segment_manager> alloc_t;

   std::size_t n = 0x1 << 26;
   std::size_t file_size = n * sizeof(double) + 1000000;

   {
      shared_memory_object::remove("MyMappedFile");
      managed_shared_memory segment(open_or_create, "MyMappedFile", file_size);
      shared_memory_object::remove("MyMappedFile");
      alloc_t alloc_inst(segment.get_segment_manager());
      vector<double, alloc_t>  v0(n, double(42.42), alloc_inst);
      time_test(v0, 10, "iterator   shared:     ");
   }
   {
      std::vector<double>      v1(n, double(42.42));
      time_test(v1, 10, "iterator   non-shared: ");
   }
  return 0;
}

*/
