/* Copyright (c) 1997-2011
   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: Polynomial.h 10191 2011-05-08 21:55:23Z gawrilow $
*/

#ifndef POLYMAKE_POLYNOMIAL_H
#define POLYMAKE_POLYNOMIAL_H

#include "polymake/Rational.h"
#include "polymake/Array.h"
#include "polymake/Ring.h"
#include "polymake/Vector.h"
#include "polymake/SparseVector.h"
#include "polymake/SparseMatrix.h"
#include "polymake/TransformedContainer.h"

#include "polymake/hash_map"
#include <cassert>

namespace pm {

template <typename Coefficient=Rational, typename Exponent=int>
class Term;

template <typename Coefficient=Rational, typename Exponent=int>
class UniTerm;

template <typename Coefficient=Rational, typename Exponent=int>
class Polynomial;

template <typename Coefficient=Rational, typename Exponent=int>
class UniPolynomial;

// forward declaration in Ring.h

template <typename Coefficient, typename Exponent>
class Monomial {
public:
   typedef Ring<Coefficient, Exponent> ring_type;
   typedef Coefficient coefficient_type;
   typedef Exponent exponent_type;
   typedef SparseVector<Exponent> value_type;

protected:
   value_type the_monom;
   ring_type ring;

public:
   // undefined object - to be read later from perl::Value
   Monomial() {}

   explicit Monomial(const ring_type& r) :
      the_monom(r.n_vars()),
      ring(r) {}

   template <typename _Vector>
   Monomial(const GenericVector<_Vector,Exponent>& m, const ring_type& r) :
      the_monom(m),
      ring(r)
   {
      if (POLYMAKE_DEBUG || !Unwary<_Vector>::value) {
         if (m.dim() != ring.n_vars())
            throw std::runtime_error("Monomial constructor - dimension mismatch");
      }
   }

   int dim() const { return the_monom.dim(); }
   const value_type& get_value() const { return the_monom; }
   const ring_type& get_ring() const { return ring; }

   void clear()
   {
      // can't say the_monom.clear() here as it would reset the dimension to 0.
      the_monom.fill(Exponent(0));
   }

   void swap(Monomial& m)
   {
      if (ring != m.ring) throw std::runtime_error("Monomials of different rings");
      the_monom.swap(m.the_monom);
   }

   static value_type default_value(const ring_type& r)
   {
      return value_type(r.n_vars());
   }

   static bool equals_to_default(const value_type& x, const ring_type&)
   {
      return x.empty();
   }

   static cmp_value compare_values(const value_type& x1, const value_type& x2);

   Exponent operator[] (const int i) const { return the_monom[i]; }

   Monomial operator* (const Monomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("Monomials of different rings");
      return Monomial(the_monom+m.the_monom, ring);
   }

   Monomial& operator*= (const Monomial& m)
   {
      if (ring != m.ring) throw std::runtime_error("Monomials of different rings");
      the_monom+=m.the_monom;
      return *this;
   }

   Monomial operator^ (typename function_argument<Exponent>::type exp) const
   {
      return Monomial(exp*the_monom, ring);
   }

   Monomial& operator^= (typename function_argument<Exponent>::type exp)
   {
      the_monom*=exp;
      return *this;
   }

   bool operator== (const Monomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("Monomials of different rings");
      return the_monom == m.the_monom;
   }
   bool operator!= (const Monomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("Monomials of different rings");
      return the_monom != m.the_monom;
   }

   bool operator< (const Monomial& m) const
   {
      return operations::cmp()(*this,m)==cmp_lt;
   }
   bool operator<= (const Monomial& m) const
   {
      return operations::cmp()(*this,m)!=cmp_gt;
   }
   bool operator> (const Monomial& m) const
   {
      return operations::cmp()(*this,m)==cmp_gt;
   }
   bool operator>= (const Monomial& m) const
   {
      return operations::cmp()(*this,m)!=cmp_lt;
   }

   template <typename Output> static
   void pretty_print(GenericOutput<Output>& out, const value_type& m, const ring_type& r)
   {
      if (m.empty()) {
         out.top() << '1';  // constant monomial
         return;
      }

      bool first = true;
      for (typename value_type::const_iterator it=m.begin(); !it.at_end(); ++it) {
         if (first)
            first = false;
         else
            out.top() << '*';
         out.top() << r.names()[it.index()];
         // FIXME: comparison with 1
         if (*it != 1) out.top() << '^' << *it;
      }
   }

   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& out, const Monomial& me)
   {
      pretty_print(out, me.the_monom, me.ring);
      return out.top();
   }

   template <typename> friend class spec_object_traits;
};


template <typename Coefficient, typename Exponent>
class UniMonomial {
public:
   typedef Ring<Coefficient, Exponent> ring_type;
   typedef Coefficient coefficient_type;
   typedef Exponent exponent_type;
   typedef Exponent value_type;

   static ring_type default_ring() { return ring_type(1); }

protected:
   value_type the_monom;
   ring_type ring;

public:
   UniMonomial() :
      the_monom(),
      ring(default_ring()) {}

   explicit UniMonomial(const Exponent& exp) :
      the_monom(exp),
      ring(default_ring()) {}

   explicit UniMonomial(const ring_type& r) :
      the_monom(),
      ring(r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniMonomial constructor - invalid ring");
   }

   UniMonomial(const Exponent& exp, const ring_type& r) :
      the_monom(exp),
      ring(r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniMonomial constructor - invalid ring");
   }

   int dim() const { return 1; }
   const value_type& get_value() const { return the_monom; }
   const ring_type& get_ring() const { return ring; }

   void clear()
   {
      the_monom=default_value();
   }

   void swap(UniMonomial& m)
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      the_monom.swap(m.the_monom);
   }

   static value_type default_value(const ring_type&)
   {
      operations::clear<Exponent> zero;
      return zero();
   }

   static bool equals_to_default(const value_type& x, const ring_type& r)
   {
      return x==default_value(r);
   }

   static cmp_value compare_values(const value_type& x1, const value_type& x2)
   {
      return operations::cmp()(x1,x2);
   }

   UniMonomial operator* (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return UniMonomial(the_monom+m.the_monom, ring);
   }

   UniMonomial& operator*= (const UniMonomial& m)
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      the_monom+=m.the_monom;
      return *this;
   }

   UniMonomial operator^ (typename function_argument<Exponent>::type exp) const
   {
      return UniMonomial(exp*the_monom, ring);
   }

   UniMonomial& operator^= (typename function_argument<Exponent>::type exp)
   {
      the_monom*=exp;
      return *this;
   }

   bool operator== (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom == m.the_monom;
   }
   bool operator!= (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom != m.the_monom;
   }
   bool operator< (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom < m.the_monom;
   }
   bool operator<= (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom <= m.the_monom;
   }
   bool operator> (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom > m.the_monom;
   }
   bool operator>= (const UniMonomial& m) const
   {
      if (ring != m.ring) throw std::runtime_error("UniMonomials of different rings");
      return the_monom >= m.the_monom;
   }

   template <typename Output> static
   void pretty_print(GenericOutput<Output>& out, const value_type& m, const ring_type& r)
   {
      if (equals_to_default(m,r)) {
         out.top() << '1';  // constant monomial
         return;
      }
      out.top() << r.names()[0];
      // FIXME: comparison with 1
      if (m!=1) out.top() << '^' << m;
   }

   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& out, const UniMonomial& me)
   {
      pretty_print(out, me.the_monom, me.ring);
      return out.top();
   }

   template <typename> friend class spec_object_traits;
};

template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< Monomial<Coefficient, Exponent> > >
  : spec_object_traits<is_composite> {

   typedef Monomial<Coefficient,Exponent> masquerade_for;

   typedef cons<typename Monomial<Coefficient, Exponent>::value_type,
                typename Monomial<Coefficient, Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << me.the_monom << me.ring;
   }
};

template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< UniMonomial<Coefficient, Exponent> > >
  : spec_object_traits<is_composite> {

   typedef UniMonomial<Coefficient,Exponent> masquerade_for;

   typedef cons<typename UniMonomial<Coefficient, Exponent>::value_type,
                typename UniMonomial<Coefficient, Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << me.the_monom << me.ring;
   }
};

template <typename Coefficient, typename Exponent>
class Ring<Coefficient, Exponent>::Variables :
   public modified_container_impl<Variables,
                                  list( Container< sequence >,
                                        Operation< Monomial_constructor > ) > {
public:
   Variables(const Ring& r) : ring(r) {}

   sequence get_container() const { return sequence(0, ring.n_vars()); }
   Monomial_constructor get_operation() const { return ring; }
private:
   Ring ring;
};

template <typename Coefficient, typename Exponent> inline
typename Ring<Coefficient, Exponent>::Variables
Ring<Coefficient, Exponent>::variables() const
{
   return *this;
}

template <typename Coefficient, typename Exponent> inline
Monomial<Coefficient, Exponent>
Ring<Coefficient, Exponent>::Monomial_constructor::operator() (int i) const
{
   if (POLYMAKE_DEBUG) {
      if (i<0 || i >= ring.n_vars())
         throw std::runtime_error("Ring::variables - index out of range");
   }
   return result_type(unit_vector<Exponent>(ring.n_vars(), i), ring);
}

template <typename Coefficient, typename Exponent> inline
UniMonomial<Coefficient, Exponent>
Ring<Coefficient, Exponent>::variable() const
{
   assert(n_vars()==1);
   return UniMonomial<Coefficient, Exponent>(1, *this);
}

template <typename _Monomial> struct Term_result {};
template <typename _Monomial> struct Polynomial_result {};

template <typename Coefficient, typename Exponent>
struct Term_result< Monomial<Coefficient, Exponent> > {
   typedef Term<Coefficient, Exponent> type;
};

template <typename Coefficient, typename Exponent>
struct Term_result< UniMonomial<Coefficient, Exponent> > {
   typedef UniTerm<Coefficient, Exponent> type;
};

template <typename Coefficient, typename Exponent>
struct Polynomial_result< Monomial<Coefficient, Exponent> > {
   typedef Polynomial<Coefficient, Exponent> type;
};

template <typename Coefficient, typename Exponent>
struct Polynomial_result< UniMonomial<Coefficient, Exponent> > {
   typedef UniPolynomial<Coefficient, Exponent> type;
};

template <typename _Monomial>
class Term_base {
public:
   typedef _Monomial monomial_type;
   typedef typename monomial_type::ring_type ring_type;
   typedef typename monomial_type::coefficient_type coefficient_type;
   typedef typename monomial_type::exponent_type exponent_type;
   typedef std::pair<typename monomial_type::value_type, coefficient_type> value_type;
   typedef typename Term_result<_Monomial>::type term_result;

   Term_base() {}

   explicit Term_base(const ring_type& r) :
      ring(r) {}

   Term_base(const coefficient_type& c, const ring_type& r) :
      the_term(monomial_type::default_value(r), c),
      ring(r) {}

   Term_base(const typename monomial_type::value_type& m, const coefficient_type& c, const ring_type& r) :
      the_term(m,c),
      ring(r) {}

   void swap(Term_base& t)
   {
      if (ring != t.ring) throw std::runtime_error("Terms of different rings");
      the_term.swap(t.the_term);
   }

   const value_type& get_value() const { return the_term; }
   const ring_type& get_ring() const { return ring; }

   monomial_type get_monomial() const { return monomial_type(the_term.first, ring); }
   const coefficient_type& get_coefficient() const { return the_term.second; }

   term_result& top() { return static_cast<term_result&>(*this); }
   const term_result& top() const { return static_cast<const term_result&>(*this); }

   friend
   term_result operator* (const Term_base& t, const coefficient_type& c)
   {
      return term_result(t.the_term.first, t.the_term.second * c, t.ring);
   }

   friend
   term_result operator* (const coefficient_type& c, const Term_base& t)
   {
      return term_result(t.the_term.first, c * t.the_term.second, t.ring);
   }

   term_result& operator*= (const coefficient_type& c)
   {
      the_term.second *= c;
      return top();
   }

   friend
   term_result operator* (const Term_base& t, const monomial_type& m)
   {
      if (t.ring != m.get_ring()) throw std::runtime_error("Terms of different rings");
      return term_result(t.the_term.first + m.get_value(), t.the_term.second, t.ring);
   }

   friend
   term_result operator* (const monomial_type& m, const Term_base& t)
   {
      return t*m;
   }

   term_result& operator*= (const monomial_type& m)
   {
      if (ring != m.get_ring()) throw std::runtime_error("Terms of different rings");
      the_term.first += m.get_value();
      return top();
   }

   friend
   term_result operator* (const Term_base& t1, const Term_base& t2)
   {
      if (t1.ring != t2.ring) throw std::runtime_error("Terms of different rings");
      return term_result(t1.the_term.first + t2.the_term.first, t1.the_term.second * t2.the_term.second, t1.ring);
   }

   term_result& operator*= (const Term_base& t)
   {
      if (ring != t.ring) throw std::runtime_error("Terms of different rings");
      the_term.first += t.the_term.first;
      the_term.second *= t.the_term.second;
      return top();
   }

   term_result operator- () const
   {
      return term_result(the_term.first, -the_term.second, ring);
   }

   term_result& negate()
   {
      operations::neg<coefficient_type> neg;
      neg.assign(the_term.second);
      return top();
   }

   friend
   bool operator== (const Term_base& x1, const Term_base& x2)
   {
      if (x1.ring != x2.ring) throw std::runtime_error("Terms of different rings");
      return x1.the_term==x2.the_term;
   }

   friend
   bool operator== (const Term_base& x1, const monomial_type& x2)
   {
      if (x1.ring != x2.get_ring()) throw std::runtime_error("Terms of different rings");
      return x1.the_term.first==x2.get_value() && x1.the_term.second==1;
   }

   friend
   bool operator== (const monomial_type& x1, const Term_base& x2)
   {
      return x2==x1;
   }

   friend
   bool operator== (const Term_base& x1, const coefficient_type& x2)
   {
      return x1.the_term.first==monomial_type::default_value() && x1.the_term.second==x2;
   }

   friend
   bool operator== (const coefficient_type& x1, const Term_base& x2)
   {
      return x2==x1;
   }

   friend
   bool operator!= (const Term_base& x1, const Term_base& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const Term_base& x1, const monomial_type& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const monomial_type& x1, const Term_base& x2)
   {
      return !(x2==x1);
   }
   friend
   bool operator!= (const Term_base& x1, const coefficient_type& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const coefficient_type& x1, const Term_base& x2)
   {
      return !(x2==x1);
   }

   cmp_value compare(const Term_base& x) const
   {
      const cmp_value cmp_monom=monomial_type::compare_values(the_term.first, x.the_term.first);
      return cmp_monom != cmp_eq
             ? cmp_monom
             : operations::cmp()(the_term.second, x.the_term.second);
   }

   friend
   bool operator< (const Term_base& x1, const Term_base& x2)
   {
      return x1.compare(x2) == cmp_lt;
   }
   friend
   bool operator> (const Term_base& x1, const Term_base& x2)
   {
      return x1.compare(x2) == cmp_gt;
   }
   friend
   bool operator<= (const Term_base& x1, const Term_base& x2)
   {
      return x1.compare(x2) != cmp_gt;
   }
   friend
   bool operator>= (const Term_base& x1, const Term_base& x2)
   {
      return x1.compare(x2) != cmp_lt;
   }

   template <typename Output> static
   void pretty_print(GenericOutput<Output>& out,
                     const typename monomial_type::value_type& m,
                     const coefficient_type& c,
                     const ring_type& r)
   {
      // FIXME: comparison with 1
      if (c != coefficient_type(1)) {
         if (!identical<typename object_traits<coefficient_type>::model, is_scalar>::value)
            out.top() << '(';
         out.top() << c;
         if (!identical<typename object_traits<coefficient_type>::model, is_scalar>::value)
            out.top() << ')';
         if (monomial_type::equals_to_default(m,r)) return;
         out.top() << '*';
      }
      monomial_type::pretty_print(out, m, r);
   }

   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& out, const Term_base& me)
   {
      pretty_print(out, me.the_term.first, me.the_term.second, me.ring);
      return out.top();
   }

   static bool needs_plus(const coefficient_type& c)
   {
      return needs_plus(c, bool2type<identical<typename object_traits<coefficient_type>::model, is_scalar>::value>());
   }

protected:
   static bool needs_plus(const coefficient_type& c, True) { return c>0; }
   static bool needs_plus(const coefficient_type&, False) { return true; }

   value_type the_term;
   ring_type ring;
};

template <typename Coefficient, typename Exponent>
class Term : public Term_base< Monomial<Coefficient, Exponent> > {
public:
   typedef Monomial<Coefficient, Exponent> monomial_type;
private:
   typedef Term_base<monomial_type> super;
public:
   Term() {}

   explicit Term(const typename super::ring_type& r) :
      super(r) {}

   Term(const Coefficient& c, const typename super::ring_type& r) :
      super(c,r) {}

   Term(const monomial_type& m, const Coefficient& c = Coefficient(1)) :
      super(m.get_value(), c, m.get_ring()) {}

   Term(const std::pair<const typename monomial_type::value_type, Coefficient>& p, const typename super::ring_type& r) :
      super(p.first, p.second, r) {}

   template <typename _Vector>
   Term(const GenericVector<_Vector,Exponent>& m, const Coefficient& c, const typename super::ring_type& r) :
      super(m,c,r) {}

   template <typename> friend class spec_object_traits;
};

template <typename Coefficient, typename Exponent>
class UniTerm : public Term_base< UniMonomial<Coefficient, Exponent> > {
public:
   typedef UniMonomial<Coefficient, Exponent> monomial_type;
private:
   typedef Term_base<monomial_type> super;
public:
   UniTerm() : super(monomial_type::default_ring()) {}

   explicit UniTerm(const typename super::ring_type& r) :
      super(r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniTerm constructor - invalid ring");
   }

   explicit UniTerm(const Coefficient& c) :
      super(c, monomial_type::default_ring()) {}

   UniTerm(const Coefficient& c, const typename super::ring_type& r) :
      super(c,r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniTerm constructor - invalid ring");
   }

   UniTerm(const monomial_type& m, const Coefficient& c = Coefficient(1)) :
      super(m.get_value(), c, m.get_ring()) {}

   UniTerm(const std::pair<const typename monomial_type::value_type, Coefficient>& p, const typename super::ring_type& r) :
      super(p.first, p.second, r) {}

   template <typename> friend class spec_object_traits;
};


template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< Term<Coefficient,Exponent> > > :
   spec_object_traits<is_composite> {

   typedef Term<Coefficient,Exponent> masquerade_for;

   typedef cons<typename Term<Coefficient,Exponent>::value_type,
                typename Term<Coefficient,Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << me.the_term << me.ring;
   }
};

template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< UniTerm<Coefficient,Exponent> > > :
   spec_object_traits<is_composite> {

   typedef UniTerm<Coefficient,Exponent> masquerade_for;

   typedef cons<typename UniTerm<Coefficient,Exponent>::value_type,
                typename UniTerm<Coefficient,Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << me.the_term << me.ring;
   }
};


template <typename monomial_type> inline
typename Term_result<monomial_type>::type
operator* (const typename monomial_type::coefficient_type& c, const monomial_type& m)
{
   return typename Term_result<monomial_type>::type(m,c);
}

template <typename monomial_type> inline
typename Term_result<monomial_type>::type
operator* (const monomial_type& m, const typename monomial_type::coefficient_type& c)
{
   return typename Term_result<monomial_type>::type(m,c);
}

template <typename monomial_type> inline
typename Term_result<monomial_type>::type
operator- (const monomial_type& m)
{
   return typename Term_result<monomial_type>::type(m,-1);
}

template <typename _Monomial>
class Polynomial_base {
public:
   typedef _Monomial monomial_type;
   typedef typename monomial_type::ring_type ring_type;
   typedef typename monomial_type::coefficient_type coefficient_type;
   typedef typename Term_result<monomial_type>::type term_type;
   typedef typename Polynomial_result<monomial_type>::type polynomial_result;
   typedef hash_map<typename monomial_type::value_type, coefficient_type> term_hash;

protected:
   shared_object<term_hash> the_terms;
   ring_type ring;
   mutable typename monomial_type::value_type the_lm;
   mutable bool the_lm_set;

public:
   Polynomial_base() :
      the_lm_set(false) {}

   // the default polynomial in a given ring
   explicit Polynomial_base(const ring_type& r) :
      ring(r),
      the_lm_set(false) {}

   Polynomial_base(const coefficient_type& c, const ring_type& r) :
      ring(r)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect((the_lm_set = non_zero(c)), 1)) {
         the_lm=monomial_type::default_value(r);
         the_terms->insert(the_lm,c);
      }
   }

   Polynomial_base(const monomial_type& m) :
      ring(m.get_ring()),
      the_lm(m.get_value()),
      the_lm_set(true)
   {
      the_terms->insert(the_lm, coefficient_type(1));
   }

   Polynomial_base(const Term_base<monomial_type>& t) :
      ring(t.get_ring()),
      the_lm(t.get_value().first)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect((the_lm_set = non_zero(t.get_coefficient())), 1))
         the_terms->insert(the_lm, t.get_coefficient());
   }

   void swap(Polynomial_base& p)
   {
      if (ring != p.ring) throw std::runtime_error("Polynomials of different rings");
      the_terms.swap(p.the_terms);
      std::swap(the_lm, p.the_lm);
      std::swap(the_lm_set, p.the_lm_set);
   }

   void clear()
   {  // the default polynomial (in the same ring)
      the_terms->clear();
      forget_lm();
   }
  
   polynomial_result& top() { return static_cast<polynomial_result&>(*this); }
   const polynomial_result& top() const { return static_cast<const polynomial_result&>(*this); }

   int n_vars() const { return ring.n_vars(); }

   const ring_type& get_ring() const { return ring; }

   int n_terms() const { return the_terms->size(); }

   const term_hash& get_terms() const { return *the_terms; }

   bool trivial() const { return the_terms->empty(); }

   // returns the coefficient of the monomial m or 0 iff m does not exists
   const coefficient_type& get_coefficient(const monomial_type& m) const
   {
      if (m.get_ring() != ring) throw std::runtime_error("Polynomial - Monomial of different ring");
      return get_coefficient(m.get_value());
   }

   const coefficient_type& get_coefficient(const typename monomial_type::value_type& m) const
   {
      typename term_hash::const_iterator find = the_terms->find(m);
      if (find != the_terms->end())
         return find->second;
      operations::clear<coefficient_type> zero;
      return zero();
   }

   bool exists(const monomial_type& m) const
   {
      if (m.get_ring() != ring) throw std::runtime_error("Polynomial - Monomial of different ring");
      return exists(m.get_value());
   }

   bool exists(const typename monomial_type::value_type& m) const
   {
      return the_terms->exists(m);
   }

   Vector<coefficient_type> coefficients_as_vector() const
   {
      return Vector<coefficient_type>(n_terms(),
                                      attach_operation(*the_terms, BuildUnary<operations::take_second>()).begin());
   }

   // leading term
   term_type lt() const
   {
      if (the_lm_set)
         return term_type(*the_terms->find(the_lm), ring);
      else if (trivial())
         return term_type(ring);
      else
         return term_type(*compute_lm(), ring);
   }


   bool lt_set() const { return the_lm_set; }

   // leading monomial
   monomial_type lm() const
   {
      if (the_lm_set)
         return monomial_type(the_lm, ring);
      else if (trivial())
         return monomial_type(ring);
      else
         return monomial_type(compute_lm()->first, ring);
   }

   // leading monomial's exponents
   typename monomial_type::value_type lm_exp() const
   {
      if (the_lm_set)
         return the_lm;
      else if (trivial())
         return monomial_type::default_value(ring);
      else
         return compute_lm()->first;
   }

   // leading coefficient
   const coefficient_type& lc() const
   {
      if (the_lm_set)
         return the_terms->find(the_lm)->second;
      else if (trivial()) {
         operations::clear<coefficient_type> zero;
         return zero();
      } else
         return compute_lm()->second;
   }

   friend
   polynomial_result operator* (const Polynomial_base& p, const coefficient_type& c)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(!non_zero(c), 0)) return polynomial_result(p.ring);

      Polynomial_base prod(p);
      for (typename Entire<term_hash>::iterator it=entire(*prod.the_terms); !it.at_end(); ++it)
         it->second *= c;
      return prod.top();
   }

   friend
   polynomial_result operator* (const coefficient_type& c, const Polynomial_base& p)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(!non_zero(c), 0)) return polynomial_result(p.ring);

      Polynomial_base prod(p);
      for (typename Entire<term_hash>::iterator it=entire(*prod.the_terms); !it.at_end(); ++it)
         it->second = c*it->second;
      return prod.top();
   }

   polynomial_result& operator*= (const coefficient_type& c)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(!non_zero(c), 0)) {
         clear();
      } else {
         for (typename Entire<term_hash>::iterator it=entire(*the_terms); !it.at_end(); ++it)
            it->second *= c;
      }
      return top();
   }

   friend
   polynomial_result operator* (const Polynomial_base& p, const monomial_type& m)
   {
      if (p.ring != m.get_ring()) throw std::runtime_error("Polynomials of different rings");

      polynomial_result prod(p.ring);
      for (typename Entire<term_hash>::const_iterator it=entire(*p.the_terms); !it.at_end(); ++it)
         prod.add_term(it->first + m.get_value(), it->second, True());

      if (p.the_lm_set) {
         prod.the_lm=p.the_lm+m.get_value();
         prod.the_lm_set=true;
      }
      return prod;
   }

   friend
   polynomial_result operator* (const monomial_type& m, const Polynomial_base& p)
   {
      return p*m;
   }

   polynomial_result& operator*= (const monomial_type& m)
   {
      *this = (*this) * m;
      return top();
   }

   friend
   polynomial_result operator* (const Polynomial_base& p, const term_type& t)
   {
      if (p.ring != t.get_ring()) throw std::runtime_error("Polynomials of different rings");

      polynomial_result prod(p.ring);
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(non_zero(t.get_coefficient()), 1)) {
         for (typename Entire<term_hash>::const_iterator it=entire(*p.the_terms); !it.at_end(); ++it)
            prod.add_term(it->first + t.get_value().first, it->second * t.get_value().second, True());

         if (p.the_lm_set) {
            prod.the_lm=p.the_lm+t.get_value().first;
            prod.the_lm_set=true;
         }
      }
      return prod;
   }

   friend
   polynomial_result operator* (const term_type& t, const Polynomial_base& p)
   {
      if (p.ring != t.get_ring()) throw std::runtime_error("Polynomials of different rings");

      polynomial_result prod(p.ring);
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(non_zero(t.get_coefficient()), 1)) {
         for (typename Entire<term_hash>::const_iterator it=entire(*p.the_terms); !it.at_end(); ++it)
            prod.add_term(it->first + t.get_value().first, t.get_value().second * it->second, True());

         if (p.the_lm_set) {
            prod.the_lm=p.the_lm+t.get_value().first;
            prod.the_lm_set=true;
         }
      }
      return prod;
   }

   polynomial_result& operator*= (const term_type& t)
   {
      *this = (*this) * t;
      return top();
   }

   friend
   polynomial_result operator* (const Polynomial_base& p1, const Polynomial_base& p2)
   {
      if (p1.ring != p2.ring) throw std::runtime_error("Polynomials of different rings");

      polynomial_result prod(p1.ring);
      for (typename Entire<term_hash>::const_iterator it1=entire(*p1.the_terms); !it1.at_end(); ++it1)
         for (typename Entire<term_hash>::const_iterator it2=entire(*p2.the_terms); !it2.at_end(); ++it2)
            prod.add_term(it1->first + it2->first, it1->second * it2->second, True());
      return prod;
   }

   polynomial_result& operator*= (const polynomial_result& p)
   {
      *this = (*this) * p;
      return top();
   }

   polynomial_result operator- () const
   {
      polynomial_result result(top());
      return result.negate();
   }

   polynomial_result& negate()
   {
      operations::neg<coefficient_type> neg;
      for (typename Entire<term_hash>::iterator it=entire(*the_terms); !it.at_end(); ++it)
         neg.assign(it->second);
      return top();
   }

   friend
   polynomial_result operator+ (const Polynomial_base& p, const coefficient_type& c)
   {
      Polynomial_base sum=p;
      return sum+=c;
   }

   friend
   polynomial_result operator+ (const coefficient_type& c, const Polynomial_base& p)
   {
      return p+c;
   }

   polynomial_result& operator+= (const coefficient_type& c)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(non_zero(c), 1))
         add_term(monomial_type::default_value(ring), c, True());
      return top();
   }

   friend
   polynomial_result operator- (const Polynomial_base& p, const coefficient_type& c)
   {
      Polynomial_base diff=p;
      return diff-=c;
   }

   friend
   polynomial_result operator- (const coefficient_type& c, const Polynomial_base& p)
   {
      Polynomial_base diff=-p;
      return diff+=c;
   }

   polynomial_result& operator-= (const coefficient_type& c)
   {
      conv<coefficient_type, bool> non_zero;
      if (__builtin_expect(non_zero(c), 1))
         add_term(monomial_type::default_value(ring), -c, True());
      return top();
   }

   friend
   polynomial_result operator+ (const Polynomial_base& p, const monomial_type& m)
   {
      Polynomial_base sum=p;
      return sum+=m;
   }

   friend
   polynomial_result operator+ (const monomial_type& m, const Polynomial_base& p)
   {
      return p+m;
   }

   polynomial_result& operator+= (const monomial_type& m)
   {
      if (ring != m.get_ring()) throw std::runtime_error("Polynomials of different rings");
      add_term(m.get_value(), 1, True());
      return top();
   }

   friend
   polynomial_result operator- (const Polynomial_base& p, const monomial_type& m)
   {
      Polynomial_base diff=p;
      return diff-=m;
   }

   friend
   polynomial_result operator- (const monomial_type& m, const Polynomial_base& p)
   {
      Polynomial_base diff=-p;
      return diff+=m;
   }

   polynomial_result& operator-= (const monomial_type& m)
   {
      if (ring != m.get_ring()) throw std::runtime_error("Polynomials of different rings");
      add_term(m.get_value(), -1, True());
      return top();
   }

   friend
   polynomial_result operator+ (const Polynomial_base& p, const term_type& t)
   {
      Polynomial_base sum=p;
      return sum+=t;
   }

   friend
   polynomial_result operator+ (const term_type& t, const Polynomial_base& p)
   {
      return p+t;
   }

   polynomial_result& operator+= (const term_type& t)
   {
      if (ring != t.get_ring()) throw std::runtime_error("Polynomials of different rings");
      add_term(t.get_value().first, t.get_value().second, False());
      return top();
   }

   friend
   polynomial_result operator- (const Polynomial_base& p, const term_type& t)
   {
      Polynomial_base diff=p;
      return diff-=t;
   }

   friend
   polynomial_result operator- (const term_type& t, const Polynomial_base& p)
   {
      Polynomial_base diff=-p;
      return diff+=t;
   }

   polynomial_result& operator-= (const term_type& t)
   {
      if (ring != t.get_ring()) throw std::runtime_error("Polynomials of different rings");
      add_term(t.get_value().first, -t.get_value().second, False());
      return top();
   }

   friend
   polynomial_result operator+ (const Polynomial_base& p1, const Polynomial_base& p2)
   {
      Polynomial_base sum=p1;
      return sum+=p2;
   }

   polynomial_result& operator+= (const Polynomial_base& p)
   {
      if (ring != p.ring) throw std::runtime_error("Polynomials of different rings");
      for (typename Entire<term_hash>::const_iterator t=entire(*p.the_terms); !t.at_end(); ++t)
         add_term(t->first, t->second, True());
      return top();
   }

   friend
   polynomial_result operator- (const Polynomial_base& p1, const Polynomial_base& p2)
   {
      Polynomial_base diff=p1;
      return diff-=p2;
   }

   polynomial_result& operator-= (const Polynomial_base& p)
   {
      if (ring != p.ring) throw std::runtime_error("Polynomials of different rings");
      for (typename Entire<term_hash>::const_iterator t=entire(*p.the_terms); !t.at_end(); ++t)
         add_term(t->first, -t->second, True());
      return top();
   }

   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& out, const Polynomial_base& me)
   {
      bool first = true;
      for (typename Entire<term_hash>::const_iterator it=entire(*me.the_terms); !it.at_end(); ++it) {
         if (first)
            first = false;
         else if (term_type::needs_plus(it->second))
            out.top() << " + ";
         else
            out.top() << ' ';

         term_type::pretty_print(out, it->first, it->second, me.ring);
      }
      return out.top();
   }

protected:
   void forget_lm()
   {
      operations::clear<typename monomial_type::value_type> cl;
      cl.assign(the_lm);
      the_lm_set=false;
   }

   typename Entire<term_hash>::const_iterator compute_lm() const
   {
      typename term_hash::const_iterator it=the_terms->begin(), lt_it=it, end=the_terms->end();
      if (it != end) {
         the_lm=it->first;
         the_lm_set=true;
         while (++it != end)
            if (monomial_type::compare_values(it->first, the_lm) == cmp_gt) {
               the_lm=it->first;
               lt_it=it;
            }
      }
      return lt_it;
   }

   template <bool trusted>
   void add_term(const typename monomial_type::value_type& m, const coefficient_type& c, bool2type<trusted>)
   {
      conv<coefficient_type, bool> non_zero;
      if (trusted || __builtin_expect(non_zero(c), 1)) {
         if (the_lm_set) forget_lm();

         operations::find_and_apply<polymake::operations::add> add;
         typename term_hash::iterator it = the_terms->insert(m, c, add);

         if (add.found() && !non_zero(it->second))
            the_terms->erase(it);
      }
   }

public:
   friend 
   bool operator== (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      if (x1.ring != x2.ring) throw std::runtime_error("Polynomials of different rings");
      return *x1.the_terms==*x2.the_terms;
   }

   friend
   bool operator== (const Polynomial_base& x1, const term_type& x2)
   {
      if (x1.ring != x2.get_ring()) throw std::runtime_error("Polynomials of different rings");
      return x1.the_terms->size()==1 &&
             x1.the_terms->begin()->first == x2.get_value().first &&
             x1.the_terms->begin()->second == x2.get_value().second;
   }

   friend
   bool operator== (const term_type& x1, const Polynomial_base& x2)
   {
      return x2==x1;
   }

   friend
   bool operator== (const Polynomial_base& x1, const monomial_type& x2)
   {
      if (x1.ring != x2.get_ring()) throw std::runtime_error("Polynomials of different rings");
      return x1.the_terms->size()==1 &&
             x1.the_terms->begin()->first == x2.get_value() &&
             x1.the_terms->begin()->second == 1;
   }

   friend
   bool operator== (const monomial_type& x1, const Polynomial_base& x2)
   {
      return x2==x1;
   }

   friend
   bool operator== (const Polynomial_base& x1, const coefficient_type& x2)
   {
      return x1.the_terms->size()==1 &&
             x1.the_terms->begin()->first==monomial_type::default_value(x1.ring) &&
             x1.the_terms->begin()->second==x2;
   }

   friend
   bool operator== (const coefficient_type& x1, const Polynomial_base& x2)
   {
      return x2==x1;
   }

   friend
   bool operator!= (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const Polynomial_base& x1, const term_type& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const term_type& x1, const Polynomial_base& x2)
   {
      return !(x2==x1);
   }
   friend
   bool operator!= (const Polynomial_base& x1, const monomial_type& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const monomial_type& x1, const Polynomial_base& x2)
   {
      return !(x2==x1);
   }
   friend
   bool operator!= (const Polynomial_base& x1, const coefficient_type& x2)
   {
      return !(x1==x2);
   }
   friend
   bool operator!= (const coefficient_type& x1, const Polynomial_base& x2)
   {
      return !(x2==x1);
   }

   cmp_value compare(const Polynomial_base& x)
   {
      if (ring != x.ring) throw std::runtime_error("Polynomials of different rings");

      if (trivial()) return x.trivial() ? cmp_eq : cmp_lt;
      if (x.trivial()) return cmp_gt;

      cmp_value cmp_leading=lt().compare(x.lt());
      if (cmp_leading == cmp_eq) {
         Polynomial_base p1(*this), p2(x);
         do {
            p1.the_terms->erase(p1.the_lm);  p1.the_lm_set=false;
            p2.the_terms->erase(p2.the_lm);  p2.the_lm_set=false;
            if (p1.trivial()) return p2.trivial() ? cmp_eq : cmp_lt;
            if (p2.trivial()) return cmp_gt;
            cmp_leading=p1.lt().compare(p2.lt());
         }
         while (cmp_leading == cmp_eq);
      }
      return cmp_leading;
   }

   friend
   bool operator< (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      return x1.compare(x2) == cmp_lt;
   }
   friend
   bool operator> (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      return x1.compare(x2) == cmp_gt;
   }
   friend
   bool operator<= (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      return x1.compare(x2) != cmp_gt;
   }
   friend
   bool operator>= (const Polynomial_base& x1, const Polynomial_base& x2)
   {
      return x1.compare(x2) != cmp_lt;
   }
};


template <typename Coefficient, typename Exponent>
class Polynomial : public Polynomial_base< Monomial<Coefficient, Exponent> > {
public:
   typedef Monomial<Coefficient, Exponent> monomial_type;
private:
   typedef Polynomial_base<monomial_type> super;
public:
   // undefined object - to be read from perl::Value
   Polynomial() {}

   // the default polynomial in a given ring
   explicit Polynomial(const typename super::ring_type& r) :
      super(r) {}

   // a constant polynomial
   Polynomial(const Coefficient& c, const typename super::ring_type& r) :
      super(c,r) {}

   Polynomial(const monomial_type& m) :
      super(m) {}

   Polynomial(const typename super::term_type& t) :
      super(t) {}

   template <typename _Matrix, typename Container>
   Polynomial(const GenericMatrix<_Matrix,Exponent>& monoms,
              const Container& coeffs,
              const typename super::ring_type& r,
              typename enable_if<void*, isomorphic_to_container_of<Container,Coefficient>::value>::type=0) :
      super(r)
   {
      if (POLYMAKE_DEBUG || !Unwary<_Matrix>::value) {
         if (monoms.rows() != coeffs.size() || monoms.cols() != this->n_vars())
            throw std::runtime_error("Polynomial constructor - dimension mismatch");
      }
      typename Container::const_iterator c=coeffs.begin();
      for (typename Entire<Rows<_Matrix> >::const_iterator m=entire(rows(monoms)); !m.at_end(); ++m, ++c)
         this->add_term(*m, *c, False());
   }

   template <typename _Matrix>
   _Matrix monomials_as_matrix() const
   {
      return _Matrix(this->n_terms(), this->n_vars(),
                     entire(attach_operation(*this->the_terms, BuildUnary<operations::take_first>())));
   }

   SparseMatrix<Exponent> monomials_as_matrix() const
   {
      return monomials_as_matrix< SparseMatrix<Exponent> >();
   }

   template <typename> friend class spec_object_traits;
};


template <typename Coefficient, typename Exponent>
class UniPolynomial : public Polynomial_base< UniMonomial<Coefficient, Exponent> > {
public:
   typedef UniMonomial<Coefficient, Exponent> monomial_type;
public:
   typedef Polynomial_base<monomial_type> super;
public:
   UniPolynomial() : super(monomial_type::default_ring()) {}

   explicit UniPolynomial(const typename super::ring_type& r) :
      super(r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniPolynomial constructor - invalid ring");
   }

   explicit UniPolynomial(const Coefficient& c) :
      super(c, monomial_type::default_ring()) {}

   UniPolynomial(const Coefficient& c, const typename super::ring_type& r) :
      super(c,r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniPolynomial constructor - invalid ring");
   }

   UniPolynomial(const monomial_type& m) :
      super(m) {}

   UniPolynomial(const typename super::term_type& t) :
      super(t) {}

   template <typename Container1, typename Container2>
   UniPolynomial(const Container1& monoms,
                 const Container2& coeffs,
                 const typename super::ring_type& r,
                 typename enable_if<void*, (isomorphic_to_container_of<Container1,Exponent>::value &&
                                            isomorphic_to_container_of<Container2,Coefficient>::value)>::type=0) :
      super(r)
   {
      if (r.n_vars() != 1) throw std::runtime_error("UniPolynomial constructor - invalid ring");
      if (POLYMAKE_DEBUG) {
         if (monoms.size() != coeffs.size())
            throw std::runtime_error("UniPolynomial constructor - dimension mismatch");
      }

      typename Container2::const_iterator c=coeffs.begin();
      for (typename Entire<Container1>::const_iterator m=entire(monoms); !m.at_end(); ++m, ++c)
         this->add_term(*m, *c, False());
   }

   Vector<Exponent> monomials_as_vector()  const
   {
      return Vector<Exponent>(this->n_terms(),
                              attach_operation(*this->the_terms, BuildUnary<operations::take_first>()).begin());
   }
  
   template <typename> friend class spec_object_traits;
};


template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< Polynomial<Coefficient,Exponent> > > :
   spec_object_traits<is_composite> {

   typedef Polynomial<Coefficient,Exponent> masquerade_for;

   typedef cons<typename Polynomial<Coefficient,Exponent>::term_hash,
                typename Polynomial<Coefficient,Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << *me.the_terms << me.ring;
   }
};

template <typename Coefficient, typename Exponent>
struct spec_object_traits< Serialized< UniPolynomial<Coefficient,Exponent> > > :
   spec_object_traits<is_composite> {

   typedef UniPolynomial<Coefficient,Exponent> masquerade_for;

   typedef cons<typename UniPolynomial<Coefficient,Exponent>::term_hash,
                typename UniPolynomial<Coefficient,Exponent>::ring_type> elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& me, Visitor& v)
   {
      v << *me.the_terms << me.ring;
   }
};

template <typename Coefficient, typename Exponent>
class conv<Polynomial<Coefficient, Exponent>, bool> {
public:
   typedef Polynomial<Coefficient, Exponent> argument_type;
   typedef bool result_type;

   bool operator() (const argument_type& p) const
   {
      return !p.trivial();
   }
};

template <typename Coefficient, typename Exponent>
class conv<UniPolynomial<Coefficient, Exponent>, bool> {
public:
   typedef UniPolynomial<Coefficient, Exponent> argument_type;
   typedef bool result_type;

   bool operator() (const argument_type& p) const
   {
      return !p.trivial();
   }
};

// some magic needed for operator construction
struct is_polynomial {};

template <typename Coefficient, typename Exponent>
struct choose_generic_object_traits< Polynomial<Coefficient, Exponent>, false, false > :
      spec_object_traits< Polynomial<Coefficient, Exponent> > {
   typedef void generic_type;
   typedef is_polynomial generic_tag;
   typedef Polynomial<Coefficient, Exponent> persistent_type;
};

template <typename Coefficient, typename Exponent>
struct choose_generic_object_traits< UniPolynomial<Coefficient, Exponent>, false, false > :
      spec_object_traits< UniPolynomial<Coefficient, Exponent> > {
   typedef void generic_type;
   typedef is_polynomial generic_tag;
   typedef UniPolynomial<Coefficient, Exponent> persistent_type;
};

template <typename Coefficient, typename Exponent, typename T>
struct compatible_with_polynomial {
   static const bool value= isomorphic_types<Coefficient, T>::value ||
                            derived_from<T, Monomial<Coefficient, Exponent> >::value ||
                            derived_from<T, Term<Coefficient, Exponent> >::value;
};

template <typename Coefficient, typename Exponent, typename T>
struct compatible_with_unipolynomial {
   static const bool value= isomorphic_types<Coefficient, T>::value ||
                            derived_from<T, UniMonomial<Coefficient, Exponent> >::value ||
                            derived_from<T, UniTerm<Coefficient, Exponent> >::value;
};

template <typename Coefficient, typename Exponent, typename T, typename TModel>
struct isomorphic_types<Polynomial<Coefficient,Exponent>, T,
                        typename enable_if<is_polynomial, compatible_with_polynomial<Coefficient,Exponent,T>::value>::type,
                        TModel> : False {
   typedef cons<is_polynomial, is_scalar> discriminant;
};

template <typename Coefficient, typename Exponent, typename T, typename TModel>
struct isomorphic_types<T, Polynomial<Coefficient,Exponent>, TModel,
                        typename enable_if<is_polynomial, compatible_with_polynomial<Coefficient,Exponent,T>::value>::type> : False {
   typedef cons<is_scalar, is_polynomial> discriminant;
};

template <typename Coefficient, typename Exponent>
struct isomorphic_types<Polynomial<Coefficient,Exponent>, Polynomial<Coefficient,Exponent>, is_polynomial, is_polynomial> : True {
   typedef cons<is_polynomial, is_polynomial> discriminant;
};

template <typename Coefficient, typename Exponent, typename T, typename TModel>
struct isomorphic_types<UniPolynomial<Coefficient,Exponent>, T,
                        typename enable_if<is_polynomial, compatible_with_unipolynomial<Coefficient,Exponent,T>::value>::type,
                        TModel> : False {
   typedef cons<is_polynomial, is_scalar> discriminant;
};

template <typename Coefficient, typename Exponent, typename T, typename TModel>
struct isomorphic_types<T, UniPolynomial<Coefficient,Exponent>, TModel,
                        typename enable_if<is_polynomial, compatible_with_unipolynomial<Coefficient,Exponent,T>::value>::type> : False {
   typedef cons<is_scalar, is_polynomial> discriminant;
};

template <typename Coefficient, typename Exponent>
struct isomorphic_types<UniPolynomial<Coefficient,Exponent>, UniPolynomial<Coefficient,Exponent>, is_polynomial, is_polynomial> : True {
   typedef cons<is_polynomial, is_polynomial> discriminant;
};


template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const monomial_type& m, const typename monomial_type::coefficient_type& c)
{
   return Polynomial_base<monomial_type>(m)+=c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const typename monomial_type::coefficient_type& c, const monomial_type& m)
{
   return m+c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const monomial_type& m, const typename monomial_type::coefficient_type& c)
{
   return Polynomial_base<monomial_type>(m)-=c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const typename monomial_type::coefficient_type& c, const monomial_type& m)
{
   return Polynomial_base<monomial_type>(c)-=m;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const monomial_type& m1, const monomial_type& m2)
{
   return Polynomial_base<monomial_type>(m1)+=m2;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const monomial_type& m1, const monomial_type& m2)
{
   return Polynomial_base<monomial_type>(m1)-=m2;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const Term_base<monomial_type>& t, const typename monomial_type::coefficient_type& c)
{
   return Polynomial_base<monomial_type>(t)+=c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const typename monomial_type::coefficient_type& c, const Term_base<monomial_type>& t)
{
   return t+c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const Term_base<monomial_type>& t, const typename monomial_type::coefficient_type& c)
{
   return Polynomial_base<monomial_type>(t)-=c;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const typename monomial_type::coefficient_type& c, const Term_base<monomial_type>& t)
{
   return Polynomial_base<monomial_type>(c)-=t;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const Term_base<monomial_type>& t, const monomial_type& m)
{
   return Polynomial_base<monomial_type>(t)+=m;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const monomial_type& m, const Term_base<monomial_type>& t)
{
   return t+m;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const Term_base<monomial_type>& t, const monomial_type& m)
{
   return Polynomial_base<monomial_type>(t)-=m;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const monomial_type& m, const Term_base<monomial_type>& t)
{
   return Polynomial_base<monomial_type>(m)-=t;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator+ (const Term_base<monomial_type>& t1, const Term_base<monomial_type>& t2)
{
   return Polynomial_base<monomial_type>(t1)+=t2;
}

template <typename monomial_type> inline
typename Polynomial_result<monomial_type>::type
operator- (const Term_base<monomial_type>& t1, const Term_base<monomial_type>& t2)
{
   return Polynomial_base<monomial_type>(t1)-=t2;
}

namespace operations {

// these operations will be required e.g. for Vector<Polynomial> or Matrix<Polynomial>

template <typename OpRef>
struct neg_impl<OpRef, is_polynomial> {
   typedef OpRef argument_type;
   typedef typename deref<OpRef>::type result_type;

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

   void assign(typename lvalue_arg<OpRef>::type x) const
   {
      x.negate();
   }
};

template <typename LeftRef, class RightRef>
struct add_impl<LeftRef, RightRef, cons<is_polynomial, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l+r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l+=r;
   }
};

template <typename LeftRef, class RightRef>
struct add_impl<LeftRef, RightRef, cons<is_polynomial, is_scalar> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l+r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l+=r;
   }
};

template <typename LeftRef, class RightRef>
struct add_impl<LeftRef, RightRef, cons<is_scalar, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<RightRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l+r;
   }
};

template <typename LeftRef, class RightRef>
struct sub_impl<LeftRef, RightRef, cons<is_polynomial, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l-r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l-=r;
   }
};

template <typename LeftRef, class RightRef>
struct sub_impl<LeftRef, RightRef, cons<is_polynomial, is_scalar> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l-r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l-=r;
   }
};

template <typename LeftRef, class RightRef>
struct sub_impl<LeftRef, RightRef, cons<is_scalar, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<RightRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l-r;
   }
};

template <typename LeftRef, class RightRef>
struct mul_impl<LeftRef, RightRef, cons<is_polynomial, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l*r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l*=r;
   }
};

template <typename LeftRef, class RightRef>
struct mul_impl<LeftRef, RightRef, cons<is_polynomial, is_scalar> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<LeftRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l*r;
   }

   void assign(typename lvalue_arg<LeftRef>::type l, typename function_argument<RightRef>::const_type r) const
   {
      l*=r;
   }
};

template <typename LeftRef, class RightRef>
struct mul_impl<LeftRef, RightRef, cons<is_scalar, is_polynomial> > {
   typedef LeftRef first_argument_type;
   typedef RightRef second_argument_type;
   typedef typename deref<RightRef>::type result_type;

   result_type operator() (typename function_argument<LeftRef>::const_type l,
                           typename function_argument<RightRef>::const_type r) const
   {
      return l*r;
   }
};

class cmp_weighted {
protected:
   Array<int> weights;
public:
   cmp_weighted() {}

   cmp_weighted(const Array<int>& weight_arg) : weights(weight_arg) {}

   // term order: graded revlex order

   template <typename Exponent>
   cmp_value compare_values(const SparseVector<Exponent>& m1, const SparseVector<Exponent>& m2) const
   {
      Exponent weighted_sum1(0), weighted_sum2(0);
      for (typename SparseVector<Exponent>::const_iterator it1=m1.begin();
           !it1.at_end(); ++it1) {
         const int index=it1.index();
         weighted_sum1 += index<weights.size() ? *it1 * weights[index] : *it1;
      }
      for (typename SparseVector<Exponent>::const_iterator it2=m2.begin();
           !it2.at_end(); ++it2) {
         const int index=it2.index();
         weighted_sum2 += index<weights.size() ? *it2 * weights[index] : *it2;
      }

      const cmp_value weight_cmp=cmp()(weighted_sum1, weighted_sum2);
      if (weight_cmp != cmp_eq) return weight_cmp;

      // revlex as tie-braker
      typename SparseVector<Exponent>::const_reverse_iterator
         rit1=m1.rbegin(), rit2=m2.rbegin();

      for (; !rit1.at_end() && !rit2.at_end(); ++rit1, ++rit2) {
         const cmp_value index_cmp=cmp()(rit1.index(), rit2.index());
         if (index_cmp != cmp_eq) return index_cmp;
         const cmp_value exp_cmp=cmp()(*rit1, *rit2);
         if (exp_cmp != cmp_eq) return exp_cmp;
      }

      return !rit1.at_end() ? cmp_gt :
             !rit2.at_end() ? cmp_lt : cmp_eq;
   }

   template <typename Coefficient, typename Exponent>
   cmp_value operator() (const Monomial<Coefficient,Exponent>& m1, const Monomial<Coefficient,Exponent>& m2) const
   {
      if (m1.get_ring() != m2.get_ring()) throw std::runtime_error("Monomials of different rings");
      return compare_values(m1.get_value(), m2.get_value());
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< Monomial<Coefficient,Exponent>, Monomial<Coefficient,Exponent> > :
   cmp_common< Monomial<Coefficient,Exponent>, Monomial<Coefficient,Exponent> > {

   cmp_value operator() (const Monomial<Coefficient,Exponent>& m1, const Monomial<Coefficient,Exponent>& m2) const
   {
      if (m1.get_ring() != m2.get_ring()) throw std::runtime_error("Monomials of different rings");
      return cmp_weighted().compare_values(m1.get_value(), m2.get_value());
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< UniMonomial<Coefficient,Exponent>, UniMonomial<Coefficient,Exponent> > :
   cmp_common< UniMonomial<Coefficient,Exponent>, UniMonomial<Coefficient,Exponent> > {

   cmp_value operator() (const UniMonomial<Coefficient,Exponent>& m1, const UniMonomial<Coefficient,Exponent>& m2) const
   {
      return cmp()(m1.get_value(), m2.get_value());
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< Term<Coefficient, Exponent>, Term<Coefficient, Exponent> > :
   cmp_common< Term<Coefficient, Exponent>, Term<Coefficient, Exponent> > {

   cmp_value operator() (const Term<Coefficient, Exponent>& x1, const Term<Coefficient, Exponent>& x2) const
   {
      return x1.compare(x2);
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< UniTerm<Coefficient, Exponent>, UniTerm<Coefficient, Exponent> > :
   cmp_common< UniTerm<Coefficient, Exponent>, UniTerm<Coefficient, Exponent> > {

   cmp_value operator() (const UniTerm<Coefficient, Exponent>& x1, const UniTerm<Coefficient, Exponent>& x2) const
   {
      return x1.compare(x2);
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< Polynomial<Coefficient, Exponent>, Polynomial<Coefficient, Exponent> > :
   cmp_common< Polynomial<Coefficient, Exponent>, Polynomial<Coefficient, Exponent> > {

   cmp_value operator() (const Polynomial<Coefficient, Exponent>& x1, const Polynomial<Coefficient, Exponent>& x2) const
   {
      return x1.compare(x2);
   }
};

template <typename Coefficient, typename Exponent>
struct cmp_opaque< UniPolynomial<Coefficient, Exponent>, UniPolynomial<Coefficient, Exponent> > :
   cmp_common< UniPolynomial<Coefficient, Exponent>, UniPolynomial<Coefficient, Exponent> > {

   cmp_value operator() (const UniPolynomial<Coefficient, Exponent>& x1, const UniPolynomial<Coefficient, Exponent>& x2) const
   {
      return x1.compare(x2);
   }
};

} // end namespace operations

template <typename Coefficient, typename Exponent>
cmp_value Monomial<Coefficient,Exponent>::compare_values(const value_type& x1, const value_type& x2)
{
   return operations::cmp_weighted().compare_values(x1,x2);
}

} // end namespace pm

namespace polymake {
   using pm::Monomial;
   using pm::UniMonomial;
   using pm::Term;
   using pm::UniTerm;
   using pm::Polynomial;
   using pm::UniPolynomial;
}

namespace std {
   template <typename Coefficient, typename Exponent>
   void swap(pm::Monomial<Coefficient,Exponent>& x1, pm::Monomial<Coefficient,Exponent>& x2) { x1.swap(x2); }

   template <typename Coefficient, typename Exponent>
   void swap(pm::UniMonomial<Coefficient,Exponent>& x1, pm::UniMonomial<Coefficient,Exponent>& x2) { x1.swap(x2); }

   template <typename Coefficient, typename Exponent>
   void swap(pm::Term<Coefficient,Exponent>& x1, pm::Term<Coefficient,Exponent>& x2) { x1.swap(x2); }

   template <typename Coefficient, typename Exponent>
   void swap(pm::UniTerm<Coefficient,Exponent>& x1, pm::UniTerm<Coefficient,Exponent>& x2) { x1.swap(x2); }

   template <typename Coefficient, typename Exponent>
   void swap(pm::Polynomial<Coefficient,Exponent>& x1, pm::Polynomial<Coefficient,Exponent>& x2) { x1.swap(x2); }

   template <typename Coefficient, typename Exponent>
   void swap(pm::UniPolynomial<Coefficient,Exponent>& x1, pm::UniPolynomial<Coefficient,Exponent>& x2) { x1.swap(x2); }
}

#endif // POLYMAKE_POLYNOMIAL_H

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