

/*****************************************************************************\

MODULE: ZZX

SUMMARY:

The class ZZX implements polynomials in ZZ[X], i.e., 
univariate polynomials with integer coefficients.

A polynomial is represented as a vector(ZZ).
If f is a ZZX, then f.rep is a vector(ZZ).
The zero polynomial is represented as a zero length vector.
Otherwise, f.rep[0] is the constant-term, and f.rep[f.rep.length()-1]
is the leading coefficient, which is always non-zero.
The member f.rep is public, so the vector representation is fully
accessible.
Use the member function normalize() to strip leading zeros.

Polynomial multiplication is implemented using one of 
4 different algorithms:

1) classical 
2) Karatsuba
3) Schoenhage & Strassen --- performs an FFT by working
     modulo a "Fermat number" of appropriate size...
     good for polynomials with huge coefficients
     and moderate degree
4) CRT/FFT --- performs an FFT by working modulo several
     small primes...good for polynomials with moderate coefficients
     and huge degree.

The choice of algorithm is somewhat heuristic, and may not always
be perfect.

Many thanks to Juergen Gerhard <jngerhar@plato.uni-paderborn.de>
for pointing out the deficiency in the NTL-1.0 ZZX arithmetic,
and for contributing the Schoenhage/Strassen code.

Here is a simple program that reads 
two polynomials, and prints their product.

#include "ZZX.h"

main()
{
   ZZX a, b, prod;
   cin >> a;
   cin >> b;
   mul(prod, a, b);
   cout << prod << "\n";
}

\*****************************************************************************/

#include "vec_ZZ.h"
#include "zz_pX.h"
#include "ZZ_pX.h"


class ZZX {
public:

   vector(ZZ) rep;
   // the represenation is public.
   // use the method normalize() to strip leading zeros.


   ZZX(); // initial value 0
   ZZX(const ZZX& a);
   void operator=(const ZZX& a); 
   ~ZZX();

   ZZX(INIT_SIZE_TYPE, long n);
   // initial value 0, but space is pre-allocated for n coefficients

   ZZX(INIT_VAL_TYPE, long a);
   ZZX(INIT_VAL_TYPE, const ZZ& a);
   // initial value is a.
   // Invoke as ZZX(INIT_VAL, a)

   void normalize();
   // strip leading zeros

   void SetMaxLength(long n); 
   // pre-allocate space for n coefficients.
   // Value is unchanged

   void kill();
   // free space held by this polynomial.  Value becomes 0.

   static const ZZX& zero();
   // read-only reference to 0

};


/*****************************************************************************\

                               Input/Output

I/O format:

   [a_0 a_1 ... a_n],

represents the polynomial a_0 + a_1*X + ... + a_n*X^n.

On output, all coefficients will be integers between 0 and p-1,
amd a_n not zero (the zero polynomial is [ ]).
Leading zeroes are stripped.

\*****************************************************************************/


istream& operator>>(istream& s, ZZX& x);
ostream& operator<<(ostream& s, const ZZX& a);


/*****************************************************************************\

                             Some utility routines

\*****************************************************************************/


long deg(const ZZX& a);
// degree of a polynomial = a.rep.length()-1.
// Note: deg(0) == -1.

const ZZ& coeff(const ZZX& a, long i);
// returns a read-only reference to a.rep[i], or zero if i not in range

void GetCoeff(ZZ& x, const ZZX& a, long i);
// x = a[i], or zero if i not in range

const ZZ& LeadCoeff(const ZZX& a);
// read-only reference to leading term of a, or zero if a == 0

const ZZ& ConstTerm(const ZZX& a);
// read-only reference to constant term of a, or zero if a == 0

void SetCoeff(ZZX& x, long i, const ZZ& a);
// makes coefficient of X^i equal to a;  error is raised if i < 0

void SetCoeff(ZZX& x, long i);
// makes coefficient of X^i equal to 1;  error is raised if i < 0

void SetX(ZZX& x); // x is set to the monomial X

long IsX(const ZZX& a); // test if x = X

void clear(ZZX& x); // x = 0

void set(ZZX& x); // x = 1

void swap(ZZX& x, ZZX& y); 
// swap x & y by swapping rep's (which swaps pointers).

void trunc(ZZX& x, const ZZX& a, long m); // x = a % X^m

void RightShift(ZZX& x, const ZZX& a, long n); // x = a/X^n

void LeftShift(ZZX& x, const ZZX& a, long n); // x = a*X^n

void diff(ZZX& x, const ZZX& a); // x = derivative of a

/*****************************************************************************\

                                Conversion 

\*****************************************************************************/



void operator<<(ZZX& x, long a); // x = a

void operator<<(ZZX& x, const ZZ& a); // x = a

void operator<<(ZZX& x, const vector(ZZ)& a); 
// set x.rep to a and normalize

void operator<<(zz_pX& x, const ZZX& a);
// set x to a reduced mod p

void operator<<(ZZX& x, const zz_pX& a);
// set x to representation of a

void operator<<(ZZ_pX& x, const ZZX& a);
// set x to a reduced mod p

void operator<<(ZZX& x, const ZZ_pX& a);
// set x to representation of a

/*****************************************************************************\

                                  Comparison

\*****************************************************************************/

long IsZero(const ZZX& a);  // test for 0

long IsOne(const ZZX& a);  // test for 1

long operator==(const ZZX& a, const ZZX& b);
long operator!=(const ZZX& a, const ZZX& b);

/*****************************************************************************\

                                   Addition

\*****************************************************************************/

void add(ZZX& x, const ZZX& a, const ZZX& b); // x = a + b
void add(ZZX & x, const ZZX& a, const ZZ& b); // x = a + b
void add(ZZX& x, const ZZX& a, long b); // x = a + b
void add(ZZX& x, const ZZ& a, const ZZX& b); // x = a + b
void add(ZZX& x, long a, const ZZX& b); // x = a + b

void sub(ZZX& x, const ZZX& a, const ZZX& b); // x = a - b
void sub(ZZX & x, const ZZX& a, const ZZ& b); // x = a - b
void sub(ZZX& x, const ZZX& a, long b); // x = a - b
void sub(ZZX& x, const ZZ& a, const ZZX& b); // x = a - b
void sub(ZZX& x, long a, const ZZX& b); // x = a - b

void negate(ZZX& x, const ZZX& a); // x = -a

/*****************************************************************************\

                               Multiplication

\*****************************************************************************/

void mul(ZZX& x, const ZZX& a, const ZZX& b); // x = a * b
void mul(ZZX & x, const ZZX& a, const ZZ& b); // x = a * b
void mul(ZZX& x, const ZZX& a, long b); // x = a * b
void mul(ZZX& x, const ZZ& a, const ZZX& b); // x = a * b
void mul(ZZX& x, long a, const ZZX& b); // x = a * b

void sqr(ZZX& x, const ZZX& a); // x = a^2


/*****************************************************************************\

                                  Division

\*****************************************************************************/


void DivRem(ZZX& q, ZZX& r, const ZZX& a, const ZZX& b);
// performs pseudo-division:  computes q and r
// with deg(r) < deg(b), and LeadCoeff(b)^(deg(a)-deg(b)+1) a = b q + r.

void div(ZZX& q, const ZZX& a, const ZZX& b);
// same as DivRem, but only computes q

void rem(ZZX& r, const ZZX& a, const ZZX& b);
// same as DivRem, but only computes r


long divide(ZZX& q, const ZZX& a, const ZZX& b);
// if b | a, sets q = a/b and returns 1; otherwise returns 0

// Note that when b does not divide a, this routine is extremely fast.
// And when b divides a, it is generally significantly faster than the
// general division routines above.


long divide(const ZZX& a, const ZZX& b);
// if b | a, returns 1; otherwise returns 0

// Note that when b does not divide a, this routine is extremely fast.
// And when b divides a, it is generally significantly faster than the
// general division routines above.


long divide(ZZX& q, const ZZX& a, const ZZ& b);
// if b | a, sets q = a/b and returns 1; otherwise returns 0

long divide(ZZX& q, const ZZX& a, long b);
// if b | a, sets q = a/b and returns 1; otherwise returns 0

long divide(const ZZX& a, const ZZ& b);
// if b | a, returns 1; otherwise returns 0

long divide(const ZZX& a, long b);
// if b | a, returns 1; otherwise returns 0

void content(ZZ& d, const ZZX& f);
// d = content of f, sign(d) == sign(LeadCoeff(f))

void PrimitivePart(ZZX& pp, const ZZX& f);
// pp = primitive part of f, LeadCoeff(pp) >= 0

void GCD(ZZX& d, const ZZX& a, const ZZX& b);
// d = gcd(a, b), LeadCoeff(d) >= 0

long MaxBits(const ZZX& f);
// returns max NumBits of coefficients of f



/*****************************************************************************\

                               Modular Arithmetic

The modulus f must be monic, and other arguments must have
smaller degree. 

\*****************************************************************************/

void MulMod(ZZX& x, const ZZX& a, const ZZX& b, const ZZX& f);
// x = a * b mod f

void SqrMod(ZZX& x, const ZZX& a, const ZZX& f);
// x = a^2 mod f

void MulByXMod(ZZX& x, const ZZX& a, const ZZX& f);
// x = a*X mod f


/*****************************************************************************\

                  traces, norms, resultants, discriminants,
                   minimal and characteristic polynomials

\*****************************************************************************/


void trace(ZZ& res, const ZZX& a, const ZZX& f);
// res = trace of (a mod f)
// f must be monic, 0 < deg(f), deg(a) < deg(f)


void resultant(ZZ& res, const ZZX& a, const ZZX& b, long deterministic=0);
// res = resultant of a and b
// if !deterministic, then it may use a randomized strategy
//    that errs with probability no more than 2^{-80}.

void norm(ZZ& res, const ZZX& a, const ZZX& f, long deterministic=0);
// res = norm of (a mod f)
// f must be monic, 0 < deg(f), deg(a) < deg(f)
// if !deterministic, then it may use a randomized strategy
//    that errs with probability no more than 2^{-80}.


void discriminant(ZZ& d, const ZZX& a, long deterministic=0);
// d = discriminant of a
//   = (-1)^{m(m-1)/2} resultant(a, a')/lc(a),
//     where m = deg(a)
// if !deterministic, then it may use a randomized strategy
//    that errs with probability no more than 2^{-80}.


void CharPoly(ZZX& g, const ZZX& a, const ZZX& f, long deterministic=0);
// g = char poly of (a mod f)
// f must be monic
// if !deterministic, then it may use a randomized strategy
//    that errs with probability no more than 2^{-80}.


void MinPoly(ZZX& g, const ZZX& a, const ZZX& f);
// g = min poly of (a mod f)
// f must be monic, 0 < deg(f), deg(a) < deg(f)
// may use a probabilistic strategy that errs with
//   probability no more than 2^{-80}

void XGCD(ZZ& r, ZZX& s, ZZX& t, const ZZX& a, const ZZX& b, 
          long deterministic=0);
// r = resultant of a and b;
// if r != 0, then computes s and t such that:
//   a*s + b*t = r;
// otherwise s and t not affected.
// if !deterministic, then resultant computation may use a randomized strategy
//    that errs with probability no more than 2^{-80}.


/*****************************************************************************\

      Incremental Chinese Remaindering

\*****************************************************************************/

long CRT(ZZX& a, ZZ& prod, const zz_pX& A);
long CRT(ZZX& a, ZZ& prod, const ZZ_pX& A);
// If p is the current modulus with (p, prod) = 1;
// Computes b such that b = a mod prod and b = A mod p,
//    with coefficients in the interval (-p*prod/2, p*prod/2];
// Sets a = b, prod = p*prod, and returns 1 if a's value changed.




/*****************************************************************************\

                                vectors of ZZX's

\*****************************************************************************/

vector_decl(ZZX)
// vector(ZZX)

vector_eq_decl(ZZX)
// == and !=

vector_io_decl(ZZX)
// I/O operators

