/* 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_OPERATIONS_BASIC_DEFS_H
#define _POLYMAKE_OPERATIONS_BASIC_DEFS_H "$Project: polymake $$Id: operations_basic_defs.h,v 1.11 2004/09/20 15:11:26 gawrilow Exp $"

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

#ifndef _POLYMAKE_TYPE_MANIP_H
#  include <type_manip.h>
#endif

namespace pm {

inline int& inv_sign(int& x) { return x=-x; }
inline long& inv_sign(long& x) { return x=-x; }
inline float& inv_sign(float& x) { return x=-x; }
inline double& inv_sign(double& x) { return x=-x; }

namespace operations {

struct partial { };
struct partial_left : partial { };
struct partial_right : partial { };

template <class T> struct arith_tag {
   static const bool has_unsigned=false;
};
template <> struct arith_tag<char> {
   static const bool has_unsigned=true;
   typedef unsigned char unsigned_type;
};
template <> struct arith_tag<short> : arith_tag<char> {
   typedef unsigned short unsigned_type;
};
template <> struct arith_tag<int> : arith_tag<short> {
   typedef unsigned int unsigned_type;
};
template <> struct arith_tag<long> : arith_tag<int> {
   typedef unsigned long unsigned_type;
};
template <> struct arith_tag<float> : arith_tag<long> {
   typedef float unsigned_type;
};
template <> struct arith_tag<double> : arith_tag<float> {
   typedef double unsigned_type;
};
template <> struct arith_tag<unsigned char> : arith_tag<char> { };
template <> struct arith_tag<unsigned short> : arith_tag<unsigned char> {
   typedef unsigned short unsigned_type;
};
template <> struct arith_tag<unsigned int> : arith_tag<unsigned short> {
   typedef unsigned int unsigned_type;
};
template <> struct arith_tag<unsigned long> : arith_tag<unsigned int> {
   typedef unsigned long unsigned_type;
};

template <typename T>
struct arith_equiv {
   typedef arith_tag<T> type;
};

template <typename T>
struct has_unsigned {
   typedef typename arith_equiv<T>::type arith_type;
   static const bool answer=arith_type::has_unsigned;
};

template <typename Left, typename Right=char,
          bool _try_unsigned=has_unsigned<Left>::answer && has_unsigned<Right>::answer,
	  typename ResultTag=typename most_derived< cons<typename arith_equiv<Left>::type,
							 typename arith_equiv<Right>::type> >::type>
struct arith_result : extract_template_type_param<ResultTag,0>, True { };

template <typename Left, typename Right>
struct arith_result<Left,Right,false,void> : False {
   typedef void type;
};

template <typename Left, typename Right>
struct arith_result<Left,Right,true,void>
   : arith_result<typename arith_equiv<Left>::type::unsigned_type, typename arith_equiv<Right>::type::unsigned_type,false> { };

template <class T> struct bitwise_tag { };

template <> struct bitwise_tag<short> : bitwise_tag<char> { };
template <> struct bitwise_tag<int> : bitwise_tag<short> { };
template <> struct bitwise_tag<long> : bitwise_tag<int> { };

template <typename Left, typename Right=char,
	  typename ResultTag=typename most_derived< cons< bitwise_tag<Left>, bitwise_tag<Right> > >::type>
struct bitwise_result : extract_template_type_param<ResultTag,0> { };

template <typename Left, typename Right>
struct bitwise_result<Left,Right,void> {
   typedef void type;
};

template <typename Op, typename Result>
struct neg_scalar {
   typedef Op argument_type;
   typedef const Result result_type;
   result_type operator() (typename function_argument<argument_type>::type a) const { return -a; }
   Op& assign(Op& a) const { return inv_sign(a); }
};

template <typename Op>
struct neg_scalar<Op,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct add_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a+b;
   }
   template <typename Iterator2>
   const Left& operator() (partial_left, const Left& a, const Iterator2&) const { return a; }
   template <typename Iterator1>
   const Right& operator() (partial_right, const Iterator1&, const Right& b) const { return b; }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a+=b; }
};

template <typename Left, typename Right, typename Result>
struct sub_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a-b;
   }
   template <typename Iterator2>
   const Left& operator() (partial_left, const Left& a, const Iterator2&) const { return a; }
   template <typename Iterator1>
   result_type operator() (partial_right, const Iterator1&, typename function_argument<Right>::type b) const { return -b; }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a-=b; }
};

template <typename Left, typename Right, class Result>
struct mul_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a*b;
   }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a*=b; }
};

template <typename Left, typename Right>
struct mul_scalar<Left,Right,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct div_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a/b;
   }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a/=b; }
};

template <typename Left, typename Right>
struct div_scalar<Left,Right,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct mod_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a%b;
   }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a%=b; }
};

template <typename Left, typename Right>
struct mod_scalar<Left,Right,void> {
   typedef void result_type;
};

template <typename Op, typename Result>
struct not_scalar {
   typedef Op argument_type;
   typedef const Result result_type;
   result_type operator() (typename function_argument<Op>::type a) const { return !a; }
   Op& assign(Op& a) const { return a=!a; }
};

template <class Op>
struct not_scalar<Op,void> {
   typedef void result_type;
};

template <typename Op, typename Result>
struct inv_scalar {
   typedef Op argument_type;
   typedef const Result result_type;
   result_type operator() (typename function_argument<Op>::type a) const { return ~a; }
   Op& assign(Op& a) const { return a=~a; }
};

template <class Op>
struct inv_scalar<Op,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct or_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a|b;
   }
   template <typename Iterator2>
   const Left& operator() (partial_left, const Left& a, const Iterator2&) const { return a; }
   template <typename Iterator1>
   const Right& operator() (partial_right, const Iterator1&, const Right& b) const { return b; }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a|=b; }
};

template <typename Left, typename Right>
struct or_scalar<Left,Right,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct and_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a&b;
   }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a&=b; }
};

template <typename Left, typename Right>
struct and_scalar<Left,Right,void> {
   typedef void result_type;
};

template <typename Left, typename Right, typename Result>
struct xor_scalar {
   typedef Left first_argument_type;
   typedef Right second_argument_type;
   typedef const Result result_type;

   result_type operator() (typename function_argument<Left>::type a, typename function_argument<Right>::type b) const {
      return a^b;
   }
   Left& assign(Left& a, typename function_argument<Right>::type b) const { return a^=b; }
};

template <typename Left, typename Right>
struct xor_scalar<Left,Right,void> {
   typedef void result_type;
};

} // end namespace operations
} // end namespace pm

#endif // _POLYMAKE_OPERATIONS_BASIC_DEFS_H

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