//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995, 1996 by the LiDIA Group
//
// File        : polynomial.h
// Author      : Stefan Neis (SN)
//

/*
$Id
*/

/**
 ** This  include  file  supports basic polynomial operations over
 ** all types
 **
 ** Most   of   this   code ( +, -, *, /, gcd, xgcd)  was
 ** originally  written by Victor Shoup (finite fields); Thomas Papanikolaou
 ** changed his implementation to use templates and the names of the
 ** functions to our standard. Then I extended this code to be able to
 ** treat polynomials over other types, especially over bigints
 **
 **                                             Stefan Neis
 **/

#ifndef LIDIA_POLYNOMIAL_H
#define LIDIA_POLYNOMIAL_H

#ifdef __GNUG__
#define LiDIA_INLINE inline
#else
#define LiDIA_INLINE
#endif

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:lidia.h>
#include <LiDIA:base_vector.h>
#include <LiDIA:bigint.h>
#include <LiDIA:bigrational.h>
#include <LiDIA:bigfloat.h>
#include <LiDIA:bigcomplex.h>
#include <LiDIA:xerror.h>
#else
//#include <LiDIA/lidia.h>
#include <LiDIA/base_vector.h>
#include <LiDIA/bigint.h>
#include <LiDIA/bigrational.h>
#include <LiDIA/bigfloat.h>
#include <LiDIA/bigcomplex.h>
#include <LiDIA/xerror.h>
#endif

//
// The first two classes in this file (base_polynomial and
//                                     field_polynomial)
// are for internal use only !!!
//
// The user is supposed to always use "polynomial < T >".
//

/**
 ** debug defines / error defines
 **/
extern const char *PRT;
extern const char *poly_error_msg[];

#define DV_BP LDBL_UNIPOL
#define DM_BP "base_polynomial"
#define LP_ERROR poly_error_msg

/**
 ** debug level
 **
 **   0 : constructors / destructor
 **   1 : internal functions
 **   2 : access functions
 **   3 : assignments
 **   4 : comparison
 **   5 : arithmetic functions
 **   6 : input / output
 **/

template < class T > class base_polynomial
                   // Type T must at least supply the functions add(c,a,b)
                   // subtract(c,a,b), negate(c,a) and multiply (c,a,b).
{
protected:

  /**
   ** base_polynomial<T> a = c[0] + c[1]*X + ... + c[deg]*X^deg
   **/

  T * coeff;
  lidia_size_t deg;


  void copy_data(T * d, const T * vd, lidia_size_t al);

  void remove_leading_zeros();

  bool equal(const base_polynomial < T > &) const;
  void negate(const base_polynomial < T > &);
  void add(const base_polynomial < T > &a, const base_polynomial < T > &b);
  void add(const base_polynomial < T > &a, const T &b);
  void add(const T &b, const base_polynomial < T > &a);
  void subtract(const base_polynomial < T > &a, const base_polynomial < T > &b);
  void subtract(const base_polynomial < T > &a, const T &b);
  void subtract(const T &b, const base_polynomial < T > &a);
  void multiply(const base_polynomial < T > &a, const base_polynomial < T > &b);
  void multiply(const base_polynomial < T > &a, const T &b);
  void multiply(const T &b, const base_polynomial < T > &a);
  void power(const base_polynomial < T > & a, const bigint & b);
  void derivative(const base_polynomial < T > & a);
  void write(ostream & out) const;
  void read(istream & in);


public:

  /**
   ** constructors and destructor
   **/

  base_polynomial();
  base_polynomial(const T & x);
  base_polynomial(const T * v, lidia_size_t d);
  base_polynomial(const base_vector < T > v);
  base_polynomial(const base_polynomial < T > &p);
  ~base_polynomial();

  /**
   ** inline friend functions
   **/

  /**  comparisons  **/

  inline friend bool operator == (const base_polynomial < T > &a, const base_polynomial < T > &b)
  {return a.equal(b);}

  inline friend bool operator != (const base_polynomial < T > &a, const base_polynomial < T > &b)
  {return !a.equal(b);}

  T lead_coeff() const;
  friend T lead_coeff(const base_polynomial < T > &a)
  { return a.lead_coeff(); }

  T const_term() const;
  friend T const_term(const base_polynomial < T > &a)
  { return a.const_term(); }

  /**
   ** member functions
   **/

  int set_data ( const T * d, lidia_size_t l );
  T* get_data () const;

  lidia_size_t degree() const
  {
    debug_handler_l(DM_BP, "in member - function "
                    "degree ()" , DV_BP + 2);
    return deg;
  }

  void set_degree(lidia_size_t);

  bool is_zero() const
  {
    return (deg < 0);
  }

  bool is_one() const
  {
    return (deg == 0 && coeff[0] == 1);
  }

  bool is_x() const
  {
    return (deg == 1 && coeff[1] == 1 && coeff[0] == 0);
  }

  /**
   ** assignment
   **/

  void assign(const  T &);

  LiDIA_INLINE friend void assign(base_polynomial < T > & a,
                     const T & b)
  {a.assign(b);}

  base_polynomial < T > & operator = (const T &a)
  {this->assign(a); return *this;}

  void assign(const base_polynomial < T > &);

  LiDIA_INLINE friend void assign(base_polynomial < T > & a,
                     const base_polynomial < T > & b)
  {a.assign(b);}

  base_polynomial < T > & operator = (const base_polynomial < T > &a)
  {this->assign(a); return *this;}

  void assign_zero();
  void assign_one();
  void assign_x();

  /**
   ** operator overloading
   **/

  T & operator[] (lidia_size_t i) const
  {
    debug_handler_l(DM_BP, "in operator "
                  "[] (lidia_size_t)",  DV_BP + 2);
    if ((i < 0) || (i > deg))
      lidia_error_handler_para(i, "i", "0 <= i <= degree",
                               "T & operator[] (lidia_size_t i) const",
                               DM_BP, LP_ERROR[2]);
    return coeff[i];
  }

  T operator() (const T & value) const;

  /**
   ** arithmetic procedures
   **/

  inline friend void negate(base_polynomial < T > & c, const base_polynomial < T > &a)
  {c.negate(a);}

  inline friend void add(base_polynomial < T > & c,
            const base_polynomial < T > & a, const base_polynomial < T > & b)
  {c.add(a,b);}

  inline friend void add(base_polynomial < T > & c,
            const base_polynomial < T > & a, const T & b)
  {c.add(a,b);}

  inline friend void add(base_polynomial < T > & c,
            const T & b, const base_polynomial < T > & a)
  {c.add(b,a);}


  inline friend void subtract(base_polynomial < T > & c,
            const base_polynomial < T > & a, const base_polynomial < T > & b)
  {c.subtract(a,b);}

  inline friend void subtract(base_polynomial < T > & c,
            const base_polynomial < T > & a, const T & b)
  {c.subtract(a,b);}

  inline friend void subtract(base_polynomial < T > & c,
            const T & b, const base_polynomial < T > & a)
  {c.subtract(b,a);}


  inline friend void multiply(base_polynomial < T > & c,
            const base_polynomial < T > & a, const base_polynomial < T > & b)
  {c.multiply(a,b);}

  inline friend void multiply(base_polynomial < T > & c,
            const base_polynomial < T > & a, const T & b)
  {c.multiply(a,b);}

  inline friend void multiply(base_polynomial < T > & c,
            const T & b, const base_polynomial < T > & a)
  {c.multiply(a,b);}


  inline friend void power(base_polynomial < T > & c,
                    const base_polynomial < T > & a, const bigint & b)
  {c.power(a,b);}

  /**
   ** operator overloading
   **/

  inline friend
  base_polynomial < T > operator - (const base_polynomial < T > &a)
  {base_polynomial < T > c; c.negate(a); return c;}

  inline friend
  base_polynomial < T > operator + (const base_polynomial < T > &a,
                                    const base_polynomial < T > &b)
  {base_polynomial < T > c; c.add(a,b); return c;}


  inline friend
  base_polynomial < T > operator + (const base_polynomial < T > & a,
                                    const T & b)
  {base_polynomial < T > c; c.add(a,b); return c;}

  inline friend
  base_polynomial < T > operator + (const T & b,
                                    const base_polynomial < T > & a)
  {base_polynomial < T > c; c.add(b,a); return c;}

  inline friend
  base_polynomial < T > operator - (const base_polynomial < T > &a,
                                    const base_polynomial < T > &b)
  {base_polynomial < T > c; c.subtract(a,b); return c;}

  inline friend
  base_polynomial < T > operator - (const base_polynomial < T > &a,
                                    const T &b)
  {base_polynomial < T > c; c.subtract(a,b); return c;}

  inline friend
  base_polynomial < T > operator - (const T &a,
                                    const base_polynomial < T > &b)
  {base_polynomial < T > c; c.subtract(b,a); return c;}

  inline friend
  base_polynomial < T > operator * (const base_polynomial < T > &a,
                                    const base_polynomial < T > &b)
  {base_polynomial < T > c; c.multiply(a,b); return c;}

  inline friend
  base_polynomial < T > operator * (const base_polynomial < T > &a,
                                    const T &b)
  {base_polynomial < T > c; c.multiply(a,b); return c;}

  inline friend
  base_polynomial < T > operator * (const T &b,
                                    const base_polynomial < T > &a)
  {base_polynomial < T > c; c.multiply(b,a); return c;}

  base_polynomial < T > & operator += (const base_polynomial < T > &a);

  base_polynomial < T > & operator += (const T &a);

  base_polynomial < T > & operator -= (const base_polynomial < T > &a);

  base_polynomial < T > & operator -= (const T &a);

  base_polynomial < T > & operator *= (const base_polynomial < T > &a);

  base_polynomial < T > & operator *= (const T &a);

  /**
   ** functions
   **/
  inline friend void derivative(base_polynomial < T > &c,
                         const base_polynomial < T > &a)
  {c.derivative(a);}

  inline friend
  base_polynomial < T > derivative(const base_polynomial < T > & a)
  {base_polynomial < T > c; c.derivative(a); return c;}

  /**
   ** input / output
   **/

  inline friend istream & operator >> (istream &is, base_polynomial < T > &a)
  {a.read(is); return is;}
  istream & read_verbose(istream &);
  inline friend ostream & operator << (ostream &os, const base_polynomial < T > &a)
  {a.write(os); return os;}


};

/**
 ** debug level
 **
 **   0 : div_rem
 **   1 : power_mod, integral
 **   2 : gcd
 **/

template < class T > class field_polynomial: public base_polynomial < T >
         // Implements polynomials over fields.
         // We require all functions needed for base_polynomial and
         // additionally the functions divide(c,a,b) and invert(c,a).
{
protected:
  void div_rem(field_polynomial < T > & r, const base_polynomial < T > & a, const base_polynomial < T > & b);
  void divide(const base_polynomial < T > & a, const T & b);
  void power_mod(const base_polynomial < T > & a, const bigint & b, const base_polynomial < T > & f);
  void integral(const base_polynomial < T > & a);
  void gcd(const base_polynomial < T > &aa, const base_polynomial < T > &bb);
  void xgcd(field_polynomial < T > &x, field_polynomial < T > &y, const base_polynomial < T > &aa, const base_polynomial < T > &bb);

public:

  /**
   ** constructors and destructor
   **/

  field_polynomial(): base_polynomial < T > ()
  { }

  field_polynomial(T x): base_polynomial < T > (x)
  { }

  field_polynomial(const T * v, lidia_size_t d): base_polynomial < T > (v,d)
  { }

  field_polynomial(const base_vector < T > v): base_polynomial < T > (v)
  { }

  field_polynomial(const base_polynomial < T > &p): base_polynomial < T > (p)
  { }

  ~field_polynomial()
  { }

  field_polynomial < T > &operator = (const base_polynomial < T > &a)
  {((base_polynomial < T > *)this)->assign(a); return *this;}

  /**
   ** Division and related stuff
   **/

  friend void div_rem(field_polynomial < T > & q,
                      field_polynomial < T > & r,
                      const base_polynomial < T > & a,
                      const base_polynomial < T > & b)
  {q.div_rem(r,a,b);}

  friend void divide(field_polynomial < T > & c,
                     const base_polynomial < T > & a, const T & b)
  {c.divide(a,b);}

  friend
  void divide(field_polynomial < T > & q, const base_polynomial < T > & a,
              const base_polynomial < T > & b)
  {field_polynomial < T > r; q.div_rem(r, a, b);}

  friend
  void remainder(field_polynomial < T > & r, const base_polynomial < T > & a,
                 const base_polynomial < T > & b)
  {field_polynomial < T > q; q.div_rem(r, a, b);}

  friend void power_mod(field_polynomial < T > & c,
                        const base_polynomial < T > & a, const bigint & b,
                        const base_polynomial < T > & f)
  {c.power_mod(a,b,f);}

  inline friend
  field_polynomial < T > operator / (const base_polynomial < T > &a,
                                     const base_polynomial < T > &b)
  {field_polynomial < T > q, r; q.div_rem(r, a, b); return q;}

  inline friend
  field_polynomial < T > operator / (const base_polynomial < T > &a,
                                     const T & b)
  {field_polynomial < T > q; q.divide(a, b); return q;}

  inline friend
  field_polynomial < T > operator % (const base_polynomial < T > &a,
                                     const base_polynomial < T > &b)
  {field_polynomial < T > q, r; q.div_rem(r, a, b); return r;}

  field_polynomial < T > & operator /= (const base_polynomial < T > &a);

  field_polynomial < T > & operator /= (const T &a);

  field_polynomial < T > & operator %= (const base_polynomial < T > &a);

  friend void integral(field_polynomial < T > &c,
                       const base_polynomial < T > &a)
  {c.integral(a);}

  friend field_polynomial < T > integral(const base_polynomial < T > & a)
  {field_polynomial < T > c; c.integral(a); return c;}

  friend field_polynomial < T > gcd(const base_polynomial < T > &aa,
                              const base_polynomial < T > &bb)
  {field_polynomial < T > d; d.gcd(aa,bb); return d;}

  friend field_polynomial < T > xgcd(field_polynomial < T > &x,
                               field_polynomial < T > &y,
                               const base_polynomial < T > &aa,
                               const base_polynomial < T > &bb)
  {field_polynomial < T > d; d.xgcd(x,y,aa,bb); return d;}
};


//
// Now the general class for polynomials:
//

template < class T > class polynomial: public base_polynomial < T >
{
public:
  /**
   ** constructors and destructor
   **/

  polynomial(): base_polynomial < T > ()
  { }

  polynomial(T x): base_polynomial < T > (x)
  { }

  polynomial(const T * v, lidia_size_t d): base_polynomial < T > (v,d)
  { }

  polynomial(const base_vector < T > v): base_polynomial < T > (v)
  { }

  polynomial(const base_polynomial < T > &p): base_polynomial < T > (p)
  { }

  ~polynomial()
  { }

  polynomial < T > &operator = (const base_polynomial < T > &a)
  {((base_polynomial < T > *)this)->assign(a); return *this;}
};

/**
 ** debug level
 **
 **   0 : div_rem
 **   1 : power_mod
 **   2 : gcd
 **   3 : div_rem mod p
 **   4 : power(_mod) mod p
 **   5 : gcd mod p
 **   6 : factoring mod p -- internals
 **   7 : factoring mod p -- global
 **   8 : number of real roots
 **/

class polynomial <bigint> : public base_polynomial <bigint>
{
  // For some routines, I would like to use Thomas Pfahler's
  // fp_polynomial, which is not yet finished...
public:

  /**
   ** constructors and destructor
   **/

  polynomial(): base_polynomial <bigint> ()
  { }

  polynomial(bigint x): base_polynomial <bigint> (x)
  { }

  polynomial(const bigint * v, lidia_size_t d): base_polynomial <bigint> (v,d)
  { }

  polynomial(const base_vector <bigint> v): base_polynomial <bigint> (v)
  { }

  polynomial(const base_polynomial <bigint> &p): base_polynomial <bigint> (p)
  { }

  ~polynomial()
  { }

  polynomial <bigint> &operator = (const polynomial <bigint> &a) // MM
  {((base_polynomial <bigint> *)this)->assign(a); return *this;}

  /**
   ** Cast operators:
   **/

  operator base_polynomial<bigrational>() const;
  operator base_polynomial<bigfloat>() const;
  operator base_polynomial<bigcomplex>() const;

  /**
   ** Pseudo - division and related stuff
   **/

  friend void div_rem(polynomial <bigint> &q, polynomial <bigint> &r,
                      const base_polynomial <bigint> &a,
                      const base_polynomial <bigint> &b);

  friend void divide(polynomial <bigint> & c,
                     const base_polynomial <bigint> & a, const bigint & b);

  LiDIA_INLINE friend
  void divide(polynomial <bigint> & q, const base_polynomial <bigint> & a,
              const base_polynomial <bigint> & b)
  {polynomial <bigint> r; div_rem(q, r, a, b);}

  LiDIA_INLINE friend
  void remainder(polynomial <bigint> & r, const base_polynomial <bigint> & a,
                 const base_polynomial <bigint> & b)
  {polynomial <bigint> q; div_rem(q, r, a, b);}

  friend void power_mod(polynomial <bigint> & c,
                        const base_polynomial <bigint> & a, const bigint & b,
                        const base_polynomial <bigint> & f);

  LiDIA_INLINE friend
  polynomial <bigint> operator / (const base_polynomial <bigint> &a,
                                  const base_polynomial <bigint> &b)
  {polynomial <bigint> q, r; div_rem(q, r, a, b); return q;}

  LiDIA_INLINE friend
  polynomial <bigint> operator / (const base_polynomial <bigint> &a,
                                  const bigint & b)
  {polynomial <bigint> q; divide(q, a, b); return q;}

  LiDIA_INLINE friend
  polynomial <bigint> operator % (const base_polynomial <bigint> &a,
                                  const base_polynomial <bigint> &b)
  {polynomial <bigint> q, r; div_rem(q, r, a, b); return r;}

  polynomial <bigint> & operator /= (const base_polynomial <bigint> &a)
  {polynomial <bigint> r; div_rem(*this, r, *this, a); return *this;}

  polynomial <bigint> & operator /= (const bigint &a)
  { divide(*this, *this, a); return *this;}

  polynomial <bigint> & operator %= (const base_polynomial <bigint> &a)
  {polynomial <bigint> q; div_rem(q, *this, *this, a); return *this;}

  /**
   ** Gcd's and related stuff, i.e. content and primitive part.
   **/

  friend bigint cont(const base_polynomial <bigint> &a);

  friend polynomial <bigint> pp(const base_polynomial <bigint> &a);

  friend polynomial <bigint> gcd(const base_polynomial <bigint> &aa,
                              const base_polynomial <bigint> &bb);

  friend polynomial <bigint> xgcd(polynomial <bigint> &x,
                               polynomial <bigint> &y,
                               const base_polynomial <bigint> &aa,
                               const base_polynomial <bigint> &bb);

  /**
   ** Number of real roots
   **/

  friend lidia_size_t no_of_real_roots(const base_polynomial<bigint>& poly_T);


  /**
   ** Modular Computations needed for factoring modulo p
   **/

  friend void div_rem(polynomial <bigint> &q, polynomial <bigint> &r,
                      const base_polynomial <bigint> &a,
                      const base_polynomial <bigint> &b, const bigint & p);

  friend void remainder(polynomial <bigint> & c,
                        const base_polynomial <bigint> & a, const bigint & b);

  LiDIA_INLINE friend
  polynomial <bigint> operator % (const base_polynomial <bigint> &a,
                                         const bigint &b)
  {polynomial <bigint> r; remainder(r, a, b); return r;}

  LiDIA_INLINE polynomial <bigint> & operator %= (const bigint &b)
  {remainder(*this, *this, b); return *this;}

  friend void power(polynomial <bigint> & c,
                    const base_polynomial <bigint> & a,
                    const bigint & b, const bigint & p);

  friend void power_mod(polynomial <bigint> & c,
                        const base_polynomial <bigint> & a, const bigint & b,
                        const base_polynomial <bigint> & f, const bigint & p);

  friend polynomial <bigint> gcd(const base_polynomial <bigint> &aa,
                                 const base_polynomial <bigint> &bb,
                                 const bigint & p);

  /**
   ** Factoring modulo p
   **/

  friend void squarefree_factor(const base_polynomial <bigint> &f,
                                polynomial <bigint> * fa,
                                const bigint & p);

  friend int ddf(const base_polynomial <bigint> &f0,
                 polynomial <bigint> * fa,
                 const bigint & p);

  friend void can_zass(const base_polynomial <bigint> &f,
                       polynomial < bigint> * fa,
                       lidia_size_t d, const bigint & p,
                       lidia_size_t count = 0);

  friend void factor(const base_polynomial <bigint> &f,
                     polynomial <bigint> * fa,  lidia_size_t * exponent,
                     const bigint & p);
};

class polynomial <bigrational>: public field_polynomial <bigrational>
{
public:

  polynomial(): field_polynomial <bigrational> ()
  { }

  polynomial(bigrational x): field_polynomial <bigrational> (x)
  { }

  polynomial(const bigrational * v, lidia_size_t d):
    field_polynomial <bigrational> (v,d)
  { }

  polynomial(const base_vector <bigrational> v):
    field_polynomial <bigrational> (v)
  { }

  polynomial(const base_polynomial <bigrational> &p):
    field_polynomial <bigrational> (p)
  { }

  ~polynomial()
  { }

  polynomial <bigrational> & operator = (const base_polynomial <bigrational>
                                         &a)
  {((base_polynomial <bigrational> *)this)->assign(a); return *this;}


  /**
   ** Cast operators:
   **/

  operator base_polynomial<bigfloat>() const;
  operator base_polynomial<bigcomplex>() const;
};

class polynomial <bigfloat>: public field_polynomial <bigfloat>
{
public:

  polynomial(): field_polynomial <bigfloat> ()
  { }

  polynomial(bigfloat x): field_polynomial <bigfloat> (x)
  { }

  polynomial(const bigfloat * v, lidia_size_t d):
    field_polynomial <bigfloat> (v,d)
  { }

  polynomial(const base_vector <bigfloat> v): field_polynomial <bigfloat> (v)
  { }

  polynomial(const base_polynomial <bigfloat> &p):
    field_polynomial <bigfloat> (p)
  { }

  ~polynomial()
  { }

  polynomial <bigfloat> & operator = (const base_polynomial <bigfloat> &a)
  {((base_polynomial <bigfloat> *)this)->assign(a); return *this;}

  operator base_polynomial<bigcomplex>() const;
};

/**
 ** debug level
 **
 **   0 : remove_leading_zeros -- redefine.
 **   1 : integral
 **   2 : root
 **   3 : cohen
 **   4 : roots
 **/

class polynomial <bigcomplex>: public field_polynomial <bigcomplex>
{
public:

  polynomial(): field_polynomial <bigcomplex> ()
  { }

  polynomial(bigcomplex x): field_polynomial <bigcomplex> (x)
  { }

  polynomial(const bigcomplex * v, lidia_size_t d):
    field_polynomial <bigcomplex> (v,d)
  { }

  polynomial(const base_vector <bigcomplex> v):field_polynomial <bigcomplex>(v)
  { }

  polynomial(const base_polynomial <bigcomplex> &p):
    field_polynomial <bigcomplex> (p)
  { }

  ~polynomial()
  { }

  polynomial <bigcomplex> & operator = (const base_polynomial <bigcomplex> &a)
  {((base_polynomial <bigcomplex> *)this)->assign(a); return *this;}

  friend bigcomplex root(const base_polynomial <bigcomplex> &,
                         const bigcomplex &);
  friend void cohen(const base_polynomial <bigcomplex> &,
                    bigcomplex *, int, int &);
  friend void roots(const base_polynomial <bigcomplex> &,
                    bigcomplex *);
  friend bigcomplex integral(const bigfloat &, const bigfloat &,
                             const base_polynomial <bigcomplex> &);
};

#undef DV_BP
#undef DM_BP
#undef LP_ERROR

#include <LiDIA/gf_polynomial.h>

#ifdef LIDIA_INCLUDE_C
#include <LiDIA/polynomial.c>
#endif

#endif
