#include <iostream>
#include <string>
#include <cmath>
#include <ostream>
/**
 * @brief A Tuple of objects.
 *
 * A maximum of 9 objects is supported.
 *
 * Use the following construction to access the individual elements.
 \code
 Tuple<std::string, float*, int> my_tuple;
 
 std:string& s = Element<0>::get(my_tuple);
 float*      p = Element<1>::get(my_tuple);
 
 // Access the third element in a generic way
 typedef ElementType<2, Tuple<std::string, float*, int> >::Type Type;
 Type&       i = Element<2>::get(my_tuple);
 \endcode
*/
  struct Nil
  {};
  template<typename T1, typename TT>
  class Pair
  {
  public:
    /**
     * @brief The type of the first field.
     */
    typedef T1 Type1;
    /**
     * @brief The type of the first field.
     */
    typedef TT Type2;
    
    /**
     * @brief Constructor
     */
    template<typename T2, typename T3, typename T4, typename T5,
	     typename T6, typename T7, typename T8, typename T9>
    Pair(const Type1& t1, const T2& t2, const T3& t3,
	 const T4& t4, const T5& t5, const T6& t6, const T7& t7,
	 const T8& t8, const T9& t9);
    /**
     * @brief Constructor
     */
    Pair(const Type1& t1, const TT& t2);
    /**
     * @brief Copy Constructor for implicit type conversion
     */
    template<typename U1, typename U2>
    Pair(const Pair<U1,U2>& other);    
    /**
     * @brief Assignment operator for implicit type conversion
     */
    template<typename U1, typename U2>
    Pair<T1,TT>& operator=(const Pair<U1,U2>& other);   
    /**
     * @brief Get the first value
     */
    Type1& first();
    const Type1& first() const;
    /**
     * @brief Get the second value
     */
    Type2& second();
    /**
     * @brief Get the second value
     */
    const Type2& second() const;
  private:
    /** @brief The value of the first field. */
    Type1 first_;
    /** @brief The value of the second field. */
    Type2 second_;
  };
  template<typename T1>
  class Pair<T1,Nil>
  {
  public:
    typedef T1 Type1;
    typedef Nil Type2;
    Pair(const Type1& first, const Nil&, const Nil&, const Nil&, const Nil&,
	 const Nil&, const Nil&, const Nil&, const Nil&);
    Pair(const Type1& first, const Nil&);
    template<typename T2>
    Pair(const Pair<T2,Nil>& other);
    template<typename T2>
    Pair<T1,Nil>& operator=(const Pair<T2,Nil>& other);
    Type1& first();
    const Type1& first() const;
  private:
    Type1 first_;
  };
/************************************************************************/
  template<typename T1, typename T2, typename T3, typename T4, typename T5,
	   typename T6, typename T7, typename T8, typename T9>
  struct TupleToPairs
  {
    typedef Pair<T1, typename TupleToPairs<T2,T3,T4,T5,T6,T7,T8,T9,Nil>::Type > 
            Type;
  };
  template<typename T1>
  struct TupleToPairs<T1,Nil,Nil,Nil,Nil,Nil,Nil,Nil,Nil>
  {
    typedef Pair<T1,Nil> Type;
  };
  template<typename T1, typename T2 = Nil, typename T3 = Nil,
           typename T4 = Nil, typename T5 = Nil, typename T6 = Nil, 
	   typename T7 = Nil, typename T8 = Nil, typename T9 = Nil>
  class Tuple : public TupleToPairs<T1,T2,T3,T4,T5,T6,T7,T8,T9>::Type
  {
  public:
    //! Type of the first Pair defining the Tuple
    typedef typename TupleToPairs<T1,T2,T3,T4,T5,T6,T7,T8,T9>::Type FirstPair;
    Tuple(const T1& t1=T1(), const T2& t2=T2(), const T3& t3=T3(), 
	  const T4& t4=T4(), const T5& t5=T5(), const T6& t6=T6(), 
	  const T7& t7=T7(), const T8& t8=T8(), const T9& t9=T8())
      : TupleToPairs<T1,T2,T3,T4,T5,T6,T7,T8,T9>::Type(t1, t2, t3,
						       t4, t5, t6, 
						       t7, t8, t9)
    {}
  };
  /**
   * @brief Print a pair or tuple.
   */
  template<typename T1, typename T2>
  inline std::ostream& operator<<(std::ostream& os, const Pair<T1,T2>& pair)
  {
    os<<pair.first()<<" "<<pair.second();
    return os;
  }
  template<typename T1>
  inline std::ostream& operator<<(std::ostream& os, const Pair<T1,Nil>& pair)
  {
    os<<pair.first();
    return os;
  }
/***************************************************************
 * Constructor and Descructor
 ****************************************************************/
  template<typename T1, typename TT>
  template<typename T2, typename T3, typename T4, typename T5,
	   typename T6, typename T7, typename T8, typename T9>
  inline Pair<T1,TT>::Pair(const Type1& first, const T2& t2, const T3& t3,
			   const T4& t4, const T5& t5, const T6& t6, 
			   const T7& t7, const T8& t8, const T9& t9)
    : first_(first), second_(t2,t3,t4,t5,t6,t7,t8,t9,Nil())
  {}
  template <typename T1, typename TT>
  inline Pair<T1, TT>::Pair(const Type1& first, const TT& second) 
    : first_(first), second_(second)
  {}
  template<typename T1, typename T2>
  template<typename U1, typename U2>
  inline Pair<T1,T2>::Pair(const Pair<U1,U2>& other)
    : first_(other.first_), second_(other.second_)
  {}
  template<typename T1, typename T2>
  template<typename U1, typename U2>
  inline Pair<T1,T2>& Pair<T1,T2>::operator=(const Pair<U1,U2>& other)
  {
    first_=other.first_;
    second_=other.second_;
    return *this;
  }

  template<typename T1, typename T2>
  inline T1& Pair<T1,T2>::first()
  {
    return first_;
  }
  template<typename T1, typename T2>
  inline const T1& Pair<T1,T2>::first() const
  {
    return first_;
  }
  template<typename T1, typename T2>
  inline T2& Pair<T1,T2>::second()
  {
    return second_;
  }
  template<typename T1, typename T2>
  inline const T2& Pair<T1,T2>::second() const
  {
    return second_;
  }
  template<typename T1>
  inline Pair<T1,Nil>::Pair(const Type1& first, const Nil&, const Nil&, const Nil&, const Nil&,
	 const Nil&, const Nil&, const Nil&, const Nil&)
    : first_(first)
  {}
  template <typename T1>
  inline Pair<T1, Nil>::Pair(const Type1& first, const Nil&)
    : first_(first)
  {}
  template<typename T1>
  template<typename T2>
  inline Pair<T1,Nil>::Pair(const Pair<T2,Nil>& other)
    : first_(other.first_)
  {}
  template<typename T1>
  template<typename T2>
  Pair<T1,Nil>& Pair<T1,Nil>::operator=(const Pair<T2,Nil>& other)
  {
    first_ = other.first_;
    return *this;
  }
  template<typename T1>
  inline T1& Pair<T1,Nil>::first()
  {
    return first_;
  }
  template<typename T1>
  inline const T1& Pair<T1,Nil>::first() const
  {
    return first_;
  }

/**********************************************************************/
#include "tupleutils.hh"  
/**********************************************************************/

template <class T>
void test(T& t) {
  std::cout << t << std::endl;
  std::cout << Length<T>::value << std::endl;
  std::cout << Element<1>::get(t) << " " << Element<4>::get(t) << std::endl;

  typename ElementType<2,T>::Type sum = Element<2>::get(t);
  sum += Element<3>::get(t);
  std::cout << sum << std::endl;
}

int main() {
  Tuple<int,double,double,int,std::string> tup(0,sqrt(3.),0.5,-1,"hallo");
  test(tup);
}

