/* 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_ops.h 9991 2010-12-12 23:36:28Z gawrilow $
*/

#ifndef POLYMAKE_INTERNAL_COMPARATORS_OPS_H
#define POLYMAKE_INTERNAL_COMPARATORS_OPS_H

#include <cmath>
#include <cstdlib>

#include "polymake/internal/comparators_basic_defs.h"
#include "polymake/internal/converters_basic_defs.h"
#include "polymake/internal/operations.h"

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 T1, typename T2,
          typename Model1=typename object_traits<T1>::model, typename Model2=typename object_traits<T2>::model,
          typename _discr=typename isomorphic_types<T1,T2>::discriminant,
          bool _enabled=isomorphic_types<T1,T2>::value>
struct build_comparator;

template <typename T1, typename T2>
struct build_comparator<T1, T2, is_scalar, is_scalar, cons<is_scalar,is_scalar>, true> {
   struct type : cmp_scalar<T1,T2> {
      template <typename Comparator>
      cmp_value _do(typename function_argument<T1>::type a, typename function_argument<T2>::type b, const Comparator&) const
      {
         return static_cast<const cmp_scalar<T1,T2>&>(*this)(a,b);
      }
   };

   static const bool partially_defined=true;
};

struct cmp : cmp_common<> {
   using cmp_common<>::operator();

   template <typename Left, typename Right>
   cmp_value operator()(const Left& l, const Right& r) const
   {
      typename build_comparator<Left,Right>::type cmp_op;
      return cmp_op._do(l,r,*this);
   }

   template <typename Left, typename Iterator2>
   cmp_value operator() (typename enable_if<partial_left, build_comparator<Left,Left>::partially_defined>::type,
                         const Left& a, const Iterator2& it) const
   {
      return _do(partial_left(),a,it, derived_from_instance2<typename build_comparator<Left,Left>::type, cmp_scalar>());
   }

   template <typename Right, typename Iterator1>
   cmp_value operator() (typename enable_if<partial_right, build_comparator<Right,Right>::partially_defined>::type,
                         const Iterator1& it, const Right& b) const
   {
      return _do(partial_right(),it,b, derived_from_instance2<typename build_comparator<Right,Right>::type, cmp_scalar>());
   }
protected:
   template <typename Left, typename Iterator2>
   cmp_value _do(partial_left, const Left& a, const Iterator2& it, True) const
   {
      return cmp_scalar<Left>()(partial_left(),a,it);
   }

   template <typename Right, typename Iterator1>
   cmp_value _do(partial_right, const Iterator1& it, const Right& b, True) const
   {
      return cmp_scalar<Right>()(partial_right(),it,b);
   }

   template <typename Left, typename Iterator2>
   cmp_value _do(partial_left, const Left& a, const Iterator2& it, False) const
   {
      typename build_comparator<Left,Left>::type cmp_op;
      return cmp_op._do(partial_left(),a,it,*this);
   }

   template <typename Right, typename Iterator1, typename CmpOp>
   cmp_value _do(partial_right, const Iterator1& it, const Right& b, False) const
   {
      typename build_comparator<Right,Right>::type cmp_op;
      return cmp_op._do(partial_right(),it,b,*this);
   }
};

template <typename T>
struct cmp_epsilon : cmp_common<T> {
protected:
   conv<T,bool> epsilon_test;

public:
   using cmp_common<T>::operator();

   template <typename Left, typename Right>
   cmp_value operator() (const Left& a, const Right& b) const
   {
      return _do(a,b, derived_from_instance2<typename build_comparator<Left,Right>::type, cmp_scalar>());
   }

   template <typename Left, typename Iterator2>
   cmp_value operator() (typename enable_if<partial_left, build_comparator<Left,Left>::partially_defined>::type,
                         const Left& a, const Iterator2& it) const
   {
      return _do(partial_left(),a,it, derived_from_instance2<typename build_comparator<Left,Left>::type, cmp_scalar>());
   }

   template <typename Right, typename Iterator1>
   cmp_value operator() (typename enable_if<partial_right, build_comparator<Right,Right>::partially_defined>::type,
                         const Iterator1& it, const Right& b) const
   {
      typename build_comparator<Right,Right>::type cmp_op;
      return _do(partial_right(),it,b, derived_from_instance2<typename build_comparator<Right,Right>::type, cmp_scalar>());
   }

protected:
   template <typename Left, typename Right>
   cmp_value _do(const Left& a, const Right& b, True) const
   {
      return epsilon_test(a-b) ? cmp_scalar<Left,Right>()(a,b) : cmp_eq;
   }

   template <typename Left, typename Right>
   cmp_value _do(const Left& a, const Right& b, False) const
   {
      typename build_comparator<Left,Right>::type cmp_op;
      return cmp_op._do(a,b,*this);
   }

   template <typename Left, typename Iterator2>
   cmp_value _do(partial_left, const Left& a, const Iterator2&, True) const
   {
      return epsilon_test(a) ? cmp_value(sign(a)) : cmp_eq;
   }

   template <typename Right, typename Iterator1>
   cmp_value _do(partial_right, const Iterator1&, const Right& b, True) const
   {
      return epsilon_test(b) ? cmp_value(-sign(b)) : cmp_eq;
   }

   template <typename Left, typename Iterator2>
   cmp_value _do(partial_left, const Left& a, const Iterator2& it, False) const
   {
      typename build_comparator<Left,Left>::type cmp_op;
      return cmp_op._do(partial_left(),a,it,*this);
   }

   template <typename Right, typename Iterator1>
   cmp_value _do(partial_right, const Iterator1& it, const Right& b, False) const
   {
      typename build_comparator<Right,Right>::type cmp_op;
      return cmp_op._do(partial_right(),it,b,*this);
   }
};

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
   {
      cmp 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 Comparator, typename T, typename Model=typename object_traits<T>::model>
struct cmp2eq {
   typedef T first_argument_type;
   typedef T second_argument_type;
   typedef bool result_type;

   result_type operator() (typename function_argument<T>::type a, typename function_argument<T>::type b) const
   {
      Comparator cmp;
      return cmp(a,b)==cmp_eq;
   }
};

template <typename T>
struct cmp2eq<cmp, T, is_opaque> {
   // not necessarily ordered, but should have at least the equality operator
   typedef T first_argument_type;
   typedef T second_argument_type;
   typedef bool result_type;

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

template <typename Comparator, typename T>
struct cmp2less {
   typedef T first_argument_type;
   typedef T second_argument_type;
   typedef bool result_type;

   result_type operator() (typename function_argument<T>::type a, typename function_argument<T>::type b) const
   {
      Comparator cmp;
      return cmp(a,b)==cmp_lt;
   }
};

template <typename Left, typename Right,
          typename TagL=typename object_traits<Left>::generic_tag,
          typename TagR=typename object_traits<Right>::generic_tag,
          bool _is_numeric=std::numeric_limits<Left>::is_specialized && std::numeric_limits<Right>::is_specialized>
struct minmax : sub_result<Left,Right> {};

template <typename Left, typename Right, typename Tag1, typename Tag2>
struct minmax<Left,Right,Tag1,Tag2,false> : least_derived< cons<Left,Right> > {};

template <typename Left, typename Right, typename Tag>
struct minmax<Left,Right,Tag,Tag,false> : identical<typename object_traits<Left>::persistent_type, typename object_traits<Right>::persistent_type> {};

template <typename T, typename Tag>
struct minmax<T,T,Tag,Tag,false> {
   typedef T type;
};

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 minmax<first_argument_type, second_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;
   }

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

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 minmax<first_argument_type, second_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;
   }

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

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

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

   void assign(typename lvalue_arg<OpRef>::type a) const
   {
      if (negative<OpRef>()(a)) inv_sign(a);
   }
};

} // end namespace operations

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

} // end namespace pm

#endif // POLYMAKE_INTERNAL_COMPARATORS_OPS_H

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