// A very small replacement for boost::tuple
// (c) Alexander Gessler, 2008 [alexander.gessler@gmx.net]

#ifndef BOOST_TUPLE_INCLUDED
#define BOOST_TUPLE_INCLUDED

namespace boost    {
    namespace detail    {

        // Represents an empty tuple slot (up to 5 supported)
        struct nulltype {};

        // For readable error messages
        struct tuple_component_idx_out_of_bounds;

        // To share some code for the const/nonconst versions of the getters
        template <bool b, typename T>
        struct ConstIf {
            typedef T t;
        };

        template <typename T>
        struct ConstIf<true,T> {
            typedef const T t;
        };

        // Predeclare some stuff
        template <typename, unsigned, typename, bool, unsigned> struct value_getter;

        // Helper to obtain the type of a tuple element
        template <typename T, unsigned NIDX, typename TNEXT, unsigned N /*= 0*/>
        struct type_getter    {
            typedef type_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,N> next_elem_getter;
            typedef typename next_elem_getter::type type;
        };

        template <typename T, unsigned NIDX, typename TNEXT >
        struct type_getter <T,NIDX,TNEXT,NIDX>    {
            typedef T type;
        };

        // Base class for all explicit specializations of list_elem
        template <typename T, unsigned NIDX, typename TNEXT >
        struct list_elem_base {

            // Store template parameters
            typedef TNEXT next_type;
            typedef T type;

            static const unsigned nidx = NIDX;
        };

        // Represents an element in the tuple component list
        template <typename T, unsigned NIDX, typename TNEXT >
        struct list_elem : list_elem_base<T,NIDX,TNEXT>{

            // Real members
            T me;
            TNEXT next;

            // Get the value of a specific tuple element
            template <unsigned N>
            typename type_getter<T,NIDX,TNEXT,N>::type& get () {
                value_getter <T,NIDX,TNEXT,false,N> s;
                return s(*this);
            }

            // Get the value of a specific tuple element
            template <unsigned N>
            const typename type_getter<T,NIDX,TNEXT,N>::type& get () const {
                value_getter <T,NIDX,TNEXT,true,N> s;
                return s(*this);
            }

            // Explicit cast
            template <typename T2, typename TNEXT2 >
            operator list_elem<T2,NIDX,TNEXT2> () const    {
                list_elem<T2,NIDX,TNEXT2> ret;
                ret.me   = (T2)me;
                ret.next = next;
                return ret;
            }

            // Recursively compare two elements (last element returns always true)
            bool operator == (const list_elem& s) const    {
                return (me == s.me && next == s.next);
            }
        };

        // Represents a non-used tuple element - the very last element processed
        template <typename TNEXT, unsigned NIDX  >
        struct list_elem<nulltype,NIDX,TNEXT> : list_elem_base<nulltype,NIDX,TNEXT> {
            template <unsigned N, bool IS_CONST = true> struct value_getter        {
                /* just dummy members to produce readable error messages */
                tuple_component_idx_out_of_bounds operator () (typename ConstIf<IS_CONST,list_elem>::t& me);
            };
            template <unsigned N> struct type_getter  {
                /* just dummy members to produce readable error messages */
                typedef tuple_component_idx_out_of_bounds type;
            };

            // dummy
            list_elem& operator = (const list_elem& other)    {
                return *this;
            }

            // dummy
            bool operator == (const list_elem& other)    {
                return true;
            }
        };

        // Represents the absolute end of the list
        typedef list_elem<nulltype,0,int> list_end;

        // Helper obtain to query the value of a tuple element
        // NOTE: This can't be a nested class as the compiler won't accept a full or
        // partial specialization of a nested class of a non-specialized template
        template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST, unsigned N>
        struct value_getter     {

            // calling list_elem
            typedef list_elem<T,NIDX,TNEXT> outer_elem;

            // typedef for the getter for next element
            typedef value_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,
                IS_CONST, N> next_value_getter;

            typename ConstIf<IS_CONST,typename type_getter<T,NIDX,TNEXT,N>::type>::t&
                operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {

                next_value_getter s;
                return s(me.next);
            }
        };

        template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST>
        struct value_getter <T,NIDX,TNEXT,IS_CONST,NIDX>    {
            typedef list_elem<T,NIDX,TNEXT> outer_elem;

            typename ConstIf<IS_CONST,T>::t& operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {
                return me.me;
            }
        };
    };

    // A very minimal implementation for up to 5 elements
    template <typename T0  = detail::nulltype,
              typename T1  = detail::nulltype,
              typename T2  = detail::nulltype,
              typename T3  = detail::nulltype,
              typename T4  = detail::nulltype>
    class tuple    {

        template <typename T0b,
              typename T1b,
              typename T2b,
              typename T3b,
              typename T4b >
        friend class tuple;

    private:

        typedef detail::list_elem<T0,0,
                    detail::list_elem<T1,1,
                        detail::list_elem<T2,2,
                            detail::list_elem<T3,3,
                                detail::list_elem<T4,4,
                                    detail::list_end > > > > > very_long;

        very_long m;

    public:

        // Get a specific tuple element
        template <unsigned N>
        typename detail::type_getter<T0,0,typename very_long::next_type, N>::type& get ()    {
            return m.get<N>();
        }

        // ... and the const version
        template <unsigned N>
        typename const detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () const    {
            return m.get<N>();
        }


        // comparison operators
        bool operator== (const tuple& other) const    {
            return m == other.m;
        }

        // ... and the other way round
        bool operator!= (const tuple& other) const    {
            return !(m == other.m);
        }

        // cast to another tuple - all single elements must be convertible
        template <    typename T0, typename T1,typename T2,
                    typename T3, typename T4>

        operator tuple <T0,T1,T2,T3,T4> () const {
            tuple <T0,T1,T2,T3,T4> s;
            s.m = (tuple <T0,T1,T2,T3,T4>::very_long)m;
            return s;
        }
    };

    // Another way to access an element ...
    template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
    inline typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
            tuple<T0,T1,T2,T3,T4>& m)    {
            return m.get<N>();
        }

    // ... and the const version
    template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
    inline const typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
            const tuple<T0,T1,T2,T3,T4>& m)    {
            return m.get<N>();
        }

    // Constructs a tuple with 5 elements
    template <typename T0,typename T1,typename T2,typename T3,typename T4>
    inline tuple <T0,T1,T2,T3,T4> make_tuple (const T0& t0,
        const T1& t1,const T2& t2,const T3& t3,const T4& t4) {

        tuple <T0,T1,T2,T3,T4> t;
        t.get<0>() = t0;
        t.get<1>() = t1;
        t.get<2>() = t2;
        t.get<3>() = t3;
        t.get<4>() = t4;
        return t;
    }

    // Constructs a tuple with 4 elements
    template <typename T0,typename T1,typename T2,typename T3>
    inline tuple <T0,T1,T2,T3> make_tuple (const T0& t0,
        const T1& t1,const T2& t2,const T3& t3) {
        tuple <T0,T1,T2,T3> t;
        t.get<0>() = t0;
        t.get<1>() = t1;
        t.get<2>() = t2;
        t.get<3>() = t3;
        return t;
    }

    // Constructs a tuple with 3 elements
    template <typename T0,typename T1,typename T2>
    inline tuple <T0,T1,T2> make_tuple (const T0& t0,
        const T1& t1,const T2& t2) {
        tuple <T0,T1,T2> t;
        t.get<0>() = t0;
        t.get<1>() = t1;
        t.get<2>() = t2;
        return t;
    }

    // Constructs a tuple with 2 elements (TODO: use std::pair instead!)
    template <typename T0,typename T1>
    inline tuple <T0,T1> make_tuple (const T0& t0,
        const T1& t1) {
        tuple <T0,T1> t;
        t.get<0>() = t0;
        t.get<1>() = t1;
        return t;
    }

    // Constructs a tuple with 1 elements (no comment ...)
    template <typename T0>
    inline tuple <T0> make_tuple (const T0& t0) {
        tuple <T0> t;
        t.get<0>() = t0;
        return t;
    }

    // Constructs a tuple with 0 elements (ehm? Try http://www.promillerechner.net)
    inline tuple <> make_tuple () {
        tuple <> t;
        return t;
    }
};

#endif // !! BOOST_TUPLE_INCLUDED
