/* Copyright (c) 1997-2006
   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_COMPARATORS_OPS_H
#define _POLYMAKE_COMPARATORS_OPS_H "$Project: polymake $$Id: comparators_ops.h 7481 2006-11-30 21:47:36Z gawrilow $"

#include <cmath>
#include <cstdlib>

#ifndef _POLYMAKE_COMPARATORS_BASIC_DEFS_H
#  include <comparators_basic_defs.h>
#endif
#ifndef _POLYMAKE_CONVERTERS_BASIC_DEFS_H
#  include <converters_basic_defs.h>
#endif
#ifndef _POLYMAKE_OPERATIONS_H
#  include <operations.h>
#endif

namespace pm {
namespace operations {

template <typename OpRef>
struct positive {
   typedef OpRef argument_type;
   typedef bool result_type;
   result_type operator() (typename function_argument<OpRef>::const_type x) const
   {
      return pm::sign(x)==cmp_gt;
   }
};

template <typename OpRef>
struct negative {
   typedef OpRef argument_type;
   typedef bool result_type;
   result_type operator() (typename function_argument<OpRef>::const_type x) const
   {
      return pm::sign(x)==cmp_lt;
   }
};

template <typename Comparator, typename T1, typename T2>
struct build_comparator_simple {
   typedef Comparator operation;
};

// resolving ambiguity
template <typename Comparator, typename T>
struct build_comparator_simple<Comparator, T, T> {
   typedef Comparator operation;
};

template <template <typename> class Comparator, typename T>
struct build_comparator_simple<Comparator<void>, T, T> {
   typedef Comparator<T> operation;
};

template <typename Comparator, typename T1, typename T2,
	  typename _discr=typename isomorphic_types<T1,T2>::discriminant,
	  bool _is_numeric=std::numeric_limits<T1>::is_specialized && std::numeric_limits<T2>::is_specialized,
	  bool _has_generic=has_generic_type<T1>::value && has_generic_type<T2>::value>
struct build_comparator_complex;

template <typename Comparator, typename T>
struct build_comparator_helper {
   typedef typename subst_template_type_param<Comparator, T>::type operation;
};

template <typename Comparator>
struct build_comparator_helper<Comparator, void> {
   typedef typename Comparator::no_matching_comparator_type_found type;
};

template <typename Comparator, typename T1, typename T2>
struct build_comparator_complex<Comparator, T1, T2, cons<is_scalar,is_scalar>, true, false>
   : build_comparator_helper<Comparator, typename sub_result<T1,T2>::type> { };

template <typename Comparator, typename T1, typename T2, typename Tag>
struct build_comparator_complex<Comparator, T1, T2, cons<Tag,Tag>, false, false>
   : build_comparator_helper<Comparator, typename least_derived< cons<T1,T2> >::type> { };

template <typename Comparator, typename T1, typename T2, typename Tag>
struct build_comparator_complex<Comparator, T1, T2, cons<Tag,Tag>, false, true>
   : build_comparator_helper<Comparator, typename T1::persistent_type> { };

template <template <typename> class Comparator, typename T1, typename T2>
struct build_comparator_simple<Comparator<void>, T1, T2> : build_comparator_complex<Comparator<void>, T1, T2> { };

template <typename Comparator, typename LeftRef, typename RightRef=LeftRef>
struct build_comparator_for_refs
   : build_comparator_simple<Comparator, typename deref<LeftRef>::type, typename deref<RightRef>::type> { };

template <typename Comparator, typename T, typename Model=typename object_traits<T>::model>
struct cmp_lex;

template <typename T=void> struct cmp : cmp_lex<cmp<void>, T> { };
template <> struct cmp<void> : incomplete { };

template <typename T>
struct cmp_lex<cmp<>, T, is_scalar>
   : if_else<std::numeric_limits<T>::is_specialized, cmp_numeric<T>, cmp_scalar<T> >::type { };

template <typename T=void> struct cmp_epsilon : cmp_lex<cmp_epsilon<void>, T> { };
template <> struct cmp_epsilon<void> : incomplete { };

template <typename T>
struct cmp_lex<cmp_epsilon<>, T, is_scalar> : cmp_scalar<T> {
   conv<T,bool> non_zero;
   using cmp_common<T>::operator();

   cmp_value operator() (typename function_argument<T>::const_type a,
			 typename function_argument<T>::const_type b) const
   {
      const T d=a-b;
      return non_zero(std::abs(d)) ? pm::sign(d) : cmp_eq;
   }
};

template <typename LeftRef, typename RightRef, cmp_value good>
struct cmp_rel {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef bool result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type a,
			   typename function_argument<RightRef>::const_type b) const
   {
      typename build_comparator_for_refs<cmp<>, LeftRef, RightRef>::operation
	 cmp_op;
      return cmp_op(a,b) == good;
   }
};

template <typename LeftRef, typename RightRef>
struct eq : cmp_rel<LeftRef,RightRef,cmp_eq> { };

template <typename LeftRef, typename RightRef>
struct lt : cmp_rel<LeftRef,RightRef,cmp_lt> { };

template <typename LeftRef, typename RightRef>
struct gt : cmp_rel<LeftRef,RightRef,cmp_gt> { };

template <typename LeftRef, typename RightRef>
struct ne : composed21<eq<LeftRef,RightRef>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef>
struct le : composed21<gt<LeftRef,RightRef>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef>
struct ge : composed21<lt<LeftRef,RightRef>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef>
struct max {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename if_else<identical_minus_const_ref<LeftRef, RightRef>::value,
			    typename function_argument<LeftRef>::const_type,
			    typename build_comparator_for_refs<cmp<>, LeftRef, RightRef>::operation::first_argument_type
                           >::type
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type a,
			   typename function_argument<RightRef>::const_type b) const
   {
      if (a>=b) return a;
      return b;
   }

   typename function_argument<LeftRef>::lvalue
   assign(typename function_argument<LeftRef>::lvalue a,
	  typename function_argument<RightRef>::const_type b) const
   {
      if (a<b) a=b;
      return a;
   }
};

template <typename LeftRef, typename RightRef>
struct min {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename if_else<identical_minus_const_ref<LeftRef, RightRef>::value,
			    typename function_argument<LeftRef>::const_type,
			    typename build_comparator_for_refs<cmp<>, LeftRef, RightRef>::operation::first_argument_type
                           >::type
      result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type a,
			   typename function_argument<RightRef>::const_type b) const
   {
      if (a<=b) return a;
      return b;
   }

   typename function_argument<LeftRef>::lvalue
   assign(typename function_argument<LeftRef>::lvalue a,
	  typename function_argument<RightRef>::const_type b) const
   {
      if (a>b) a=b;
      return a;
   }
};

template <typename OpRef>
struct abs {
   typedef OpRef argument_type;
   typedef typename deref<OpRef>::type result_type;

   result_type operator() (typename function_argument<OpRef>::const_type a) const
   {
      return std::abs(a);
   }

   typename function_argument<OpRef>::lvalue
   assign(typename function_argument<OpRef>::lvalue a) const
   {
      if (negative<OpRef>()(a))
	 neg<OpRef>().assign(a);
      return a;
   }
};

} // end namespace operations

template <typename Iterator1, typename Iterator2, typename LeftRef, typename RightRef>
struct binary_op_builder< operations::cmp<void>, Iterator1, Iterator2, LeftRef, RightRef>
   : empty_op_builder< typename operations::build_comparator_for_refs<operations::cmp<void>, LeftRef, RightRef>::operation > { };

template <typename Iterator1, typename Iterator2, typename LeftRef, typename RightRef>
struct binary_op_builder< operations::cmp_epsilon<void>, Iterator1, Iterator2, LeftRef, RightRef>
   : empty_op_builder< typename operations::build_comparator_for_refs<operations::cmp_epsilon<void>, LeftRef, RightRef>::operation > { };;

template <typename Comparator>
struct reverse_comparator {
   Comparator comparator;

   typedef typename Comparator::first_argument_type second_argument_type;
   typedef typename Comparator::second_argument_type first_argument_type;
   typedef typename Comparator::result_type result_type;

   reverse_comparator(const Comparator& comparator_arg=Comparator()) : comparator(comparator_arg) { }

   result_type operator() (typename function_argument<first_argument_type>::type a,
			   typename function_argument<second_argument_type>::type b) const
   {
      return comparator(b,a);
   }
};

template <typename Comparator, typename Iterator1, typename Iterator2, typename LeftRef, typename RightRef>
struct binary_op_builder< reverse_comparator<Comparator>, Iterator1, Iterator2, LeftRef, RightRef> {
   typedef reverse_comparator<Comparator> Operation;
   typedef binary_op_builder<Comparator, Iterator1, Iterator2, LeftRef, RightRef> comparator_builder;
   typedef typename comparator_builder::operation comparator;
   typedef reverse_comparator<comparator> operation;

   static const Operation& create(const Operation& op, True) { return op; }

   static operation create(const Operation& op, False)
   {
      return operation(comparator_builder::create(op.comparator));
   }

   static const bool idem=identical<Operation, operation>::value;

   static typename if_else<idem, const Operation&, operation>::type
   create(const Operation& op)
   {
      return create(op, bool2type<idem>());
   }
};

// Tag for various parameter lists
template <typename> class Comparator;

} // end namespace pm

#endif // _POLYMAKE_COMPARATORS_OPS_H

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