/*!
Helper traits for working with tuples.
*/

/// A trait for removing the first item from a tuple and returning the rest.
/// Only implemented for a few low dimensions.
pub trait TupleOps : Sized {
    /// First element of the tuple.
    type Head;
    /// Rest of the tuple.
    type Tail;

    /// Nested 2-tuple conversioon of an n-tuple.
    type Nested;

    /// Remove the first item from a tuple and return the rest.
    fn tail(self) -> Self::Tail { self.split_tail().1 }

    /// Return the first element and the rest of the tuple
    fn split_tail(self) -> (Self::Head, Self::Tail);

    /// Convert `(a,b,c,…)` into `(a, (b, (c, …)))`.
    fn nest(self) -> Self::Nested;

    /// Convert `(a, (b, (c, …)))` into `(a,b,c,…)`.
    fn from_nested(nested : Self::Nested) -> Self;

    // Convert `head` and `tail` into `(head, tail…)`.
    fn from_head_tail(head : Self::Head, tail : Self::Tail) -> Self;
}

macro_rules! nest {
    ($t1:ident) => { ($t1,) };
    ($t1:ident $($ts:ident)+) => { ($t1, nest!($($ts)+)) };
}

macro_rules! impl_tuple_ops {
    (@do $type1:ident $($types:ident)*, $var1:ident $($vars:ident)*) => {
        impl<$type1, $($types),*> TupleOps for ($type1, $($types),*) {
            type Head = $type1;
            type Tail = ($($types,)*);
            type Nested = nest!($type1 $($types)*);

            #[inline]
            fn split_tail(self) -> (Self::Head, Self::Tail) {
                let ($var1, $($vars),*) = self;
                ($var1, ($($vars,)*))
            }

            #[inline]
            fn nest(self) -> Self::Nested {
                let ($var1, $($vars),*) = self;
                nest!($var1 $($vars)*)
            }

            #[inline]
            fn from_nested(nested : Self::Nested) -> Self {
                let nest!($var1 $($vars)*) = nested;
                ($var1, $($vars),*)
            }

            #[inline]
            fn from_head_tail($var1 : Self::Head, tail : Self::Tail) -> Self {
                let ($($vars,)*) = tail;
                ($var1, $($vars),*)
            }
        }
    };
    ($type1:ident $($types:ident)+, $var1:ident $($vars:ident)+) => {
        impl_tuple_ops!(@do $type1 $($types)+, $var1 $($vars)+);
        impl_tuple_ops!($($types)+, $($vars)+);
    };
    ($type1:ident, $var1:ident) => {
        impl_tuple_ops!(@do $type1, $var1);
    };
}

impl_tuple_ops!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z,
                a b c d e f g h i j k l m n o p q r s t u v w x y z);


