#include <vector>
#include <algorithm>
#include <string>
#if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
#  include <rope>
#endif
#if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
#  include <slist>
#endif
#include <list>
#include <deque>
#include <set>
#include <map>
#if defined (STLPORT)
#  include <unordered_set>
#  include <unordered_map>
#endif
#if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
#  include <hash_set>
#  include <hash_map>
#endif
#include <queue>
#include <stack>

#include "mvctor_test.h"

#if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
using namespace std;
#  if defined (STLPORT)
using namespace std::tr1;
#  endif
#endif

#if defined (STLPORT) && !defined (_STLP_NO_MOVE_SEMANTIC)

#  if defined (__GNUC__) && defined (_STLP_USE_NAMESPACES)
// libstdc++ sometimes exposed its own __true_type in
// global namespace resulting in an ambiguity.
#    define __true_type std::__true_type
#    define __false_type std::__false_type
#  endif

static bool type_to_bool(__true_type)
{ return true; }
static bool type_to_bool(__false_type)
{ return false; }

template <class _Tp>
static bool is_movable(const _Tp&) {
  typedef typename __move_traits<_Tp>::implemented _MovableTp;
  return type_to_bool(_MovableTp());
}

template <class _Tp>
static bool is_move_complete(const _Tp&) {
  typedef __move_traits<_Tp> _TpMoveTraits;
  typedef typename _TpMoveTraits::complete _TpMoveComplete;
  return type_to_bool(_TpMoveComplete());
}

struct specially_allocated_struct {
  bool operator < (const specially_allocated_struct&) const;
#  if defined (__DMC__) // slist<_Tp,_Alloc>::remove error
  bool operator==(const specially_allocated_struct&) const;
#  endif
};

#if defined (__DMC__)
bool specially_allocated_struct::operator < (const specially_allocated_struct&) const
{ return false; }
#endif

struct struct_with_specialized_less {};

#  if defined (_STLP_USE_NAMESPACES)
namespace std {
#  endif
  _STLP_TEMPLATE_NULL
  class allocator<specially_allocated_struct> {
    //This allocator just represent what a STLport could do and in this
    //case the STL containers implemented with it should still be movable
    //but not completely as we cannot do any hypothesis on what is in this
    //allocator.
  public:
    typedef specially_allocated_struct value_type;
    typedef value_type *       pointer;
    typedef const value_type* const_pointer;
    typedef value_type&       reference;
    typedef const value_type& const_reference;
    typedef size_t     size_type;
    typedef ptrdiff_t  difference_type;
#  if defined (_STLP_MEMBER_TEMPLATE_CLASSES)
    template <class _Tp1> struct rebind {
      typedef allocator<_Tp1> other;
    };
#  endif
    allocator() _STLP_NOTHROW {}
#  if defined (_STLP_MEMBER_TEMPLATES)
    template <class _Tp1> allocator(const allocator<_Tp1>&) _STLP_NOTHROW {}
#  endif
    allocator(const allocator&) _STLP_NOTHROW {}
    ~allocator() _STLP_NOTHROW {}
    pointer address(reference __x) const { return &__x; }
    const_pointer address(const_reference __x) const { return &__x; }
    pointer allocate(size_type, const void* = 0) { return 0; }
    void deallocate(pointer, size_type) {}
    size_type max_size() const _STLP_NOTHROW  { return 0; }
    void construct(pointer, const_reference) {}
    void destroy(pointer) {}
  };

  _STLP_TEMPLATE_NULL
  struct less<struct_with_specialized_less> {
    bool operator() (struct_with_specialized_less const&,
                     struct_with_specialized_less const&) const;
  };

#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
#    if !defined (_STLP_NO_MOVE_SEMANTIC)
#      if defined (__BORLANDC__) && (__BORLANDC__ >= 0x564)
  _STLP_TEMPLATE_NULL
  struct __move_traits<vector<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<deque<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<list<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<slist<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<less<struct_with_specialized_less> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<set<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
  _STLP_TEMPLATE_NULL
  struct __move_traits<multiset<specially_allocated_struct> > {
    typedef __true_type implemented;
    typedef __false_type complete;
  };
#      endif
#    endif
#  endif

#  if defined (_STLP_USE_NAMESPACES)
}
#  endif
#endif

void MoveConstructorTest::movable_declaration()
{
#if defined (STLPORT) && !defined (_STLP_DONT_SIMULATE_PARTIAL_SPEC_FOR_TYPE_TRAITS) && \
                         !defined (_STLP_NO_MOVE_SEMANTIC)
  //This test purpose is to check correct detection of the STL movable
  //traits declaration
  {
    //string, wstring:
    CPPUNIT_ASSERT( is_movable(string()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(string()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(string()) );
#  endif
#  if defined (_STLP_HAS_WCHAR_T)
    CPPUNIT_ASSERT( is_movable(wstring()) );
#    if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(wstring()) );
#    else
    CPPUNIT_ASSERT( !is_move_complete(wstring()) );
#    endif
#  endif
  }

#  if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
  {
    //crope, wrope:
    CPPUNIT_ASSERT( is_movable(crope()) );
#    if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(crope()) );
#    else
    CPPUNIT_ASSERT( !is_move_complete(crope()) );
#    endif
#    if defined (_STLP_HAS_WCHAR_T)
    CPPUNIT_ASSERT( is_movable(wrope()) );
#      if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(wrope()) );
#      else
    CPPUNIT_ASSERT( !is_move_complete(wrope()) );
#      endif
#    endif
  }
#  endif

  {
    //vector:
    CPPUNIT_ASSERT( is_movable(vector<char>()) );
    CPPUNIT_ASSERT( is_movable(vector<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(vector<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(vector<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(vector<char>()) );
#  endif
  }

  {
    //deque:
    CPPUNIT_ASSERT( is_movable(deque<char>()) );
    CPPUNIT_ASSERT( is_movable(deque<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(deque<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(deque<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(deque<char>()) );
#  endif
  }

  {
    //list:
    CPPUNIT_ASSERT( is_movable(list<char>()) );
    CPPUNIT_ASSERT( is_movable(list<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(list<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(list<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(list<char>()) );
#  endif
  }

#  if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
  {
    //slist:
    CPPUNIT_ASSERT( is_movable(slist<char>()) );
    CPPUNIT_ASSERT( is_movable(slist<specially_allocated_struct>()) );
#    if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(slist<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(slist<specially_allocated_struct>()) );
#    else
    CPPUNIT_ASSERT( !is_move_complete(slist<char>()) );
#    endif
  }
#  endif

  {
    //queue:
    CPPUNIT_ASSERT( is_movable(queue<char>()) );
    CPPUNIT_ASSERT( is_movable(queue<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(queue<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(queue<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(queue<char>()) );
#  endif
  }

  {
    //stack:
    CPPUNIT_ASSERT( is_movable(stack<char>()) );
    CPPUNIT_ASSERT( is_movable(stack<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(stack<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(stack<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(stack<char>()) );
#  endif
  }

#endif
}

void MoveConstructorTest::movable_declaration_assoc()
{
#if defined (STLPORT) && !defined (_STLP_DONT_SIMULATE_PARTIAL_SPEC_FOR_TYPE_TRAITS) && \
                         !defined (_STLP_NO_MOVE_SEMANTIC)
  {
    //associative containers, set multiset, map, multimap:

    //For associative containers it is important that less is correctly recognize as
    //the STLport less or a user specialized less:
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(less<char>()) );
#  endif
    CPPUNIT_ASSERT( !is_move_complete(less<struct_with_specialized_less>()) );

    //set
    CPPUNIT_ASSERT( is_movable(set<char>()) );
    CPPUNIT_ASSERT( is_movable(set<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(set<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(set<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(set<char>()) );
#  endif

    //multiset
    CPPUNIT_ASSERT( is_movable(multiset<char>()) );
    CPPUNIT_ASSERT( is_movable(multiset<specially_allocated_struct>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(multiset<char>()) );
    CPPUNIT_ASSERT( !is_move_complete(multiset<specially_allocated_struct>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(multiset<char>()) );
#  endif

    //map
    CPPUNIT_ASSERT( is_movable(map<char, char>()) );
    CPPUNIT_ASSERT( is_movable(map<specially_allocated_struct, char>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(map<char, char>()) );
    //Here even if allocator has been specialized for specially_allocated_struct
    //this pecialization won't be used in default map instanciation as the default
    //allocator is allocator<pair<specially_allocated_struct, char> >
    CPPUNIT_ASSERT( is_move_complete(map<specially_allocated_struct, char>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(map<char, char>()) );
#  endif

    //multimap
    CPPUNIT_ASSERT( is_movable(multimap<char, char>()) );
    CPPUNIT_ASSERT( is_movable(multimap<specially_allocated_struct, char>()) );
#  if defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)
    CPPUNIT_ASSERT( is_move_complete(multimap<char, char>()) );
    //Idem map remark
    CPPUNIT_ASSERT( is_move_complete(multimap<specially_allocated_struct, char>()) );
#  else
    CPPUNIT_ASSERT( !is_move_complete(multimap<char, char>()) );
#  endif
  }
#endif
}

void MoveConstructorTest::movable_declaration_hash()
{
#if defined (STLPORT) && !defined (_STLP_DONT_SIMULATE_PARTIAL_SPEC_FOR_TYPE_TRAITS) && \
                         !defined (_STLP_NO_MOVE_SEMANTIC)
  {
    //hashed containers, unordered_set unordered_multiset, unordered_map, unordered_multimap,
    //                   hash_set, hash_multiset, hash_map, hash_multimap:

    //We only check that they are movable, completness is not yet supported
    CPPUNIT_ASSERT( is_movable(unordered_set<char>()) );
    CPPUNIT_ASSERT( is_movable(unordered_multiset<char>()) );
    CPPUNIT_ASSERT( is_movable(unordered_map<char, char>()) );
    CPPUNIT_ASSERT( is_movable(unordered_multimap<char, char>()) );
#  if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    CPPUNIT_ASSERT( is_movable(hash_set<char>()) );
    CPPUNIT_ASSERT( is_movable(hash_multiset<char>()) );
    CPPUNIT_ASSERT( is_movable(hash_map<char, char>()) );
    CPPUNIT_ASSERT( is_movable(hash_multimap<char, char>()) );
#  endif
  }
#endif
}