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

/*
$Id
*/

#include <LiDIA/polynomial.h>

#define DV_BP LDBL_UNIPOL
#define DM_BP "base_polynomial"
#define DV_FP LDBL_UNIPOL+8
#define DM_FP "field_polynomial"
#define DV_P LDBL_UNIPOL+12
#define DM_P "polynomial<T>"
#define LP_ERROR poly_error_msg

// BASE_POLYNOMIAL FUNCTIONS

/**
 ** constructors and destructor
 **/

template < class T >
base_polynomial < T >::base_polynomial()
{
  debug_handler_l(DM_BP, "in constructor "
                "base_polynomial()", DV_BP);
  deg = -1;
  coeff = NULL;
}

template < class T >
base_polynomial < T >::base_polynomial(const T & x)
{
  debug_handler_l(DM_BP, "in constructor "
                "base_polynomial(const T &)", DV_BP);
  if (x == 0){
    deg = -1;
    coeff = NULL;
  }
  else{
    deg = 0;
    coeff = new T[1];
    memory_handler(coeff, DM_BP, "base_polynomial(const T &) :: "
                 "Error in memory allocation (coeff)");
    coeff[0] = x;
  }
}

template < class T >
base_polynomial < T >::base_polynomial(const T * v, lidia_size_t d)
{
  debug_handler_l(DM_BP, "in constructor "
                "base_polynomial(const T *, lidia_size_t)", DV_BP);
  if (d < 0 || v == NULL)
    lidia_error_handler_para(d, "d", "d >= 0",
                           PRT, "v", "v != NULL",
                           "base_polynomial < T >::"
                           "base_polynomial(const T * v, lidia_size_t d)",
                           DM_BP, LP_ERROR[0]);
  deg = d;
  coeff = new T[d + 1];
  memory_handler(coeff, DM_BP, "base_polynomial(const T *,lidia_size_t)"
               " :: Error in memory allocation (coeff)");
  copy_data(coeff, v, deg);
  remove_leading_zeros();
}

template < class T >
base_polynomial < T >::base_polynomial(const base_vector < T > v)
{
  debug_handler_l(DM_BP, "in constructor "
                "base_polynomial(const base_vector < T >)", DV_BP);
  deg = v.size()-1;
  coeff = v.get_data();
  remove_leading_zeros();
}

template < class T >
base_polynomial < T >::base_polynomial(const base_polynomial < T > &p)
{
  debug_handler_l(DM_BP, "in constructor "
                "base_polynomial(const base_polynomial < T >)", DV_BP);
  deg = p.deg;
  if (deg < 0)
    coeff = NULL;
  else{
    coeff = new T[deg + 1];
    memory_handler(coeff, DM_BP, "base_polynomial(const T *,lidia_size_t)"
                 " :: Error in memory allocation (coeff)");
    copy_data(coeff, p.coeff, deg);
  }
}

template < class T >
base_polynomial < T >::~base_polynomial()
{
  debug_handler_l(DM_BP, "in destructor "
                "~base_polynomial()", DV_BP);
  if (deg >= 0)
    delete[] coeff;
}


/**
 ** comparisons
 **/

template < class T >
bool base_polynomial < T >::equal(const base_polynomial < T > &b) const
{
  debug_handler_l(DM_BP, "in member - function "
                "bool equal(const base_polynomial < T > &)",  DV_BP + 4);
  const T *ap, *bp;
  lidia_size_t i;
  if (deg != b.deg)
    return false;
  for (i = deg + 1, ap = coeff, bp = b.coeff; i; i--, ap++, bp++)
    if (*ap != *bp)
    return false;
  return true;
}


/**
 ** member functions
 **/

template < class T >
T base_polynomial < T >::lead_coeff() const
{
  if (deg < 0) return (T)0; else return coeff[deg];
}

template < class T >
T base_polynomial < T >::const_term() const
{
  if (deg < 0) return (T)0; else return coeff[0];
}

template < class T >
int base_polynomial < T >::set_data ( const T * d, lidia_size_t l )
{
  debug_handler_l(DM_BP, "in member - function "
                "set_data ( const T *, lidia_size_t )" , DV_BP + 2);
  if (l <= 0 || d == NULL)
    lidia_error_handler_para(l, "l", "l > 0",
                           PRT, "d", "d != NULL",
                           "base_polynomial < T >::"
                           "set_data(const T * d, lidia_size_t l)",
                           DM_BP, LP_ERROR[0]);
  if (deg >= 0)
    delete[] coeff;
  coeff = new T[l];
  memory_handler(coeff, DM_BP, "set_data(const T *, lidia_size_t) :: "
               "Error in memory allocation (coeff)");

  deg=l-1;

  for (register lidia_size_t i = 0; i < l; i++)
    coeff[i] = d[i];
  remove_leading_zeros();
  return 0;
}

template < class T >
T* base_polynomial < T >::get_data () const
{
  debug_handler_l(DM_BP, "in member - function "
                "get_data ()" , DV_BP + 2);

  T * d;

  if (deg < 0){
    d = new T[1];
    memory_handler(d, DM_BP, "get_data () :: "
                 "Error in memory allocation (d)");
    d[0] = 0;
    return d;
  }

  d = new T [deg+1];
  memory_handler(d, DM_BP, "get_data () :: "
               "Error in memory allocation (d)");

  for (register lidia_size_t i = 0; i <= deg; i++)
    d[i] = coeff[i] ;
  return d;
}


template < class T >
void base_polynomial < T >::copy_data(T * d, const T * vd, lidia_size_t al)
{
  debug_handler_l(DM_BP, "in member - function "
                "copy_data(T *, const T *, lidia_size_t)", DV_BP + 1);
  for (lidia_size_t i = al +1; i; i--, d++, vd++)
    (*d) = (*vd);
}

template < class T >
void base_polynomial < T >::remove_leading_zeros()
{
  debug_handler_c(DM_BP, "in member - function remove_leading_zeros ()",
                  DV_BP + 1, cout<<"original degree is "<<deg<<endl<<flush);
  T c, *np;
  lidia_size_t d, i;

  d = deg;
  np = coeff + d;
  while (d >= 0 && np->is_zero())
    d--, np--;

  if (d < 0){
    deg = d;
    delete[] coeff;
    coeff = NULL;
  }
  else if (d != deg){
    debug_handler_c(DM_BP, "in member - function remove_leading_zeros()",
                    DV_BP + 1, cout << "new degree is "<<d<<endl<<flush);
    deg = d;
    np = new T[d + 1];
    memory_handler(np, DM_BP, "remove_leading_zeros() :: "
                 "Error in memory allocation (np)");
    for (i = 0; i <= d; i++)
      np[i] = coeff[i];
    delete[] coeff;
    coeff = np;
  }
}

template < class T >
void base_polynomial < T >::set_degree(lidia_size_t d)
{
  debug_handler_l(DM_BP, "in member - function "
                  "set_degree(lidia_size_t)",  DV_BP + 2);

  if (d < -1)
    lidia_error_handler_para(d, "d", "d >= -1",
                             "void base_polynomial < T >::"
                             "set_degree(lidia_size_t d)",
                             DM_BP, LP_ERROR[2]);

  if (d == deg)
    return;

  if (d < 0){
    delete[] coeff;     // Note: coeff != NULL, since otherwise d == deg !
    coeff = NULL;
    deg = d;
    return;
  }

  T *tmp = coeff;
  coeff = new T [d + 1];
  memory_handler(coeff, DM_BP, "set_degree(lidia_size_t d) :: "
                 "Error in memory allocation (coeff)");

  register lidia_size_t minimum = ( d < deg)? d : deg;

  for (register lidia_size_t i = 0; i <= minimum; i++)
    coeff[i] = tmp[i];

  if (tmp != NULL)
    delete[] tmp;
  deg = d;
}


/**
 ** assignment
 **/

template < class T >
void base_polynomial < T >::assign(const T & a)
{
  debug_handler_l(DM_BP,"in member - function "
                  "assign (const T &)", DV_BP + 3);
  if (a == 0){
    if (deg >= 0){
      delete[] coeff;
      coeff = NULL;
      deg = -1;
    }
    return;
  }
  if (deg > 0)
    delete[] coeff;
  if (deg != 0){
    deg = 0;
    coeff = new T[1];
    memory_handler(coeff, DM_BP, "assign(const T &) :: "
                   "Error in memory allocation (coeff)");
  }
  coeff[0] = a;
}

template < class T >
void base_polynomial < T >::assign(const base_polynomial < T > &a)
{
  debug_handler_l(DM_BP,"in member - function "
                  "assign (const base_polynomial <T> &)", DV_BP + 3);
  if (deg != a.deg){
    if (deg >= 0)
      delete[] coeff;
    deg = a.deg;
    if (deg >= 0){
      coeff = new T[deg + 1];
      memory_handler(coeff, DM_BP, "assign(const base_polynomial < T > &) :: "
                     "Error in memory allocation (coeff)");
    }
    else coeff = NULL;
  }
  for (register lidia_size_t i = 0; i <= deg; i++)
    coeff[i] = a.coeff[i];
}

template < class T >
void base_polynomial < T >::assign_zero()
{
  debug_handler_l(DM_BP, "in member - function "
                "assign_zero ()" , DV_BP + 3);
  if (deg >= 0){
    deg = -1;
    delete[] coeff;
    coeff = NULL;
  }
}

template < class T >
void base_polynomial < T >::assign_one()
{
  debug_handler_l(DM_BP, "in member - function "
                "assign_one ()" , DV_BP + 3);
  if (deg > 0)
    delete[] coeff;
  if (deg != 0){
    deg = 0;
    coeff = new T[1];
    memory_handler(coeff, DM_BP, "base_polynomial(const T &) :: "
                 "Error in memory allocation (coeff)");
  }
  coeff[0] = 1;
}

template < class T >
void base_polynomial < T >::assign_x()
{
  debug_handler_l(DM_BP, "in member - function "
                "assign_x ()" , DV_BP + 3);
  if (deg > 1 || deg == 0)
    delete[] coeff;
  if (deg != 1){
    deg = 1;
    coeff = new T[2];
    memory_handler(coeff, DM_BP, "base_polynomial(const T &) :: "
                 "Error in memory allocation (coeff)");
  }
  coeff[0] = 0;
  coeff[1] = 1;
}

/**
 ** operator overloading
 **/

template < class T >
T base_polynomial < T >::operator() (const T & value) const
{
  debug_handler_l(DM_BP,"in operator "
                  "() (const T &)", DV_BP + 5);

  if (deg == 0)
    return coeff[0];

  T result;
  result = 0;

  if (deg < 0)
    return result;

  result = coeff[deg];
  for (lidia_size_t i = deg - 1; i >= 0; i--){
    ::multiply(result, result, value);
    ::add(result, result, coeff[i]);
  }
  return result;
}


template < class T >
base_polynomial < T > &
base_polynomial < T >::operator += (const base_polynomial < T > &a)
{ add(*this, a); return *this;}

template < class T >
base_polynomial < T > &
base_polynomial < T >::operator += (const T &a)
{ add(*this, a); return *this;}

template < class T >
base_polynomial < T > &
base_polynomial < T >::operator -= (const base_polynomial < T > &a)
{ subtract(*this, a); return *this;}

template < class T >
base_polynomial < T > &
base_polynomial < T >::operator -= (const T &a)
{ subtract(*this, a); return *this;}

template < class T >
base_polynomial < T > &
base_polynomial < T >::operator *= (const base_polynomial < T > &a)
{ multiply(*this, a); return *this;}

template < class T >
base_polynomial < T > &
base_polynomial < T >::operator *= (const T &a)
{ multiply(*this, a); return *this;}


/**
 ** arithmetic procedures
 **/

template < class T >
void base_polynomial < T >::negate(const base_polynomial < T > &a)
{
  debug_handler_l(DM_BP, "in member - function "
                  "negate (const base_polynomial < T > &)",  DV_BP + 5);
  register lidia_size_t d = a.deg;
  set_degree(d);

  const T * ap = a.coeff;
  T * cp = coeff;

  for (d++; d; d--, ap++, cp++)
    ::negate(*cp, *ap);
}

template < class T >
void base_polynomial < T >::add(const base_polynomial < T > & a,
         const base_polynomial < T > & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "add (const base_polynomial < T > &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  const T *ap, *bp;
  T *cp;
  lidia_size_t deg_a = a.deg, deg_b = b.deg;
  lidia_size_t i, min_deg_ab, max_deg_ab;

  if (deg_a > deg_b){
    max_deg_ab = deg_a;
    min_deg_ab = deg_b;
  }
  else{
    max_deg_ab = deg_b;
    min_deg_ab = deg_a;
  }

  set_degree(max_deg_ab);
  if (max_deg_ab < 0) return;

  ap = a.coeff;
  bp = b.coeff;
  cp = coeff;

  for (i = min_deg_ab + 1; i; i--, ap++, bp++, cp++)
    ::add(*cp, *ap, *bp);

  if (deg_a > min_deg_ab)
    for (i = deg_a - min_deg_ab; i; i--, cp++, ap++)
      *cp = *ap;
  else if (deg_b > min_deg_ab)
    for (i = deg_b - min_deg_ab; i; i--, cp++, bp++)
      *cp = *bp;
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::add(const base_polynomial < T > & a, const T & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "add (base_polynomial < T > &, "
                  "const T &)",  DV_BP + 5);
  if (a.deg < 0){
    if (b != 0){
      set_degree(0);
      coeff[0] = b;
      return;
    }
    set_degree(-1);
    return;
  }
  set_degree(a.deg);

  const T *ap = a.coeff;
  T *cp = coeff;

  ::add(*cp ,*ap, b);
  if (a.deg > 0  &&  &a != this)
    for (register lidia_size_t i = a.deg; i; i--)
      *(++cp) = *(++ap);
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::add(const T & b, const base_polynomial < T > & a)
{
  debug_handler_l(DM_BP, "in member - function "
                  "add (const T &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  if (a.deg < 0){
    if (b != 0){
      set_degree(0);
      coeff[0] = b;
      return;
    }
    set_degree(-1);
    return;
  }
  set_degree(a.deg);

  const T *ap = a.coeff;
  T *cp = coeff;

  ::add(*cp, *ap, b);
  if (a.deg > 0  &&  &a != this)
    for (register lidia_size_t i = a.deg; i; i--)
      *(++cp) = *(++ap);
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::subtract(const base_polynomial < T > & a,
              const base_polynomial < T > & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "subtract (const base_polynomial < T > &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  const T *ap, *bp;
  T *cp;
  lidia_size_t deg_a = a.deg, deg_b = b.deg;
  lidia_size_t i, min_deg_ab, max_deg_ab;

  if (deg_a > deg_b){
    max_deg_ab = deg_a;
    min_deg_ab = deg_b;
  }
  else{
    max_deg_ab = deg_b;
    min_deg_ab = deg_a;
  }

  set_degree(max_deg_ab);
  if (max_deg_ab < 0) return;

  ap = a.coeff;
  bp = b.coeff;
  cp = coeff;
  for (i = min_deg_ab + 1; i; i--, ap++, bp++, cp++)
    ::subtract(*cp, *ap, *bp);

  if (deg_a > min_deg_ab  && this != &a)
    for (i = deg_a - min_deg_ab; i; i--, cp++, ap++)
      *cp = *ap;
  else if (deg_b > min_deg_ab)
    for (i = deg_b - min_deg_ab; i; i--, cp++, bp++)
      ::negate(*cp, *bp);
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::subtract(const base_polynomial < T > & a, const T & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "subtract (const base_polynomial < T > &, "
                  "const T &)",  DV_BP + 5);
  if (a.deg < 0){
    if (b != 0){
      set_degree(0);
      coeff[0] = - b;
      return;
    }
    set_degree(-1);
    return;
  }
  set_degree(a.deg);

  const T *ap = a.coeff;
  T *cp = coeff;

  ::subtract(*cp, *ap, b);
  if (a.deg > 0  && this != &a)
    for (register lidia_size_t i = a.deg; i; i--)
      *(++cp) = *(++ap);
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::subtract(const T & b, const base_polynomial < T > & a)
{
  debug_handler_l(DM_BP, "in member - function "
                  "subtract (const T &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  if (a.deg < 0){
    if (b != 0){
      set_degree(0);
      coeff[0] = b;
      return;
    }
    set_degree(-1);
    return;
  }
  set_degree(a.deg);

  const T *ap = a.coeff;
  T *cp = coeff;

  ::subtract(*cp, b, *ap);
  if (a.deg > 0)
    for (register lidia_size_t i = a.deg; i; i--)
      ::negate(*(++cp), *(++ap));
  else
    remove_leading_zeros();
}

template < class T >
void base_polynomial < T >::multiply(const base_polynomial < T > & a,
              const base_polynomial < T > & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "multiply (const base_polynomial < T > &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  const T *ap, *bp;
  T * cp, temp;
  lidia_size_t deg_a = a.deg, deg_b = b.deg;

  if (deg_a < 0 || deg_b < 0){
    set_degree(-1);
    return;
  }

  lidia_size_t i, j, deg_ab = deg_a + deg_b;

  if (coeff == a.coeff || coeff == b.coeff){
    base_polynomial < T > c_temp;
    c_temp.set_degree(deg_ab);

    for (i = deg_ab + 1, cp = c_temp.coeff; i; i--, cp++)
      (*cp) = 0;

    for (i = 0, ap = a.coeff; i <= deg_a; i++, ap++)
      for (j = deg_b + 1, bp = b.coeff, cp = c_temp.coeff + i;
           j; j--, bp++, cp++){
        ::multiply(temp, *ap, *bp);
        ::add(*cp, *cp, temp);
      }
    c_temp.remove_leading_zeros();
    assign(c_temp);
  }
  else {
    set_degree(deg_ab);

    for (i = deg_ab + 1, cp = coeff; i; i--, cp++)
      (*cp) = 0;

    for (i = 0, ap = a.coeff; i <= deg_a; i++, ap++)
      for (j = deg_b + 1, bp = b.coeff, cp = coeff + i;
           j; j--, bp++, cp++){
        ::multiply(temp, *ap, *bp);
        ::add(*cp, *cp, temp);
      }
    remove_leading_zeros();
  }
}

template < class T >
void base_polynomial < T >::multiply(const base_polynomial < T > & a, const T & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "multiply (const base_polynomial < T > &, "
                  "const T &)",  DV_BP + 5);
  if(b == 0){
    set_degree(-1);
    return;
  }
  const T *ap;
  T *cp;
  lidia_size_t deg_a = a.deg;

  set_degree(deg_a);

  register lidia_size_t i = 0;
  for (ap = a.coeff, cp = coeff; i <= deg_a; i++, ap++, cp++)
    ::multiply(*cp, *ap, b);
  remove_leading_zeros();               // necessary if characteristic != 0
}

template < class T >
void base_polynomial < T >::multiply(const T & b, const base_polynomial < T > & a)
{
  debug_handler_l(DM_BP, "in member - function "
                  "multiply (const T &, "
                  "const base_polynomial < T > &)",  DV_BP + 5);
  if(b == 0){
    set_degree(-1);
    return;
  }
  const T *ap;
  T *cp;
  lidia_size_t deg_a = a.deg;

  set_degree(deg_a);

  register lidia_size_t i = 0;
  for (ap = a.coeff, cp = coeff; i <= deg_a; i++, ap++, cp++)
    ::multiply(*cp, b, *ap);
  remove_leading_zeros();               // necessary if characteristic != 0
}


template < class T >
void base_polynomial < T >::power(const base_polynomial < T > & a, const bigint & b)
{
  debug_handler_l(DM_BP, "in member - function "
                  "power (const base_polynomial < T > &, "
                  "const bigint &)",  DV_BP + 5);
  bigint exponent;
  base_polynomial < T > multiplier;
  if (b.is_negative())
    assign_zero();
  else if (b.is_zero() || a.is_one())
    assign_one();
  else{
    exponent.assign(b);
    multiplier.assign(a);
    assign_one();
    while (exponent.is_gt_zero()){
      if (!exponent.is_even())
        this->multiply(*this, multiplier);
      ::multiply(multiplier, multiplier, multiplier);
      exponent.divide_by_2();
    }
  }
}

/**
 ** functions
 **/

template < class T >
void base_polynomial < T >::derivative(const base_polynomial < T > & a)
{
  debug_handler_l(DM_BP, "in member - function "
                  "derivative (const base_polynomial < T > &)", DV_BP + 5);

  lidia_size_t d = a.deg;

  if (d <= 0){
    set_degree(-1);
    return;
  }

  set_degree(d - 1);
  const T *ap = a.coeff + 1;
  T *cp = coeff;
  T temp;
  for (lidia_size_t i = 1; i <= d; i++, cp++, ap++){
    temp = i;                   // necessary, since bigcomplex does not
                                // support automatic cast !!
    ::multiply(*cp, *ap, temp);
  }
  remove_leading_zeros();               // necessary if characteristic != 0
}


/**
 ** input / output
 **/

template < class T >
void base_polynomial < T >::read(istream & s)
{
  debug_handler_l(DM_BP,"in member - function "
                  "read(istream &)", DV_BP + 6);
  char c;
  s >> ws >> c;
  s.putback(c);

  if (c != '['){
    read_verbose(s);
    return;
  }

  base_vector < T > temp;
  s >> temp;
  temp.reverse();

  set_degree(temp.size() - 1);
  if (deg >= 0)
    coeff = temp.get_data();
  remove_leading_zeros();
}

template < class T >
istream & base_polynomial < T >::read_verbose(istream & s)
{
  // This function reads a univariate polynomial in any variable.
  // input format : a_n*x^n+ ... + a_1*x + a_0
  // e.g. :   3*x^2 + 4*x - 1
  // Monomials need not be sorted, and powers of x may even appear 
  // repeated, '*' may be omitted and coefficients may follow the variable:
  //        -4 + 8x^5 + 2 - x^2 3 + x^5 + x^5*17
  // Note however, that the routine will work faster, if the leading monomial
  // is read first. 

  debug_handler_l(DM_BP,"in member-function "
                  "istream & read_verbose(istream &)", DV_BP + 6);

  register lidia_size_t n = 0;
  lidia_size_t sz;
  char c;

  set_degree(8);
  for(;n <= deg; n++)
    coeff[n] = 0;

  char variable = 0;
  T coeff_tmp;
  coeff_tmp = 1;
  T tmp;

  // Read a monomial, i.e. "x^k" or "- x^k"
  // or "a*x^k" or "a x^k" or "x^k*a" or "x^k a"

  do{
    c = s.get();
  } while (isspace(c) && c != '\n');
  while (c!='\n' && c != EOF && s.good()){
    sz = 0;             // Assume we read coeffizient of x^0;
    if (c=='+'){
      coeff_tmp = 1;
      do{
        c = s.get();
      } while (isspace(c) && c != '\n');
    }
    if (c=='-'){
      coeff_tmp = -1;
      do{
        c = s.get();
      } while (isspace(c) && c != '\n');
    }
#ifdef POLYREAD_DEBUG
    cout << "\n 1) Now looking at " << c;
#endif
    if (c>='0' && c<='9' || c == '('){
      s.putback(c);
      s >> tmp;
      coeff_tmp *= tmp;
      do{
        c = s.get();
#ifdef POLYREAD_DEBUG
        cout << ", looking at "<< c;
#endif
      } while (isspace(c) && c != '\n');
      if (c == '*')
        do{
          c = s.get();
        } while (isspace(c) && c != '\n');
#ifdef POLYREAD_DEBUG
      cout << "\n coeff_tmp is now "<<coeff_tmp;
      cout << ", looking at "<< c;
#endif
    }
#ifdef POLYREAD_DEBUG
    cout << "\n 2) Now looking at " << c;
#endif
    if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')){
      if (variable == 0)
        variable = c;
      else if (variable != c)
        lidia_error_handler_c("base_polynomial", "member function "
                              "read_verbose: The given string is not "
                              "recognized to be a univariate polynomial",
                              cout << "Variable name seemed to be "<<variable;
                              cout << " and now you used "<<c<<"."<<endl);
      do{
        c = s.get();
      } while (isspace(c) && c != '\n');
#ifdef POLYREAD_DEBUG
      cout << "\n 3) Now looking at " << c;
#endif

      if (c != '^') sz = 1;
      else {
        s >> sz;
#ifdef POLYREAD_DEBUG
        cout << "sz ist "<<sz;
#endif
        do {
          c = s.get();
#ifdef POLYREAD_DEBUG
          cout << "\n4') Looking at "<<c;
#endif
        } while (isspace(c) && c != '\n');
#ifdef POLYREAD_DEBUG
        cout << "\n 5) Now looking at " << c;
#endif
      }
      if (c=='*'){
        s >> tmp;
        coeff_tmp *= tmp;
        do{
          c = s.get();
        } while (isspace(c) && c != '\n');
      }
#ifdef POLYREAD_DEBUG
      cout << "\n 6) Now looking at " << c;
#endif

      if (c>='0' && c<='9'|| c == '('){
        s.putback(c);
        s >> tmp;
#ifdef POLYREAD_DEBUG
        cout <<"\n Old coeff_tmp: "<<coeff_tmp;
#endif
        coeff_tmp *= tmp;
#ifdef POLYREAD_DEBUG
        cout <<"\n New coeff_tmp: "<<coeff_tmp;
#endif
        do{
          c = s.get();
        } while (isspace(c) && c != '\n');
      }
    }

    if (c!='+' && c!='-') {
      // No next monomial, so assume end of input is reached
      s.putback(c);
      c = '\n';	// set c to end--marker
    }
    if (sz >= n){
      set_degree(sz);
      for(;n <= deg; n++)
        coeff[n] = 0;
    }
    coeff[sz]+=coeff_tmp;

#ifdef POLYREAD_DEBUG
    cout << "\nSuccessfully read next monomial; Poly now is "<<(*this);
    cout << "\n Now looking at " << c;
#endif
  }
  remove_leading_zeros();
  return s;
}

/* print polynom */
template < class T >
void base_polynomial < T >::write(ostream & s) const
{
  debug_handler_l(DM_BP,"in member - function "
                  "write (ostream &)", DV_BP + 6);
  lidia_size_t d = deg;

  if (d < 0)
    s << 0;
  else if (d == 0)
    s << coeff[0];
  else if (d == 1){
    if (coeff[1] == 1)
      s << "x";
    else
      s << coeff[1] << " * x";
    if (coeff[0] != 0)
      s << "+ " << coeff[0];
  }
  else {
    if (coeff[d] == 1)
      s << "x^" << d;
    else
      s << coeff[d] << " * x^" << d;
    for (register lidia_size_t i = d - 1; i > 1; i--)
      if (coeff[i] == 1)
        s << " + x^" << i;
      else if (coeff[i] != 0)
        s << " + " << coeff[i] << " * x^" << i;
    if (coeff[1] == 1)
      s << " + x";
    else if (coeff[1] != 0)
      s << " + " << coeff[1] << " * x";
    if (coeff[0] != 0)
      s << " + " << coeff[0];
  }
}

// FIELD_POLYNOMIAL FUNCTIONS

/**
 ** assignment
 **/

// template < class T >
// field_polynomial < T > & field_polynomial < T >::operator = (const base_polynomial < T > &a)
// {
//
//   if (deg != a.degree()){
//     delete[] coeff;
//     deg = a.degree();
//     coeff = new T[deg + 1];
//   }
//   for (register lidia_size_t i = 0; i <= deg; i++)
//     coeff[i] = a[i];
//   return *this;
// }


/**
 ** Division
 **/

template < class T >
void field_polynomial < T >::divide(const base_polynomial < T > & a,
            const T & b)
{
  debug_handler_l(DM_FP, "in member - function "
                  "divide (const base_polynomial < T > &, "
                  "const T &)",  DV_FP);
  const T * ap;
  T * cp;
  lidia_size_t deg_a = a.degree();

  set_degree(deg_a);
  T b_inv;
  invert(b_inv, b);

  register lidia_size_t i = deg_a + 1;
  for (ap = ((field_polynomial< T >*)(&a))->coeff, cp = coeff;
       i; i--, ap++, cp++)
    ::multiply(*cp, *ap, b_inv);
  // No remove_leading_zeros, since zero_divisors do not exist!!
}

template < class T >
void field_polynomial < T >::div_rem(field_polynomial < T > &r,
             const base_polynomial < T > &aa, const base_polynomial < T > &bb)
{
  debug_handler_l(DM_FP, "in member - function "
                  "div_rem (field_polynomial < T > &, "
                  "const base_polynomial < T > &, "
                  "const base_polynomial < T > &)",  DV_FP);

  lidia_size_t deg_a = aa.degree(), deg_b = bb.degree();
  lidia_size_t deg_ab = deg_a - deg_b;

  if (deg_b < 0)
    lidia_error_handler("field_polynomial <T>", "div_rem::division by zero");
  if (deg_ab < 0){
    r.assign(aa);
    assign_zero();
  }
  else{
    const T *ap, *bp;
    T *qp, *rp, x, y, z;
    lidia_size_t i, j;
    field_polynomial < T > a(aa), b(bb);
    set_degree(deg_ab);
    r.set_degree(deg_a);

    invert(x, b.coeff[deg_b]);

    for (i = deg_a + 1, rp = r.coeff, ap = a.coeff; i; i--, ap++, rp++)
      *rp = *ap;

    for (i = 0, qp = coeff+deg_ab; i <= deg_ab; i++, qp--){
      rp = r.coeff + deg_a - i;
      ::multiply(y, x, *rp);
      *qp = y;
      for (j = deg_b + 1, bp = b.coeff + deg_b; j; j--, rp--, bp--){
        ::multiply(z, y, *bp);
        ::subtract(*rp, *rp, z);
      }
    }
    remove_leading_zeros();
    r.remove_leading_zeros();
  }
}

template < class T >
void field_polynomial < T >::power_mod(
               const base_polynomial < T > & a, const bigint & b,
               const base_polynomial < T > & f)
{
  debug_handler_l(DM_FP, "in member - function "
                  "power_mod (const base_polynomial < T > &, const bigint &, "
                  "const base_polynomial < T > &)",  DV_FP + 1);
  bigint exponent;
  field_polynomial < T > multiplier, garbage;
  if (b.is_negative())
    assign_zero();
  else if (b.is_zero() || a.is_one())
    assign_one();
  else{
    exponent.assign(b);
    multiplier = a;
    assign_one();
    while (exponent.is_gt_zero()){
      if (!exponent.is_even()){
        this->multiply(*this, multiplier);
        ::div_rem(garbage, *this, *this, f);
      }
      ::multiply(multiplier, multiplier, multiplier);
      ::div_rem(garbage, multiplier, multiplier, f);
      exponent.divide_by_2();
    }
  }
}


/**
 ** operators
 **/


template < class T >
field_polynomial < T > &
field_polynomial < T >::operator /= (const base_polynomial < T > &a)
{field_polynomial < T > r; div_rem(r, *this, a); return *this;}

template < class T >
field_polynomial < T > &
field_polynomial < T >::operator /= (const T &a)
{ divide(*this, a); return *this;}

template < class T >
field_polynomial < T > &
field_polynomial < T >::operator %= (const base_polynomial < T > &a)
{field_polynomial < T > q; q.div_rem(*this, *this, a); return *this;}


/**
 ** functions
 **/

template < class T >
void field_polynomial < T >::integral(const base_polynomial < T > & a)
{
  debug_handler_l(DM_FP, "in member - function "
                  "integral (const base_polynomial < T > &)",  DV_FP + 1);

  lidia_size_t d = a.degree();
  if (d<0){
    set_degree(-1);
    return;
  }

  set_degree(d + 1);

  const T *ap = ((field_polynomial< T >*)(&a))->coeff;
  T *cp = coeff;
  (*cp) = 0;
  cp++;

  T temp;

  for (register lidia_size_t i = 0; i <= d; i++, cp++, ap++){
    temp = i + 1;               // necessary, since bigcomplex does not
                                // support automatic cast !!
    ::divide(*cp, *ap, temp);
  }
}


/**
 ** Gcd's
 **/

template < class T >
void field_polynomial < T >::gcd(const base_polynomial < T > &aa, const base_polynomial < T > &bb)
{
  debug_handler_l(DM_FP, "in member - function "
                  "gcd (const base_polynomial < T > &, "
                  "const base_polynomial < T > &)",  DV_FP + 2);
  T lc_inv;

  if (bb.is_zero()){
    invert(lc_inv, aa[aa.degree()]);
    this->multiply(aa, lc_inv);
  }
  debug_handler_l("field_polynomial<T>", "gcd(...)::initialize;", DV_FP - 1);
  field_polynomial < T > q, r, a(aa), b(bb);
  debug_handler_l("field_polynomial<T>", "gcd(...)::initialized;", DV_FP - 1);
  do{
    ::div_rem(q, r, a, b);
    debug_handler_c("field_polynomial<T>", "gcd(...)", DV_FP - 1,
                    cout << a <<" = "<<q<<b<<" + "<<r<<flush);
    a.assign(b);
    b.assign(r);
  } while (b.deg > 0);
  if (b.deg < 0){
    invert(lc_inv, a.coeff[a.deg]);
    this->multiply(a, lc_inv);
  }
  else{
    lc_inv = 1;
    this->assign(lc_inv);
  }
}

template < class T >
void field_polynomial < T >::xgcd(field_polynomial < T > &x,
                            field_polynomial < T > &y,
                            const base_polynomial < T > &aa,
                            const base_polynomial < T > &bb)
{
  debug_handler_l(DM_FP, "in member - function "
                  "xgcd (field_polynomial < T > &, "
                  "field_polynomial < T > &, "
                  "const base_polynomial < T >, "
                  "const base_polynomial < T > &)",  DV_FP + 2);

  field_polynomial < T >  u0, v0, u2, v2, q, r, a = aa, b = bb;

  if (b.deg < 0){
    *this = a;
    x.assign_one();
    y.assign_zero();
  }
  else{
    x.assign_one();
    y.assign_zero();
    u2.assign_zero();
    v2.assign_one();

    do{
      ::div_rem(q, r, a, b);
      a.assign(b);
      b.assign(r);
      u0.assign(u2);
      v0.assign(v2);
      ::multiply(r, q, u2);
      ::subtract(u2, x, r);
      ::multiply(r, q, v2);
      ::subtract(v2, y, r);
      x.assign(u0);
      y.assign(v0);
    } while (b.deg >= 0);
    this->assign(a);
  }

  if (deg < 0)
    return;
  T normalizer;
  invert(normalizer, lead_coeff());
  this->multiply(*this, normalizer);
  ::multiply(x, x, normalizer);
  ::multiply(y, y, normalizer);
}

// POLYNOMIAL FUNCTIONS

/**
 ** assignment
 **/

// template < class T >
// polynomial < T > & polynomial < T >::operator = (const base_polynomial < T > &a)
// {
//   debug_handler_l("polynomial","operator =(const base_polynomial <T> &)", -1);
//   if (deg != a.degree()){
//     delete[] coeff;
//     deg = a.degree();
//     coeff = new T[deg + 1];
//   }
//   for (register lidia_size_t i = 0; i <= deg; i++)
//     coeff[i] = a[i];
//   return *this;
// }
#undef DV_BP
#undef DM_BP
#undef DV_FP
#undef DM_FP
#undef DV_P
#undef DM_P
#undef LP_ERROR

