//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-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/container for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_CONTAINER_DETAIL_UTILITIES_HPP
#define BOOST_CONTAINER_DETAIL_UTILITIES_HPP

#include "config_begin.hpp"
#include <cstdio>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_enum.hpp>
#include <boost/type_traits/is_member_pointer.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/move/move.hpp>
#include <boost/container/detail/mpl.hpp>
#include <boost/container/detail/type_traits.hpp>
#include <boost/container/allocator_traits.hpp>
#include <algorithm>

namespace boost {
namespace container {
namespace container_detail {

template <typename T>
inline T* addressof(T& obj)
{
   return static_cast<T*>(
	   static_cast<void*>(
	      const_cast<char*>(
            &reinterpret_cast<const char&>(obj)
   )));
}

template<class T>
const T &max_value(const T &a, const T &b)
{  return a > b ? a : b;   }

template<class T>
const T &min_value(const T &a, const T &b)
{  return a < b ? a : b;   }

template <class SizeType>
SizeType
   get_next_capacity(const SizeType max_size
                    ,const SizeType capacity
                    ,const SizeType n)
{
//   if (n > max_size - capacity)
//      throw std::length_error("get_next_capacity");

   const SizeType m3 = max_size/3;

   if (capacity < m3)
      return capacity + max_value(3*(capacity+1)/5, n);

   if (capacity < m3*2)
      return capacity + max_value((capacity+1)/2, n);

   return max_size;
}

template <class T>
inline T* to_raw_pointer(T* p)
{  return p; }

template <class Pointer>
inline typename Pointer::element_type*
   to_raw_pointer(const Pointer &p)
{  return boost::container::container_detail::to_raw_pointer(p.operator->());  }

//!To avoid ADL problems with swap
template <class T>
inline void do_swap(T& x, T& y)
{
   using std::swap;
   swap(x, y);
}

template<class AllocatorType>
inline void swap_alloc(AllocatorType &, AllocatorType &, container_detail::false_type)
   BOOST_CONTAINER_NOEXCEPT
{}

template<class AllocatorType>
inline void swap_alloc(AllocatorType &l, AllocatorType &r, container_detail::true_type)
{  container_detail::do_swap(l, r);   }

template<class AllocatorType>
inline void assign_alloc(AllocatorType &, const AllocatorType &, container_detail::false_type)
   BOOST_CONTAINER_NOEXCEPT
{}

template<class AllocatorType>
inline void assign_alloc(AllocatorType &l, const AllocatorType &r, container_detail::true_type)
{  l = r;   }

template<class AllocatorType>
inline void move_alloc(AllocatorType &, AllocatorType &, container_detail::false_type)
   BOOST_CONTAINER_NOEXCEPT
{}

template<class AllocatorType>
inline void move_alloc(AllocatorType &l, AllocatorType &r, container_detail::true_type)
{  l = ::boost::move(r);   }

//Rounds "orig_size" by excess to round_to bytes
template<class SizeType>
inline SizeType get_rounded_size(SizeType orig_size, SizeType round_to)
{
   return ((orig_size-1)/round_to+1)*round_to;
}

template <std::size_t OrigSize, std::size_t RoundTo>
struct ct_rounded_size
{
   enum { value = ((OrigSize-1)/RoundTo+1)*RoundTo };
};

template<class T>
struct move_const_ref_type
   : if_c
//   < ::boost::is_fundamental<T>::value || ::boost::is_pointer<T>::value || ::boost::is_member_pointer<T>::value || ::boost::is_enum<T>::value
   < !::boost::is_class<T>::value
   ,const T &
   ,BOOST_CATCH_CONST_RLVALUE(T)
   >
{};

}  //namespace container_detail {

//////////////////////////////////////////////////////////////////////////////
//
//                               uninitialized_move_alloc
//
//////////////////////////////////////////////////////////////////////////////

//! <b>Effects</b>:
//!   \code
//!   for (; first != last; ++result, ++first)
//!      allocator_traits::construct(a, &*result, boost::move(*first));
//!   \endcode
//!
//! <b>Returns</b>: result
template
   <typename A,
    typename I, // I models InputIterator
    typename F> // F models ForwardIterator
F uninitialized_move_alloc(A &a, I f, I l, F r)
{
   while (f != l) {
      allocator_traits<A>::construct(a, container_detail::to_raw_pointer(&*r), boost::move(*f));
      ++f; ++r;
   }
   return r;
}

//////////////////////////////////////////////////////////////////////////////
//
//                               uninitialized_copy_alloc
//
//////////////////////////////////////////////////////////////////////////////

//! <b>Effects</b>:
//!   \code
//!   for (; first != last; ++result, ++first)
//!      allocator_traits::construct(a, &*result, *first);
//!   \endcode
//!
//! <b>Returns</b>: result
template
   <typename A,
    typename I, // I models InputIterator
    typename F> // F models ForwardIterator
F uninitialized_copy_alloc(A &a, I f, I l, F r)
{
   while (f != l) {
      allocator_traits<A>::construct(a, container_detail::to_raw_pointer(&*r), *f);
      ++f; ++r;
   }
   return r;
}

//////////////////////////////////////////////////////////////////////////////
//
//                               uninitialized_copy_alloc
//
//////////////////////////////////////////////////////////////////////////////

//! <b>Effects</b>:
//!   \code
//!   for (; first != last; ++result, ++first)
//!      allocator_traits::construct(a, &*result, *first);
//!   \endcode
//!
//! <b>Returns</b>: result
template
   <typename A,
    typename F, // F models ForwardIterator
    typename T>
void uninitialized_fill_alloc(A &a, F f, F l, const T &t)
{
   while (f != l) {
      allocator_traits<A>::construct(a, container_detail::to_raw_pointer(&*f), t);
      ++f;
   }
}

//////////////////////////////////////////////////////////////////////////////
//
//                            uninitialized_copy_or_move_alloc
//
//////////////////////////////////////////////////////////////////////////////

template
<typename A
,typename I    // I models InputIterator
,typename F>   // F models ForwardIterator
F uninitialized_copy_or_move_alloc
   (A &a, I f, I l, F r
   ,typename boost::container::container_detail::enable_if
      < boost::move_detail::is_move_iterator<I> >::type* = 0)
{
   return ::boost::container::uninitialized_move_alloc(a, f, l, r);
}

template
<typename A
,typename I    // I models InputIterator
,typename F>   // F models ForwardIterator
F uninitialized_copy_or_move_alloc
   (A &a, I f, I l, F r
   ,typename boost::container::container_detail::disable_if
      < boost::move_detail::is_move_iterator<I> >::type* = 0)
{
   return ::boost::container::uninitialized_copy_alloc(a, f, l, r);
}


}  //namespace container {
}  //namespace boost {


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

#endif   //#ifndef BOOST_CONTAINER_DETAIL_UTILITIES_HPP
