/* 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_COMPARATORS_H
#define _POLYMAKE_COMPARATORS_H "$Project: polymake $$Id: comparators.h,v 1.24 2004/09/20 15:07:47 gawrilow Exp $"

#if defined(__GNUC__) && __GNUC_MINOR__<3 && !defined(__APPLE__) && !defined(__COMO__)
#pragma interface
#endif

#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_ITERATOR_ZIPPER_H
#  include <iterator_zipper.h>
#endif

namespace pm {
namespace operations {

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

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

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct cmp_impl;

template <typename LeftRef, typename RightRef>
struct cmp_impl<LeftRef, RightRef, false, cons<is_scalar,is_scalar> >
   : cmp_scalar<typename deref<LeftRef>::type, typename deref<RightRef>::type> { };

template <typename LeftRef, typename RightRef>
struct cmp_impl<LeftRef, RightRef, false, cons<is_composite,is_composite> >
   : cmp_atomic<typename deref<LeftRef>::type, typename deref<RightRef>::type> { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct cmp_epsilon_impl;

template <typename LeftRef, typename RightRef>
struct cmp_epsilon_impl<LeftRef, RightRef, false, cons<is_scalar,is_scalar> > {
   typedef typename deref<LeftRef>::type first_argument_type;
   typedef typename deref<RightRef>::type second_argument_type;
   typedef cmp_value result_type;

   typedef typename arith_result<first_argument_type, second_argument_type>::type diff_type;
   conv<diff_type,bool> non_zero;

   result_type operator() (typename function_argument<LeftRef>::type a,
			   typename function_argument<RightRef>::type b) const
   {
      const diff_type d=a-b;
      return non_zero(std::abs(d)) ? pm::sign(d) : cmp_eq;
   }
};

struct cmp_based { }; // just a tag

template <typename LeftRef, typename RightRef, bool check, typename Discr, cmp_value good>
struct cmp_rel : cmp_impl<LeftRef,RightRef,check,Discr>, cmp_based {
   typedef cmp_impl<LeftRef,RightRef,check,Discr> super;
   typedef bool result_type;

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

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct eq_impl : cmp_rel<LeftRef,RightRef,check,Discr,cmp_eq> { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct lt_impl : cmp_rel<LeftRef,RightRef,check,Discr,cmp_lt> { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct gt_impl : cmp_rel<LeftRef,RightRef,check,Discr,cmp_gt> { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct ne_impl : composed21<eq_impl<LeftRef,RightRef,check,Discr>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct le_impl : composed21<gt_impl<LeftRef,RightRef,check,Discr>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef, bool check=false,
          typename Discr=typename isomorphic<typename deref<LeftRef>::type, typename deref<RightRef>::type>::discriminant>
struct ge_impl : composed21<lt_impl<LeftRef,RightRef,check,Discr>, std::logical_not<bool> > { };

template <typename LeftRef, typename RightRef>
struct cmp : cmp_impl<LeftRef, RightRef> { };

template <typename LeftRef, typename RightRef>
struct cmp_epsilon : cmp_epsilon_impl<LeftRef, RightRef> { };

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

template <typename LeftRef, typename RightRef>
struct ne : ne_impl<LeftRef, RightRef> { };

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

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

template <typename LeftRef, typename RightRef>
struct le : le_impl<LeftRef, RightRef> { };

template <typename LeftRef, typename RightRef>
struct ge : ge_impl<LeftRef, RightRef> { };

template <typename LeftRef, typename RightRef>
struct max {
   typedef typename deref<LeftRef>::type first_argument_type;
   typedef typename deref<RightRef>::type second_argument_type;
   typedef typename if_else<identical<first_argument_type, second_argument_type>::answer,
			    typename function_argument<LeftRef>::type,
			    typename arith_result<first_argument_type, second_argument_type>::type
                           >::result
      result_type;

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

   first_argument_type& assign(first_argument_type& a, typename function_argument<RightRef>::type b) const
   {
      if (a<b) a=b;
      return a;
   }
};

template <typename LeftRef, typename RightRef>
struct min {
   typedef typename deref<LeftRef>::type first_argument_type;
   typedef typename deref<RightRef>::type second_argument_type;
   typedef typename if_else<identical<first_argument_type, second_argument_type>::answer,
			    typename function_argument<LeftRef>::type,
			    typename arith_result<first_argument_type, second_argument_type>::type
                           >::result
      result_type;

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

   first_argument_type& assign(first_argument_type& a, typename function_argument<RightRef>::type b) const
   {
      if (a>b) a=b;
      return a;
   }
};

} // 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 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>::answer;

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

} // end namespace pm

namespace polymake {
   using pm::sign;
   using pm::assign_max;
   using pm::assign_min;

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

      typedef BuildBinary<pm::operations::cmp> cmp;
      typedef BuildBinary<pm::operations::cmp_epsilon> 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;
   }
}

#endif // _POLYMAKE_COMPARATORS_H

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