/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: comparators.h 9991 2010-12-12 23:36:28Z gawrilow $
*/

#ifndef POLYMAKE_INTERNAL_COMPARATORS_H
#define POLYMAKE_INTERNAL_COMPARATORS_H

#include "polymake/internal/comparators_ops.h"
#include "polymake/TransformedContainer.h"

namespace pm {
namespace operations {

template <typename Container1, typename Container2,
          int dim1=object_traits<Container1>::dimension,
          int dim2=object_traits<Container2>::dimension>
struct cmp_lex_containers;

template <typename Container1, typename Container2, int dim>
struct cmp_lex_containers<Container1,Container2,dim,dim> {
   typedef cmp_lex_containers<typename object_traits<Container1>::serialized,
                              typename object_traits<Container2>::serialized> cmp_ser;

   template <typename Comparator>
   cmp_value _do(const Container1& a, const Container2& b, const Comparator& cmp_el) const
   {
      return cmp_ser()._do(object_traits<Container1>::serialize(a), object_traits<Container2>::serialize(b), cmp_el);
   }
};

template <typename Container1, typename Container2>
struct cmp_lex_containers<Container1,Container2,1,1> {
protected:
   template <typename Iterator> static
   bool at_end1(const Iterator& it, end_sensitive*)
   {
      return static_cast<const typename Iterator::first_type&>(it).at_end();
   }
   template <typename Iterator> static
   bool at_end1(const Iterator&, void*)
   {
      return false;
   }
   template <typename Iterator> static
   bool at_end2(const Iterator& it, end_sensitive*)
   {
      return it.second.at_end();
   }
   template <typename Iterator> static
   bool at_end2(const Iterator&, void*)
   {
      return false;
   }

   template <typename Iterator, typename _end1, typename _end2> static
   cmp_value _eval(Iterator it, _end1 *e1, _end2 *e2)
   {
      for (;;) {
         if (at_end1(it,e1))
            return at_end2(it,e2) ? cmp_eq : cmp_lt;
         if (at_end2(it,e2))
            return cmp_gt;
         cmp_value result=*it;
         if (result != cmp_eq)
            return result;
         ++it;
      }
   }

   template <typename Comparator> static
   cmp_value _do(const Container1& a, const Container2& b, const Comparator& cmp_el, False)
   {
      typedef typename if_else<object_classifier::what_is<Container1>::value==object_classifier::is_constant,
                               void, end_sensitive>::type
         feature1;
      typedef typename if_else<object_classifier::what_is<Container2>::value==object_classifier::is_constant,
                               void, end_sensitive>::type
         feature2;
      typedef TransformedContainerPair< masquerade_add_features<const Container1&, feature1>,
                                        masquerade_add_features<const Container2&, feature2>, Comparator > TP;
      return _eval(entire(TP(a,b,cmp_el)), (feature1*)0, (feature2*)0);
   }

   template <typename Comparator> static
   cmp_value _do(const Container1& a, const Container2& b, const Comparator& cmp_el, True)
   {
      const cmp_value result=first_differ(entire(attach_operation(a,b,cmp_el)), cmp_eq);
      return result!=cmp_eq ? result : pm::sign(get_dim(a)-get_dim(b));
   }

public:
   template <typename Comparator>
   cmp_value _do(const Container1& a, const Container2& b, const Comparator& cmp_el) const
   {
      const bool _got_sparse=check_container_feature<Container1,sparse>::value ||
                             check_container_feature<Container2,sparse>::value;
      return _do(a, b, cmp_el, bool2type<_got_sparse>());
   }

   template <typename Iterator2, typename Comparator>
   cmp_value _do(partial_left, const Container1& a, const Iterator2&, const Comparator& cmp_el) const
   {
      cmp_value ret=cmp_eq;
      for (typename Entire<Container1>::const_iterator it=entire(a); !it.at_end(); ++it)
         if ((ret=cmp_el(partial_left(), *it, it)) != cmp_eq) break;
      return ret;
   }

   template <typename Iterator1, typename Comparator>
   cmp_value _do(partial_right, const Iterator1&, const Container2& b, const Comparator& cmp_el) const
   {
      cmp_value ret=cmp_eq;
      for (typename Entire<Container2>::const_iterator it=entire(b); !it.at_end(); ++it)
         if ((ret=cmp_el(partial_right(), it, *it)) != cmp_eq) break;
      return ret;
   }
};

template <typename Composite1, typename Composite2,
          int l1=list_length<typename object_traits<Composite1>::elements>::value,
          int l2=list_length<typename object_traits<Composite2>::elements>::value>
struct cmp_lex_composite;

template <typename Composite1, typename Composite2, int l>
struct cmp_lex_composite<Composite1,Composite2,l,l> {
protected:
   static const int last=l-1;

   // SUGGESTION: make provisions for a list of comparators?

   template <typename Comparator, int i> static
   cmp_value _do(const Composite1& a, const Composite2& b, const Comparator& cmp_el, int2type<i>)
   {
      cmp_value result=cmp_el(visit_n_th(a,int2type<i>()), visit_n_th(b,int2type<i>()));
      const int next= i+1<l ? i+1 : i;
      if (i<next && result==cmp_eq) result=_do(a,b,cmp_el,int2type<next>());
      return result;
   }
public:
   template <typename Comparator> static
   cmp_value _do(const Composite1& a, const Composite2& b, const Comparator& cmp_el)
   {
      return _do(a,b,cmp_el,int2type<0>());
   }
};

template <typename T1, typename T2, typename Tag>
struct build_comparator<T1, T2, is_container, is_container, cons<Tag,Tag>, true> {
   typedef cmp_lex_containers<T1,T2> type;
   static const bool partially_defined=build_comparator<typename T1::value_type, typename T2::value_type>::partially_defined;
};

template <typename T1, typename T2, typename Tag>
struct build_comparator<T1, T2, is_composite, is_composite, cons<Tag,Tag>, true> {
   typedef cmp_lex_composite<T1,T2> type;
   static const bool partially_defined=false;
};

template <typename T, typename Tag>
struct build_comparator<T, T, is_opaque, is_opaque, cons<Tag,Tag>, true> {
   typedef cmp_opaque<T,T> type;
   static const bool partially_defined=is_partially_defined<type>::value;
};

} // end namespace operations

namespace operators {

template <typename Left, typename Right> inline
bool operator!= (const Left& l, const Right& r)
{
   return !(l==r);
}

template <typename Left, typename Right> inline
bool operator> (const Left& l, const Right& r)
{
   return r<l;
}

template <typename Left, typename Right> inline
bool operator<= (const Left& l, const Right& r)
{
   return !(r<l);
}

template <typename Left, typename Right> inline
bool operator>= (const Left& l, const Right& r)
{
   return !(l<r);
}

} // end namespace operators

template <typename T, bool _is_max> inline
bool operator== (const T&, const extremal<T,_is_max>&) { return false; }
template <typename T, bool _is_max> inline
bool operator!= (const T&, const extremal<T,_is_max>&) { return true; }
template <typename T, bool _is_max> inline
bool operator< (const T&, const extremal<T,_is_max>&) { return _is_max; }
template <typename T, bool _is_max> inline
bool operator<= (const T&, const extremal<T,_is_max>&) { return _is_max; }
template <typename T, bool _is_max> inline
bool operator> (const T&, const extremal<T,_is_max>&) { return !_is_max; }
template <typename T, bool _is_max> inline
bool operator>= (const T&, const extremal<T,_is_max>&) { return !_is_max; }
template <typename T, bool _is_max> inline
bool operator== (const extremal<T,_is_max>&, const T&) { return false; }
template <typename T, bool _is_max> inline
bool operator!= (const extremal<T,_is_max>&, const T&) { return true; }
template <typename T, bool _is_max> inline
bool operator< (const extremal<T,_is_max>&, const T&) { return !_is_max; }
template <typename T, bool _is_max> inline
bool operator<= (const extremal<T,_is_max>&, const T&) { return !_is_max; }
template <typename T, bool _is_max> inline
bool operator> (const extremal<T,_is_max>&, const T&) { return _is_max; }
template <typename T, bool _is_max> inline
bool operator>= (const extremal<T,_is_max>&, const T&) { return _is_max; }

template <typename T, bool _is_max1, bool _is_max2> inline
bool operator== (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1==_is_max2; }
template <typename T, bool _is_max1, bool _is_max2> inline
bool operator!= (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1!=_is_max2; }
template <typename T, bool _is_max1, bool _is_max2> inline
bool operator< (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1<_is_max2; }
template <typename T, bool _is_max1, bool _is_max2> inline
bool operator<= (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1<=_is_max2; }
template <typename T, bool _is_max1, bool _is_max2> inline
bool operator> (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1>_is_max2; }
template <typename T, bool _is_max1, bool _is_max2> inline
bool operator>= (const extremal<T,_is_max1>&, const extremal<T,_is_max2>&) { return _is_max1>=_is_max2; }

// for hash_set and similar
struct is_unordered_set;

template <typename T>
struct hash_func<T, is_container> {
protected:
   hash_func<typename T::value_type> hash_elem;

public:
   size_t operator() (const T& x) const
   {
      size_t h=0;
      int i=1;
      for (typename Entire<T>::const_iterator it=entire(x); !it.at_end(); ++it, ++i) {
         h += hash_elem(*it) * i;
      }
      return h;
   }
};

template <typename T>
struct hash_composite : hash_composite<typename list_tail<T>::type> {
   typedef hash_composite<typename list_tail<T>::type> super;
   static const int magic_number=137;

   typedef typename list_head<T>::type element_type;
   typedef typename deref<element_type>::type value_type;
   super& operator<< (typename attrib<element_type>::plus_const_ref x)
   {
      if (super::magic_number) this->value *= super::magic_number;
      this->value += hash_elem(x);
      return *this;
   }

   hash_func<value_type> hash_elem;
};

template <>
struct hash_composite<void> {
   static const int magic_number=0;
   size_t value;

   hash_composite() : value(0) {}
};

template <typename T>
struct hash_func<T, is_composite> {
public:
   size_t operator() (const T& x) const
   {
      hash_composite<typename object_traits<T>::elements> h;
      object_traits<T>::visit_elements(x, h);
      return h.value;
   }
};

} // end namespace pm

namespace polymake {
   using pm::sign;
   using pm::assign_max;
   using pm::assign_min;
   using pm::assign_min_max;
   using pm::maximal;
   using pm::minimal;
   using std::abs;
   using pm::abs;
   using std::isinf;
   using pm::isinf;
   using std::isfinite;
   using pm::isfinite;

   namespace operations {
      typedef BuildUnary<pm::operations::positive> positive;
      typedef BuildUnary<pm::operations::negative> negative;

      using pm::operations::cmp;
      using pm::operations::cmp_epsilon;
      typedef BuildBinary<pm::operations::eq> eq;
      typedef BuildBinary<pm::operations::ne> ne;
      typedef BuildBinary<pm::operations::lt> lt;
      typedef BuildBinary<pm::operations::le> le;
      typedef BuildBinary<pm::operations::gt> gt;
      typedef BuildBinary<pm::operations::ge> ge;
      typedef BuildBinary<pm::operations::max> max;
      typedef BuildBinary<pm::operations::min> min;
      typedef BuildUnary<pm::operations::abs_value> abs_value;
   }
}

#endif // POLYMAKE_INTERNAL_COMPARATORS_H

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
