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

MODULE: ZZ_pX

SUMMARY:

The class ZZ_pX implements polynomial arithmetic modulo p.
A polynomial is represented as a vector(ZZ_p).
If f is a ZZ_pX, then f.rep is a vector(ZZ_p).
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 arithmetic is implemented using the FFT,
along with the Chinese Remainder Theorem.
A more detailed description of the techniques used
here can be found in [Shoup, J. Symbolic Comp. 20:363-397, 1995].
There are many routines available to perform convolutions
and related operations that are not described here
(see "ZZ_pX.h" for complete details).

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

#include "ZZ_pX.h"

main()
{
   ZZ p;

   cin >> p;
   ZZ_pInit(p);

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

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

#include "ZZ_p.h"
#include "vec_ZZ_p.h"

class ZZ_pX {
public:

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

   ZZ_pX(); // initial value 0
   ZZ_pX(const ZZ_pX& a);
   void operator=(const ZZ_pX& a);
   ~ZZ_pX();
   
   ZZ_pX(INIT_SIZE_TYPE, long n);
   // initial value is zero, but space is pre-allocated for n coefficients
   // Invoke as ZZ_pX(INIT_SIZE, n)
   
   ZZ_pX(INIT_VAL_TYPE, long a);
   // initial value is the constant (a mod p).  Invoke as ZZ_pX(INIT_VAL, a)
   
   ZZ_pX(INIT_VAL_TYPE, const ZZ& a);
   // initial value is the constant (a mod p).  Invoke as ZZ_pX(INIT_VAL, a)
   
   ZZ_pX(INIT_VAL_TYPE, const ZZ_p& a);
   // initial value is the constant a.  Invoke as ZZ_pX(INIT_VAL, a)
   
   void normalize();
   // strip leading zeros
   
   void SetMaxLength(long n);
   // pre-allocate space for n coefficients.  Value is unchanged

   void kill();
   // set to 0 and release memory

   static const ZZ_pX& zero();
   // read-only 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 [ ]).
On input, the coefficients are arbitrary integers which are
reduced modulo p, and leading zeros stripped.

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

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

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

                              Some utility routines

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


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

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

void GetCoeff(ZZ_p& x, const ZZ_pX& a, long i);
// x = a.rep[i], or zero if i not in range

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

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

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

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

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

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

void clear(ZZ_pX& x) // x = 0

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

void swap(ZZ_pX& x, ZZ_pX& y) // swap x and y

void random(ZZ_pX& x, long n);
// generate a random polynomial of degree < n 

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

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

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

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

void MakeMonic(ZZ_pX& x); 
// if x != 0 makes x into its monic associate,
// (LeadCoeff(x) must be invertible in this case)

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

                             Conversion routines

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

void operator<<(ZZ_pX& x, long a); // x = (a mod p)

void operator<<(ZZ_pX& x, const ZZ& a); // x = (a mod p)

void operator<<(ZZ_pX& x, const ZZ_p& a); // x = a

void operator<<(ZZ_pX& x, const vector(ZZ_p)& a); 
// copy a to x.rep and normalize.

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

                                  Comparison

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

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

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

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

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

                                   Addition

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

void add(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b); // x = a + b
void add(ZZ_pX & x, const ZZ_pX& a, const ZZ_p& b); // x = a + b
void add(ZZ_pX & x, const ZZ_pX& a, long b); // x = a + b
void add(ZZ_pX& x, const ZZ_p& a, const ZZ_pX& b); // x = a + b
void add(ZZ_pX& x, long a, const ZZ_pX& b); // x = a + b

void sub(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b); // x = a - b
void sub(ZZ_pX & x, const ZZ_pX& a, const ZZ_p& b); // x = a - b
void sub(ZZ_pX& x, const ZZ_pX& a, long b); // x = a - b
void sub(ZZ_pX& x, const ZZ_p& a, const ZZ_pX& b); // x = a - b
void sub(ZZ_pX& x, long a, const ZZ_pX& b); // x = a - b

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

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

                               Multiplication

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

void mul(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b); // x = a * b
void mul(ZZ_pX & x, const ZZ_pX& a, const ZZ_p& b); // x = a * b
void mul(ZZ_pX & x, const ZZ_pX& a, long b); // x = a * b
void mul(ZZ_pX& x, const ZZ_p& a, const ZZ_pX& b); // x = a * b
void mul(ZZ_pX& x, long a, const ZZ_pX& b); // x = a * b

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

void MulTrunc(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b, long n);
// x = a * b % X^n

void SqrTrunc(ZZ_pX& x, const ZZ_pX& a, long n);
// x = a^2 % X^n


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

                                  Division

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

void DivRem(ZZ_pX& q, ZZ_pX& r, const ZZ_pX& a, const ZZ_pX& b);
// q = a/b, r = a%b

void div(ZZ_pX& q, const ZZ_pX& a, const ZZ_pX& b);
// q = a/b

void rem(ZZ_pX& r, const ZZ_pX& a, const ZZ_pX& b);
// r = a%b

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

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

void inv(ZZ_pX& x, const ZZ_pX& a, long m);
// computes x = a^{-1} % X^m.
// constant term must be invertible

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

                                   GCD's

These routines are intended for use when p is prime.

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

void GCD(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b);
// x = GCD(a, b),  x is always monic (or zero if a==b==0).

void XGCD(ZZ_pX& d, ZZ_pX& s, ZZ_pX& t, const ZZ_pX& a, const ZZ_pX& b);
// d = gcd(a,b), a s + b t = d 

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

                    Polynomial Evaluation and related problems

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


void BuildFromRoots(ZZ_pX& x, const vector(ZZ_p)& a);
// computes the polynomial (X-a[0]) ... (X-a[n-1]), where n = a.length()

void eval(ZZ_p& b, const ZZ_pX& f, const ZZ_p& a);
// b = f(a)

void eval(vector(ZZ_p)& b, const ZZ_pX& f, const vector(ZZ_p)& a);
//  b.SetLength(a.length())
//  b[i] = f(a[i]) for 0 <= i < a.length()

void interpolate(ZZ_pX& f, const vector(ZZ_p)& a, const vector(ZZ_p)& b);
// interpolates the polynomial f satisfying f(a[i]) = b[i]
// p should be prime.

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

                             vectors of ZZ_pX's

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

vector_decl(ZZ_pX)
// vector(ZZ_pX)

vector_eq_decl(ZZ_pX)
// == and !=

vector_io_decl(ZZ_pX)
// I/O operators

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

                Modular Arithmetic (without pre-conditioning)

Arithmetic mod f.
All inputs and outputs are polynomials of degree less than deg(f).
ASSUMPTION: deg(f) > 0.
ALIAS RESTRICTIONS: f can not alias an output.
NOTE: if you want to do many computations with a fixed f,
      use the ZZ_pXModulus data structure and associated routines below.

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

void MulMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b, const ZZ_pX& f);
// x = (a * b) % f

void SqrMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& f);
// x = a^2 % f

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

void InvMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& f);
// x = a^{-1} % f, error is a is not invertible

long InvModStatus(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& f);
// if (a, f) = 1, returns 0 and sets x = a^{-1} % f
// otherwise, returns 1 and sets x = (a, f)

void PowerMod(ZZ_pX& x, const ZZ_pX& a, const ZZ& e, const ZZ_pX& f);
// x = a^e % f, e >= 0

void PowerXMod(ZZ_pX& x, const ZZ& e, const ZZ_pX& f);
//x = X^e mod f, e >= 0

void PowerXPlusAMod(ZZ_pX& x, const ZZ_p& a, const ZZ& e, const ZZ_pX& f);
// x = (X + a)^e mod f, e >= 0

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

                     Modular Arithmetic with Pre-Conditioning

If you need to do a lot of arithmetic modulo a fixed f,
that speeds up the computation a great deal.
build ZZ_pXModulus F for f.  This pre-computes information about f
that speeds up subsequent computations.

As an example, the following routine the product modulo f
of a vector of polynomials.

#include "ZZ_pX.h"

void product(ZZ_pX& x, const vector(ZZ_pX)& v, const ZZ_pX& f)
{
   ZZ_pXModulus F;
   build(F, f); 
   ZZ_pX res;
   set(res);
   long i;
   for (i = 0; i < v.length(); i++)
      MulMod(res, res, v[i], F); 
   x = res;
}

Note that conversion operators are provided between ZZ_pXModulus
and ZZ_pX so that one may appear wherever the other is allowed.
While the conversion to ZZ_pX is trivial, please note
that conversion to ZZ_pXModulus is nontrivial.
   
\*****************************************************************************/

class ZZ_pXModulus {
public:
   ZZ_pXModulus(); // initially in an unusable state
   ~ZZ_pXModulus();

   ZZ_pXModulus(const ZZ_pX& f); // initialize with f
   operator const ZZ_pX& () const; // read-only access to f

private:
   ZZ_pXModulus(const ZZ_pXModulus&);    // disabled
   void operator=(const ZZ_pXModulus&);  // disabled
};

const ZZ_pX& rep(const ZZ_pXModulus& F);
// explicit read-only access to f

void build(ZZ_pXModulus& F, const ZZ_pX& f);
// pre-computes information about f and stores it in F
// deg(f) > 0.

// In the following, f refers to the polynomial f supplied
// to the build routine, and n = deg(f).

void MulMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pX& b, const ZZ_pXModulus& F);
// x = (a * b) % f
// deg(a), deg(b) < n

void SqrMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pXModulus& F);
// x = a^2 % f
// deg(a) < n

void PowerMod(ZZ_pX& x, const ZZ_pX& a, const ZZ& e, const ZZ_pXModulus& F);
// x = a^e % f
// e >= 0, deg(a) < n

void PowerXMod(ZZ_pX& x, const ZZ& e, const ZZ_pXModulus& F);
// x = X^e % f
// e >= 0

void PowerXPlusAMod(ZZ_pX& x, const ZZ_p& a, const ZZ& e, 
                    const ZZ_pXModulus& F);
// x = (X + a)^e % f
// e >= 0

void rem(ZZ_pX& x, const ZZ_pX& a, const ZZ_pXModulus& F);
// x = a % f

void DivRem(ZZ_pX& q, ZZ_pX& r, const ZZ_pX& a, const ZZ_pXModulus& F);
// q = a/f, r = a%f

void div(ZZ_pX& q, const ZZ_pX& a, const ZZ_pXModulus& F);
// q = a/f

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


                                More Pre-Conditioning

If you need to compute a * b % f for a fixed b, but for many a's, it is
much more efficient to first build a ZZ_pXMultiplier B for b,
and then use the MulMod routine below.

Here is an example that multiplies each element of a vector
by a fixed polynomial modulo f.

#include "ZZ_pX.h"

void mul(vector(ZZ_pX)& v, const ZZ_pX& b, const ZZ_pX& f)
{
   ZZ_pXModulus F;
   build(F, f);
   ZZ_pXMultiplier B;
   build(B, b, F);
   long i;
   for (i = 0; i < v.length(); i++)
      MulMod(v[i], v[i], B, F);
}

Note that a conversion operator from ZZ_pXMultiplier to ZZ_pX
are provided, so that a ZZ_pXMultiplier can appear 
wherever a ZZ_pX is allowed.

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


class ZZ_pXMultiplier {
public:
   ZZ_pXMultiplier(); // initially in an unusable state
   ~ZZ_pXMultiplier();

   operator const ZZ_pX& () const; // read-only access to b


private:
   ZZ_pXMultiplier(const ZZ_pXMultiplier&); // disabled
   void operator=(const ZZ_pXMultiplier&); // disabled
};

const ZZ_pX& rep(const ZZ_pXMultiplier& B);
// explicit read-only access to b

void build(ZZ_pXMultiplier& B, const ZZ_pX& b, const ZZ_pXModulus& F);
// pre-computes information about b and stores it in B
// deg(b) < n

void MulMod(ZZ_pX& x, const ZZ_pX& a, const ZZ_pXMultiplier& B,
                                      const ZZ_pXModulus& F);

// x = (a * b) % f
// deg(a) < n

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

                              Modular Composition

Modular composition is the problem of computing g(h) mod f
for polynomials f, g, and h.

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

void compose(ZZ_pX& x, const ZZ_pX& g, const ZZ_pX& h, const ZZ_pXModulus& F);
// x = g(h) mod f
// deg(h) < n

void compose2(ZZ_pX& x1, ZZ_pX& x2, const ZZ_pX& g1, const ZZ_pX& g2,
              const ZZ_pX& h, const ZZ_pXModulus& F);
// xi = gi(h) mod f (i=1,2)
// deg(h) < n
// ALIAS RESTRICTION:  xi may not alias gj, for i != j

void compose3(ZZ_pX& x1, ZZ_pX& x2, ZZ_pX& x3, 
              const ZZ_pX& g1, const ZZ_pX& g2, const ZZ_pX& g3,
              const ZZ_pX& h, const ZZ_pXModulus& F);
// xi = gi(h) mod f (i=1..3)
// deg(h) < n
// ALIAS RESTRICTION:  xi may not alias gj, for i != j


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

                     Composition with Pre-Conditioning

If a single h is going to be used with many g's
then you should build a ZZ_pXArgument for h,
and then use the compose routine below.
The routine build computes and stores h, h^2, ..., h^m mod f.
After this pre-computation, composing a polynomial of degree 
roughly n with h takes n/m multiplies mod f, plus n^2 scalar multiplies.
Thus, increasing m increases the space requirement and the pre-computation
time, but reduces the composition time.

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


struct ZZ_pXArgument {
   vector(ZZ_pX) H;
};

void build(ZZ_pXArgument& H, const ZZ_pX& h, const ZZ_pXModulus& F, long m);
// Pre-Computes information about h.
// m > 0, deg(h) < n

void compose(ZZ_pX& x, const ZZ_pX& g, const ZZ_pXArgument& H, 
             const ZZ_pXModulus& F);

void UpdateMap(vector(ZZ_p)& x, const vector(ZZ_p)& a,
               const ZZ_pXMultiplier& B, const ZZ_pXModulus& F);

// computes (a, b), (a, (b*X)%f), ..., (a, (b*X^{n-1})%f),
// where ( , ) denotes the vector inner product.
//
// This is really a "transposed" MulMod by B.

void UpdateMap(vector(ZZ_p)& x, const vector(ZZ_p)& a,
                    const ZZ_pX& b, const ZZ_pX& f);
// same as above, but uses only classical arithmetic


void ProjectPowers(vector(ZZ_p)& x, const vector(ZZ_p)& a, long k,
                   const ZZ_pX& h, const ZZ_pXModulus& F);

// computes (a, 1), (a, h), ..., (a, h^{k-1} % f)
// This operation is really the "transpose" of the
// modular composition operation.

void ProjectPowers(vector(ZZ_p)& x, const vector(ZZ_p)& a, long k,
                   const ZZ_pXArgument& H, const ZZ_pXModulus& F);

// same as above, but uses a pre-computed ZZ_pXArgument


extern long ZZ_pXArgBound;

// Initially 0.
// If this is set to a value greater than zero,
// then composition routines will allocate a table of no
// than about ZZ_pXArgBound KB.
// Setting this value affects all compose routines and the
// ProjectPowers routine, and indirectly affects many
// routines in ZZ_pXFactoring.

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

                              Minimum Polynomials

These routines should be used with prime p.

All of these routines implement the algorithm from
[Shoup, J. Symbolic Comp. 17:371-391, 1994] and
[Shoup, J. Symbolic Comp. 20:363-397, 1995], based on 
transposed modular composition and the Berlekamp/Massey algorithm.

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


void MinPoly(ZZ_pX& h, const vector(ZZ_p)& a, long m);
// computes the minimum polynomial of a linealy generated sequence;
// m is a bound on the degree of the polynomial;
// required: a.length() >= 2*m

void ProbMinPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F, long m);
void ProbMinPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F);

// computes the monic minimal polynomial if (g mod f).
// m = a bound on the degree of the minimal polynomial;
// in the second version, this argument defaults to n. 
// The algorithm is probabilistic, always returns a divisor of
// the minimal polynomial, and returns a proper divisor with
// probability at most m/p.

void MinPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F, long m);
void MinPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F);
// same as above, but guarantees that result is correct

void IrredPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F, long m);
void IrredPoly(ZZ_pX& h, const ZZ_pX& g, const ZZ_pXModulus& F);
// same as above, but assumes that f is irreducible, 
// or at least that the minimal poly of g is itself irreducible.
// The algorithm is deterministic (and is always correct).


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

                   Traces, norms, resultants

These routines should be used with prime p.

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


void trace(ZZ_p& x, const ZZ_pX& a, const ZZ_pXModulus& F);
// x = Trace(a mod f)
// deg(a) < deg(f)

void TraceVec(vector(ZZ_p)& S, const ZZ_pX& f);
// S[i] = Trace(X^i mod f), i = 0..deg(f)-1;
// 0 < deg(f)

// The above two routines implement the asymptotically
// fast trace algorithm from [von zur Gathen and Shoup,
// Computational Complexity, 1992].

void norm(ZZ_p& x, const ZZ_pX& a, const ZZ_pX& f);
// x = Norm(a mod f)
// 0 < deg(f), deg(a) < deg(f)

void resultant(ZZ_p& x, const ZZ_pX& a, const ZZ_pX& b);
// x = resultant(a, b)

void CharPoly(ZZ_pX& g, const ZZ_pX& a, const ZZ_pX& f);
// g = charcteristic polynomial of (a mod f);
// 0 < deg(f), deg(g) < deg(f)
