// Copyright (C) 2010  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_UNORDERED_PAiR_Hh_
#define DLIB_UNORDERED_PAiR_Hh_

#include "serialize.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

    template <
        typename T
        >
    struct unordered_pair 
    {
        /*!
            REQUIREMENTS ON T
                T must be default constructable, copyable, and comparable using
                operator < and ==

            WHAT THIS OBJECT REPRESENTS
                This object is very similar to the std::pair struct except unordered_pair 
                is only capable of representing an unordered set of two items rather than 
                an ordered list of two items like std::pair.  

                This is best illustrated by example.  Suppose we have the following
                five variables:
                    std::pair<int,int> p1(1, 5), p2(5,1);
                    unordered_pair<int> up1(1,5), up2(5,1), up3(6,7);

                Then it is the case that:   
                    up1 == up2
                    up1 != up3
                    p1 != p2

               So the unordered_pair doesn't care about the order of the arguments.
               In this case, up1 and up2 are both equivalent.

        !*/

        typedef T type;
        typedef T first_type;
        typedef T second_type;

        const T first;
        const T second;

        unordered_pair() : first(), second() 
        /*!
            ensures
                - #first and #second are default initialized
        !*/ {}

        unordered_pair(
            const T& a, 
            const T& b
        ) :
            first( a < b ? a : b),
            second(a < b ? b : a)
        /*!
            ensures
                - #first <= #second
                - #first and #second contain copies of the items a and b.
        !*/ {}

        unordered_pair (
            const unordered_pair& p
        ) = default;
        /*!
            ensures
                - #*this is a copy of p
        !*/

        template <typename U>
        unordered_pair (
            const unordered_pair <U>& p
        ) :
            first(p.first),
            second(p.second)
        /*!
            ensures
                - #*this is a copy of p
        !*/ {}

        unordered_pair& operator= (
            const unordered_pair& item
        ) 
        /*!
            ensures
                - #*this == item
        !*/
        {
            const_cast<T&>(first) = item.first;
            const_cast<T&>(second) = item.second;
            return *this;
        }
    };

// ----------------------------------------------------------------------------------------

    template <typename T>
    bool operator==(const unordered_pair<T>& a, const unordered_pair <T>& b)
    {
        return a.first == b.first && a.second == b.second;
    }

    template <typename T>
    bool operator!=(const unordered_pair<T>& a, const unordered_pair <T>& b)
    {
        return !(a == b);
    }

    template <typename T>
    bool operator<(const unordered_pair<T>& a, const unordered_pair<T>& b)
    {
        return (a.first < b.first || (!(b.first < a.first) && a.second < b.second));
    }

    template <typename T>
    bool operator>(const unordered_pair<T>& a, const unordered_pair <T>& b)
    {
        return b < a;
    }

    template <typename T>
    bool operator<=(const unordered_pair<T>& a, const unordered_pair <T>& b)
    {
        return !(b < a);
    }

    template <typename T>
    bool operator>=(const unordered_pair<T>& a, const unordered_pair <T>& b)
    {
        return !(a < b);
    }

    template <typename T>
    unordered_pair<T> make_unordered_pair (const T& a, const T& b)
    {
        return unordered_pair<T>(a,b);
    }

// ----------------------------------------------------------------------------------------

    template <typename T>
    void serialize (
        const unordered_pair<T>& item,
        std::ostream& out
    )
    {
        try
        { 
            serialize(item.first,out); 
            serialize(item.second,out); 
        }
        catch (serialization_error& e)
        { throw serialization_error(e.info + "\n   while serializing object of type unordered_pair"); }
    }

    template <typename T>
    void deserialize (
        unordered_pair<T>& item,
        std::istream& in 
    )
    {
        try
        { 
            T a, b;
            deserialize(a,in); 
            deserialize(b,in); 
            item = make_unordered_pair(a,b);
        }
        catch (serialization_error& e)
        { throw serialization_error(e.info + "\n   while deserializing object of type unordered_pair"); }
    }

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_UNORDERED_PAiR_Hh_