/* Copyright (c) 1997-2004
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ifndef _POLYMAKE_PPAIR_H
#define _POLYMAKE_PPAIR_H "$Project: polymake $$Id: ppair.h,v 1.22 2004/06/22 16:22:42 gawrilow Exp $"

#include <utility>
#ifndef _POLYMAKE_TYPE_MANIP_H
#  include <type_manip.h>
#endif
#ifndef _POLYMAKE_NOTHING_H
#  include <nothing.h>
#endif

namespace pm {
using std::pair;

template <typename U1, typename U2, typename Target, bool _whole=isomorphic<pair<U1,U2>, Target>::answer>
struct pair_chooser {
   typedef const pair<U1,U2>& first_type;
   typedef first_type second_type;
   static first_type first(first_type u) { return u; }
   static first_type second(first_type u) { return u; }
};

template <typename U1, typename U2, typename Target>
struct pair_chooser<U1, U2, Target, false> {
   typedef typename function_argument<U1>::plus_ref first_type;
   typedef typename function_argument<U2>::plus_ref second_type;
   static first_type first(const pair<U1,U2>& u) { return u.first; }
   static second_type second(const pair<U1,U2>& u) { return u.second; }
};

template <typename Target, typename U1, typename U2> inline
typename pair_chooser<U1, U2, Target>::first_type
pair_init_first(const pair<U1,U2>& u)
{
   return pair_chooser<U1, U2, Target>::first(u);
}

template <typename Target, typename U1, typename U2> inline
typename pair_chooser<U1, U2, Target>::first_type
pair_init_second(const pair<U1,U2>& u)
{
   return pair_chooser<U1, U2, Target>::second(u);
}
}

namespace std {

template <typename T1, typename T2>
struct pair<T1&, T2> {
   typedef T1& first_type;
   typedef T2 second_type;
   /// data types referred to by the components
   typedef T1 first_data_type;
   typedef T2 second_data_type;

   first_type first;
   second_type second;

   pair(T1& first_arg, const T2& second_arg) : first(first_arg), second(second_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1&,U2>& p) : first(p.first), second(p.second) { }
};

template <typename T1, typename T2>
struct pair<T1, T2&> {
   typedef T1 first_type;
   typedef T2& second_type;

   typedef T1 first_data_type;
   typedef T2 second_data_type;

   first_type first;
   second_type second;

   pair(const T1& first_arg, T2& second_arg) : first(first_arg), second(second_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1,U2&>& p) : first(p.first), second(p.second) { }
};

template <typename T1, typename T2>
struct pair<T1&, T2&> {
   typedef T1& first_type;
   typedef T2& second_type;

   typedef T1 first_data_type;
   typedef T2 second_data_type;

   first_type first;
   second_type second;

   pair(T1& first_arg, T2& second_arg) : first(first_arg), second(second_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1&,U2&>& p) : first(p.first), second(p.second) { }
};

template <typename T1>
struct pair<T1, pm::nothing> {
   typedef T1 first_type;
   typedef pm::nothing second_type;

   typedef T1 first_data_type;
   typedef pm::nothing second_data_type;

   first_type first;
   static pm::nothing second;

   pair() { }

   explicit pair(const T1& first_arg) : first(first_arg) { }

   pair(const T1& first_arg, const pm::nothing&) : first(first_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1,U2>& u) : first(pm::pair_init_first<T1>(u)) { }
};

template <typename T1> pm::nothing pair<T1,pm::nothing>::second;

template <typename T1>
struct pair<T1&, pm::nothing> {
   typedef T1& first_type;
   typedef pm::nothing second_type;

   typedef T1 first_data_type;
   typedef pm::nothing second_data_type;

   first_type first;
   static pm::nothing second;

   explicit pair(T1& first_arg) : first(first_arg) { }

   pair(T1& first_arg, const pm::nothing&) : first(first_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1&,U2>& u) : first(pm::pair_init_first<T1>(u)) { }
};

template <typename T1> pm::nothing pair<T1&, pm::nothing>::second;

template <typename T2>
struct pair<pm::nothing, T2> {
   typedef pm::nothing first_type;
   typedef T2 second_type;

   typedef pm::nothing first_data_type;
   typedef T2 second_data_type;

   static pm::nothing first;
   second_type second;

   pair() { }

   explicit pair(const T2& second_arg) : second(second_arg) { }

   pair(const pm::nothing&, const T2& second_arg) : second(second_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1,U2>& u) : second(pm::pair_init_second<T2>(u)) { }
};

template <typename T2> pm::nothing pair<pm::nothing, T2>::first;

template <typename T2>
struct pair<pm::nothing, T2&> {
   typedef pm::nothing first_type;
   typedef T2& second_type;

   typedef pm::nothing first_data_type;
   typedef T2 second_data_type;

   static pm::nothing first;
   second_type second;

   explicit pair(T2& second_arg) : second(second_arg) { }

   pair(const pm::nothing&, T2& second_arg) : second(second_arg) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1,U2&>& u) : second(pm::pair_init_second<T2>(u)) { }
};

template <typename T2> pm::nothing pair<pm::nothing, T2&>::first;

template <>
struct pair<pm::nothing, pm::nothing> {
   typedef pm::nothing first_type;
   typedef pm::nothing second_type;

   typedef pm::nothing first_data_type;
   typedef pm::nothing second_data_type;

   static pm::nothing first, second;

   pair() { }
   explicit pair(const pm::nothing&) { }
   pair(const pm::nothing&, const pm::nothing&) { }

   template <typename U1, typename U2>
   explicit pair(const pair<U1,U2>&) { }
};

} // end namespace std

namespace pm {

template <typename T1, typename T2>
struct spec_object_traits< pair<T1,T2> > : spec_object_traits<is_composite> {
   typedef cons<T1,T2> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& x, Visitor& v)
   {
      v << x.first << x.second;
   }
};

namespace operations {

template <typename T1, typename T2>
struct pair_maker {
   typedef T1 first_argument_type;
   typedef T2 second_argument_type;
   typedef pair<T1,T2> result_type;

   result_type operator() (typename function_argument<T1>::type first_arg,
			   typename function_argument<T2>::type second_arg) const
   {
      return result_type(first_arg,second_arg);
   }
};

template <typename Ref1, typename Ref2>
struct ref_pair_maker {
   typedef Ref1 first_argument_type;
   typedef Ref2 second_argument_type;
   typedef pair<Ref1,Ref2> result_type;

   result_type operator() (typename function_argument<Ref1>::ref first_arg,
			   typename function_argument<Ref2>::ref second_arg) const
   {
      return result_type(first_arg,second_arg);
   }
};

template <typename PairRef>
struct take_first {
   typedef PairRef argument_type;
   typedef typename deref<PairRef>::type pair_type;
   typedef typename inherit_ref<typename pair_type::first_type, PairRef>::type result_type;

   result_type operator() (typename function_argument<PairRef>::ref arg) const
   {
      return arg.first;
   }
};

template <typename PairRef>
struct take_second {
   typedef PairRef argument_type;
   typedef typename deref<PairRef>::type pair_type;
   typedef typename inherit_ref<typename pair_type::second_type, PairRef>::type result_type;

   result_type operator() (typename function_argument<PairRef>::ref arg) const
   {
      return arg.second;
   }
};

} // end namespace operations

template <typename Container> inline
pair<typename Container::const_reference, typename Container::const_reference>
front_pair(const Container& c)
{
   typename Container::const_iterator it=c.begin();
   typename Container::const_reference e1=*it, e2=*(++it);
   return pair<typename Container::const_reference, typename Container::const_reference>(e1,e2);
}

template <typename Container> inline
pair<typename Container::const_reference, typename Container::const_reference>
back_pair(const Container& c)
{
   typename Container::const_reverse_iterator it=c.rbegin();
   typename Container::const_reference e1=*it, e2=*(++it);
   return pair<typename Container::const_reference, typename Container::const_reference>(e1,e2);
}

} // end namespace pm

namespace std_ext {
template <typename _Key> struct hash;

template <typename T1, typename T2>
struct hash< std::pair<T1,T2> > {
protected:
   hash<typename pm::deref<T1>::type> h_first;
   hash<typename pm::deref<T2>::type> h_second;

   static const size_t k=137;  // magic number
public:
   size_t operator() (const std::pair<T1,T2>& p) const
   {
      return k * h_first(p.first) + h_second(p.second);
   }
};

}

#endif // _POLYMAKE_PPAIR_H

// Local Variables:
// mode:C++
// End:
