//  (C) Copyright Steve Cleary, Beman Dawes, Howard Hinnant & John Maddock 2000.

//  Use, modification and distribution are subject to 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/utility for most recent version including documentation.



// compressed_pair: pair that "compresses" empty members

// (see libs/utility/compressed_pair.htm)

//

// JM changes 25 Jan 2004:

// For the case where T1 == T2 and both are empty, then first() and second()

// should return different objects.

// JM changes 25 Jan 2000:

// Removed default arguments from compressed_pair_switch to get

// C++ Builder 4 to accept them

// rewriten swap to get gcc and C++ builder to compile.

// added partial specialisations for case T1 == T2 to avoid duplicate constructor defs.



#ifndef BOOST_DETAIL_COMPRESSED_PAIR_HPP

#define BOOST_DETAIL_COMPRESSED_PAIR_HPP



#include <algorithm>



#include <boost/type_traits/remove_cv.hpp>

#include <boost/type_traits/is_empty.hpp>

#include <boost/type_traits/is_same.hpp>

#include <boost/call_traits.hpp>



#ifdef BOOST_MSVC

# pragma warning(push)

# pragma warning(disable:4512)

#endif 

namespace boost

{



template <class T1, class T2>

class compressed_pair;





// compressed_pair



namespace details

{

   // JM altered 26 Jan 2000:

   template <class T1, class T2, bool IsSame, bool FirstEmpty, bool SecondEmpty>

   struct compressed_pair_switch;



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, false, false, false>

      {static const int value = 0;};



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, false, true, true>

      {static const int value = 3;};



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, false, true, false>

      {static const int value = 1;};



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, false, false, true>

      {static const int value = 2;};



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, true, true, true>

      {static const int value = 4;};



   template <class T1, class T2>

   struct compressed_pair_switch<T1, T2, true, false, false>

      {static const int value = 5;};



   template <class T1, class T2, int Version> class compressed_pair_imp;



#ifdef __GNUC__

   // workaround for GCC (JM):

   using std::swap;

#endif

   //

   // can't call unqualified swap from within classname::swap

   // as Koenig lookup rules will find only the classname::swap

   // member function not the global declaration, so use cp_swap

   // as a forwarding function (JM):

   template <typename T>

   inline void cp_swap(T& t1, T& t2)

   {

#ifndef __GNUC__

      using std::swap;

#endif

      swap(t1, t2);

   }



   // 0    derive from neither



   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 0>

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {} 



      compressed_pair_imp(first_param_type x, second_param_type y)

         : first_(x), second_(y) {}



      compressed_pair_imp(first_param_type x)

         : first_(x) {}



      compressed_pair_imp(second_param_type y)

         : second_(y) {}



      first_reference       first()       {return first_;}

      first_const_reference first() const {return first_;}



      second_reference       second()       {return second_;}

      second_const_reference second() const {return second_;}



      void swap(::boost::compressed_pair<T1, T2>& y)

      {

         cp_swap(first_, y.first());

         cp_swap(second_, y.second());

      }

   private:

      first_type first_;

      second_type second_;

   };



   // 1    derive from T1



   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 1>

      : protected ::boost::remove_cv<T1>::type

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {}



      compressed_pair_imp(first_param_type x, second_param_type y)

         : first_type(x), second_(y) {}



      compressed_pair_imp(first_param_type x)

         : first_type(x) {}



      compressed_pair_imp(second_param_type y)

         : second_(y) {}



      first_reference       first()       {return *this;}

      first_const_reference first() const {return *this;}



      second_reference       second()       {return second_;}

      second_const_reference second() const {return second_;}



      void swap(::boost::compressed_pair<T1,T2>& y)

      {

         // no need to swap empty base class:

         cp_swap(second_, y.second());

      }

   private:

      second_type second_;

   };



   // 2    derive from T2



   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 2>

      : protected ::boost::remove_cv<T2>::type

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {}



      compressed_pair_imp(first_param_type x, second_param_type y)

         : second_type(y), first_(x) {}



      compressed_pair_imp(first_param_type x)

         : first_(x) {}



      compressed_pair_imp(second_param_type y)

         : second_type(y) {}



      first_reference       first()       {return first_;}

      first_const_reference first() const {return first_;}



      second_reference       second()       {return *this;}

      second_const_reference second() const {return *this;}



      void swap(::boost::compressed_pair<T1,T2>& y)

      {

         // no need to swap empty base class:

         cp_swap(first_, y.first());

      }



   private:

      first_type first_;

   };



   // 3    derive from T1 and T2



   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 3>

      : protected ::boost::remove_cv<T1>::type,

        protected ::boost::remove_cv<T2>::type

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {}



      compressed_pair_imp(first_param_type x, second_param_type y)

         : first_type(x), second_type(y) {}



      compressed_pair_imp(first_param_type x)

         : first_type(x) {}



      compressed_pair_imp(second_param_type y)

         : second_type(y) {}



      first_reference       first()       {return *this;}

      first_const_reference first() const {return *this;}



      second_reference       second()       {return *this;}

      second_const_reference second() const {return *this;}

      //

      // no need to swap empty bases:

      void swap(::boost::compressed_pair<T1,T2>&) {}

   };



   // JM

   // 4    T1 == T2, T1 and T2 both empty

   //      Originally this did not store an instance of T2 at all

   //      but that led to problems beause it meant &x.first() == &x.second()

   //      which is not true for any other kind of pair, so now we store an instance

   //      of T2 just in case the user is relying on first() and second() returning

   //      different objects (albeit both empty).

   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 4>

      : protected ::boost::remove_cv<T1>::type

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {}



      compressed_pair_imp(first_param_type x, second_param_type y)

         : first_type(x), m_second(y) {}



      compressed_pair_imp(first_param_type x)

         : first_type(x), m_second(x) {}



      first_reference       first()       {return *this;}

      first_const_reference first() const {return *this;}



      second_reference       second()       {return m_second;}

      second_const_reference second() const {return m_second;}



      void swap(::boost::compressed_pair<T1,T2>&) {}

   private:

      T2 m_second;

   };



   // 5    T1 == T2 and are not empty:   //JM



   template <class T1, class T2>

   class compressed_pair_imp<T1, T2, 5>

   {

   public:

      typedef T1                                                 first_type;

      typedef T2                                                 second_type;

      typedef typename call_traits<first_type>::param_type       first_param_type;

      typedef typename call_traits<second_type>::param_type      second_param_type;

      typedef typename call_traits<first_type>::reference        first_reference;

      typedef typename call_traits<second_type>::reference       second_reference;

      typedef typename call_traits<first_type>::const_reference  first_const_reference;

      typedef typename call_traits<second_type>::const_reference second_const_reference;



      compressed_pair_imp() {}



      compressed_pair_imp(first_param_type x, second_param_type y)

         : first_(x), second_(y) {}



      compressed_pair_imp(first_param_type x)

         : first_(x), second_(x) {}



      first_reference       first()       {return first_;}

      first_const_reference first() const {return first_;}



      second_reference       second()       {return second_;}

      second_const_reference second() const {return second_;}



      void swap(::boost::compressed_pair<T1, T2>& y)

      {

         cp_swap(first_, y.first());

         cp_swap(second_, y.second());

      }

   private:

      first_type first_;

      second_type second_;

   };



}  // details



template <class T1, class T2>

class compressed_pair

   : private ::boost::details::compressed_pair_imp<T1, T2,

             ::boost::details::compressed_pair_switch<

                    T1,

                    T2,

                    ::boost::is_same<typename remove_cv<T1>::type, typename remove_cv<T2>::type>::value,

                    ::boost::is_empty<T1>::value,

                    ::boost::is_empty<T2>::value>::value>

{

private:

   typedef details::compressed_pair_imp<T1, T2,

             ::boost::details::compressed_pair_switch<

                    T1,

                    T2,

                    ::boost::is_same<typename remove_cv<T1>::type, typename remove_cv<T2>::type>::value,

                    ::boost::is_empty<T1>::value,

                    ::boost::is_empty<T2>::value>::value> base;

public:

   typedef T1                                                 first_type;

   typedef T2                                                 second_type;

   typedef typename call_traits<first_type>::param_type       first_param_type;

   typedef typename call_traits<second_type>::param_type      second_param_type;

   typedef typename call_traits<first_type>::reference        first_reference;

   typedef typename call_traits<second_type>::reference       second_reference;

   typedef typename call_traits<first_type>::const_reference  first_const_reference;

   typedef typename call_traits<second_type>::const_reference second_const_reference;



            compressed_pair() : base() {}

            compressed_pair(first_param_type x, second_param_type y) : base(x, y) {}

   explicit compressed_pair(first_param_type x) : base(x) {}

   explicit compressed_pair(second_param_type y) : base(y) {}



   first_reference       first()       {return base::first();}

   first_const_reference first() const {return base::first();}



   second_reference       second()       {return base::second();}

   second_const_reference second() const {return base::second();}



   void swap(compressed_pair& y) { base::swap(y); }

};



// JM

// Partial specialisation for case where T1 == T2:

//

template <class T>

class compressed_pair<T, T>

   : private details::compressed_pair_imp<T, T,

             ::boost::details::compressed_pair_switch<

                    T,

                    T,

                    ::boost::is_same<typename remove_cv<T>::type, typename remove_cv<T>::type>::value,

                    ::boost::is_empty<T>::value,

                    ::boost::is_empty<T>::value>::value>

{

private:

   typedef details::compressed_pair_imp<T, T,

             ::boost::details::compressed_pair_switch<

                    T,

                    T,

                    ::boost::is_same<typename remove_cv<T>::type, typename remove_cv<T>::type>::value,

                    ::boost::is_empty<T>::value,

                    ::boost::is_empty<T>::value>::value> base;

public:

   typedef T                                                  first_type;

   typedef T                                                  second_type;

   typedef typename call_traits<first_type>::param_type       first_param_type;

   typedef typename call_traits<second_type>::param_type      second_param_type;

   typedef typename call_traits<first_type>::reference        first_reference;

   typedef typename call_traits<second_type>::reference       second_reference;

   typedef typename call_traits<first_type>::const_reference  first_const_reference;

   typedef typename call_traits<second_type>::const_reference second_const_reference;



            compressed_pair() : base() {}

            compressed_pair(first_param_type x, second_param_type y) : base(x, y) {}

#if !(defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x530))

   explicit 

#endif

      compressed_pair(first_param_type x) : base(x) {}



   first_reference       first()       {return base::first();}

   first_const_reference first() const {return base::first();}



   second_reference       second()       {return base::second();}

   second_const_reference second() const {return base::second();}



   void swap(::boost::compressed_pair<T,T>& y) { base::swap(y); }

};



template <class T1, class T2>

inline

void

swap(compressed_pair<T1, T2>& x, compressed_pair<T1, T2>& y)

{

   x.swap(y);

}



} // boost



#ifdef BOOST_MSVC

# pragma warning(pop)

#endif 



#endif // BOOST_DETAIL_COMPRESSED_PAIR_HPP



