/* 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_basic_defs.h 10003 2010-12-24 02:09:22Z gawrilow $
*/

#ifndef POLYMAKE_INTERNAL_COMPARATORS_BASIC_DEFS_H
#define POLYMAKE_INTERNAL_COMPARATORS_BASIC_DEFS_H

#include "polymake/internal/operations_basic_defs.h"

#include <limits>
#include <cmath>

#ifdef __GNUC__
#if __GNUC__ >= 4
#  include <tr1/functional>
#  define pm_hash_func_impl std::tr1::hash
#else
#  include <ext/hash_fun.h>
#  include <string>
#  define pm_hash_func_impl __gnu_cxx::hash
#endif  // __GNUC__ < 4
#endif

namespace pm {

enum cmp_value { cmp_lt=-1, cmp_eq=0, cmp_gt=1 };

template <typename T> inline
cmp_value _sign(const T& x, True)
{
   return x<0 ? cmp_lt : cmp_value(x>0);
}

template <typename T> inline
cmp_value _sign(const T& x, False)
{
   return cmp_value(x!=0);
}

template <typename T> inline
cmp_value sign(const T& x)
{
   return _sign(x, bool2type<std::numeric_limits<T>::is_signed>());
}

template <typename T, bool _is_max> struct extremal {};
template <typename T> struct maximal : extremal<T,true> {};
template <typename T> struct minimal : extremal<T,false> {};

namespace operations {

template <typename T1=bool, typename T2=T1>             // default can be an arbitrary type different from void
struct cmp_common {
   typedef T1 first_argument_type;
   typedef T2 second_argument_type;
   typedef cmp_value result_type;

   template <typename T, bool _is_max_l, bool _is_max_r>
   result_type operator() (const extremal<T,_is_max_l>&, const extremal<T,_is_max_r>&) const
   {
      return _is_max_l == _is_max_r ? cmp_eq : _is_max_l ? cmp_gt : cmp_lt;
   }

   template <typename T, bool _is_max_l>
   result_type operator() (const extremal<T,_is_max_l>&, const T&) const
   {
      return _is_max_l ? cmp_gt : cmp_lt;
   }

   template <typename T, bool _is_max_r>
   result_type operator() (const T&, const extremal<T,_is_max_r>&) const
   {
      return _is_max_r ? cmp_lt : cmp_gt;
   }
};

template <typename T1, typename T2=T1>
struct cmp_partial {
   template <typename Iterator2>
   cmp_value operator() (partial_left, typename function_argument<T1>::type a, const Iterator2&) const
   {
      return cmp_value(sign(a));
   }
   template <typename Iterator1>
   cmp_value operator() (partial_right, const Iterator1&, typename function_argument<T2>::type b) const
   {
      return cmp_value(-sign(b));
   }
};

template <typename T1, typename T2=T1>
struct cmp_opaque : cmp_common<T1,T2> {
   using cmp_common<T1,T2>::operator();

   cmp_value operator() (typename function_argument<T1>::type a, typename function_argument<T2>::type b) const
   {
      return a<b ? cmp_lt : cmp_value(a>b);
   }
};

template <typename T1, typename T2=T1>
struct cmp_scalar : cmp_common<T1,T2>, cmp_partial<T1,T2> {
protected:
   cmp_value _do(typename function_argument<T1>::type a, typename function_argument<T2>::type b, True) const
   {
      return sign(a-b);
   }
   cmp_value _do(typename function_argument<T1>::type a, typename function_argument<T2>::type b, False) const
   {
      return cmp_opaque<T1,T2>()(a,b);
   }
public:
   using cmp_common<T1,T2>::operator();
   using cmp_partial<T1,T2>::operator();

   cmp_value operator() (typename function_argument<T1>::type a, typename function_argument<T2>::type b) const
   {
      return _do(a,b,bool2type<std::numeric_limits<T1>::is_integer && std::numeric_limits<T2>::is_integer &&
                               std::numeric_limits<T1>::is_signed  && std::numeric_limits<T2>::is_signed>());
   }
};

} // end namespace operations

template <typename T1, typename T2> inline
T1& assign_max(T1& max, const T2& x) { if (max<x) max=x; return max; }

template <typename T1, typename T2> inline
T1& assign_min(T1& min, const T2& x) { if (min>x) min=x; return min; }

template <typename T1, typename T2, typename T3> inline
void assign_min_max(T1& min, T2& max, const T3& x)
{
   if (min>x) min=x; else if (max<x) max=x;
}

template <typename T, typename Tag=typename object_traits<T>::generic_tag>
struct hash_func;

template <typename T>
struct hash_func<T, is_scalar> : public pm_hash_func_impl<T> {};

#undef pm_hash_func_impl

#ifdef __GNUC__
#if __GNUC__ < 4

template <typename T>
struct hash_func<T*, is_opaque> {
   size_t operator() (T* x) const { return reinterpret_cast<size_t>(x); }
};

template <typename _Char, _Traits, typename _Alloc>
struct hash_func< std::basic_string<_Char,_Traits,_Alloc>, is_scalar > : hash_func<const _Char*> {
   size_t operator() (const std::basic_string<_Char,_Traits,_Alloc>& s) const
   {
      return hash_func<const _Char*>::operator() (s.c_str());
   }
};
#endif
#endif

using std::abs;
using std::isfinite;
inline int isinf(double x) { if (std::isinf(x)) return (x>0)*2-1; else return 0; }

template <typename T, bool _is_max>
struct spec_object_traits< extremal<T,_is_max> > : spec_object_traits<T> {};

template <typename T>
struct spec_object_traits< maximal<T> > : spec_object_traits<T> {};

template <typename T>
struct spec_object_traits< minimal<T> > : spec_object_traits<T> {};

} // end namespace pm

namespace std {
   template <>
   struct numeric_limits<pm::cmp_value> : numeric_limits<short> {
      static pm::cmp_value min() throw() { return pm::cmp_lt; }
      static pm::cmp_value max() throw() { return pm::cmp_gt; }
      static const int digits = 1;
   };
}

#endif // POLYMAKE_INTERNAL_COMPARATORS_BASIC_DEFS_H

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