//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : Fp_polynomial.h
// Author      : Victor Shoup, Thomas Pfahler (TPf)
// Last change : TPf, Feb 29, 1996, initial version
//             


#ifndef LIDIA_FP_POLYNOMIAL__H
#define LIDIA_FP_POLYNOMIAL__H


#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LIDIA:comparator.h>
#include <LiDIA:base_vector.h>
#include <LiDIA:bigint.h>
#else
#include <LiDIA/comparator.h>
#include <LiDIA/base_vector.h>
#include <LiDIA/bigint.h>
#endif



#ifndef HEADBANGER


inline void Remainder(bigint& x, const bigint& a, const bigint& p)
{
    remainder(x, a, p);
    if (x.is_lt_zero())	add(x, x, p);
}

inline void AddMod(bigint& c, const bigint& a, const bigint& b, const bigint& p)
{
    add(c, a, b);
    Remainder(c, c, p);
//instead of:    if (c >= p) subtract(c, c, p);
}

inline void SubMod(bigint& c, const bigint& a, const bigint& b, const bigint& p)
{
    subtract(c, a, b);
    Remainder(c, c, p);
//instead of:    if (c.is_lt_zero()) add(c, c, p);
}

inline void MulMod(bigint& c, const bigint& a, const bigint& b, const bigint& p)
{
    multiply(c, a, b);
    Remainder(c, c, p);
}

//inline void SqrMod(bigint& c, const bigint& a, const bigint& p)
//{
//    square(c, a);
//    Remainder(c, c, p);
//}

inline void NegateMod(bigint& x, const bigint& a, const bigint& p)
{
    negate(x, a);
    Remainder(x, x, p);
//instead of:    subtract(x, p, a);
}

inline void InvMod(bigint& x, const bigint& a, const bigint& p)
{
// x = a^{-1} mod n, 0 <= x < p
// error is raised if inverse not defined
    if (xgcd_left(x, a, p).is_one())
    {
	Remainder(x, x, p);
        return;
    }
    else
        lidia_error_handler( "Fp_polynomial", "InvMod(...)::inverse does not exist" );
}

/* NOW INLINE (see above):
void Remainder(bigint& x, const bigint& a, const bigint& p);
void AddMod(bigint& c, const bigint& a, const bigint& b, const bigint& p);
void SubMod(bigint& c, const bigint& a, const bigint& b, const bigint& p);
void MulMod(bigint& c, const bigint& a, const bigint& b, const bigint& p);
void NegateMod(bigint& x, const bigint& a, const bigint& p);
void InvMod(bigint& x, const bigint& a, const bigint& p);
*/





/************************************************************

					class mcp

handles different moduli and their reference_counters,
used in class Fp_polynomial (see also file mcp.c)
this implementation uses a very simple list -> this should
be improved
************************************************************/

class mcp
{
    struct item
    {
	bigint M;
	int reference_counter;
	item* next;
    };

    static bigint ZERO;
    static item* head;					//list of items
    static void delete_from_list(item* p);
    static item* insert_into_list(const bigint& B);


    item* ptr;

    void del(item* p);
    void assign(const mcp& a);
    
public:
    mcp();
    mcp(const mcp& a);
    ~mcp();

    void set(const bigint& B);

    mcp& operator = (const mcp& a)
    { assign(a); return *this; }
    
    inline void del()
    { del(ptr); ptr = 0; }

    const bigint& mod() const;

    friend void swap(mcp& x, mcp& y);
    
    friend inline bool operator == (const mcp &a, const mcp &b);
    friend inline bool operator != (const mcp &a, const mcp &b);


    static void statistics();
};

// -------------- INLINE -----------------

bool operator == (const mcp &a, const mcp &b)
{
      return (a.ptr == b.ptr);
}

bool operator != (const mcp &a, const mcp &b)
{
      return (a.ptr != b.ptr);
}


#endif 		//HEADBANGER




/************************************************************
	    class Fp_pol_ref
    tries to differentiate between the use of
    Fp_polynomial::operator[] as an l-value and
    as an r-value
************************************************************/
class Fp_pol_ref
{
  friend class Fp_polynomial; // MM

 private:
    class Fp_polynomial	&p;
    lidia_size_t	 ix;

    Fp_pol_ref();
    Fp_pol_ref(const Fp_pol_ref&);
    
 public:
    Fp_pol_ref(Fp_polynomial &f, lidia_size_t i) :
	p(f), ix(i)
    { }

//l-values
    Fp_pol_ref &operator= (const bigint &a);
    void assign_zero();
    void assign_one();
    void assign(const bigint &a);

//r-value
    operator bigint();
};



#ifndef HEADBANGER

/************************************************************
		class crossover_class
    class for computing crossover-points with respect to
    bitlengths of used moduli
************************************************************/
#define CROV_NUM_VALUES 10
class crossover_class
{
    int x[CROV_NUM_VALUES];
    int     y_fftmul[CROV_NUM_VALUES],  y_fftdiv[CROV_NUM_VALUES],
	       y_inv[CROV_NUM_VALUES];
    double y2_fftmul[CROV_NUM_VALUES], y2_fftdiv[CROV_NUM_VALUES],
	      y2_inv[CROV_NUM_VALUES];
    int halfgcd, gcd, log2_newton;

    crossover_class(const crossover_class &);	//disable

 public :
    crossover_class();
    void init(const int x_val[CROV_NUM_VALUES],
	const int fftmul_val[CROV_NUM_VALUES],
	const int fftdiv_val[CROV_NUM_VALUES],
	const int inv_val[CROV_NUM_VALUES]);
	
    int fftmul_crossover(const bigint &modulus) const;
    int fftdiv_crossover(const bigint &modulus) const;
    int inv_crossover(const bigint &modulus) const;
    int halfgcd_crossover(const bigint &modulus) const;
    int gcd_crossover(const bigint &modulus) const;
    int log2_newton_crossover(const bigint &modulus) const;
};

#endif 		//HEADBANGER



/************************************************************

			 Fp_polynomial

The class Fp_polynomial implements polynomial arithmetic modulo p.
Polynomials are represented as vectors of bigint with values in the
range 0..p-1.

If f is a Fp_polynomial, then f.coeff is a bigint*.
The zero polynomial is represented as a zero length vector.
Otherwise, f.coeff[0] is the constant-term, and f.coeff[f.c_length-1]
is the leading coefficient, which is always non-zero.
Use the member function remove_leading_zeros() to strip leading zeros.

**************************************************************/


class fft_table;

class Fp_polynomial
{

private:
    static bigint ZERO;			//coeff. value for zero polynomial
    static fft_table *fft_table_list;	//list of different fft_tables
					//        sorted by max_degree
    static crossover_class crossovers;	//crossover-points for fft-arithm.
					//       depending on architecture

    bigint *coeff;			//coefficient vector
    lidia_size_t c_length, c_alloc;
    mcp MOD;				//pointer to modulus


    inline void set_degree(lidia_size_t n)
    {
	debug_handler_l( "Fp_polynomial", "set_degree ( lidia_size_t )", 2 );
	set_max_degree(n);
	c_length = n+1;
    }

    void read_x_term(istream &datei, bigint &coeff, lidia_size_t &exp);



    
public:

/***************************************************************

	  Constructors, Destructors, and Basic Functions

****************************************************************/

Fp_polynomial() :
    coeff(0),
    c_length(0),
    c_alloc(0)
{
    debug_handler_l( "Fp_polynomial", "Fp_polynomial()", 1 );
}

Fp_polynomial(const Fp_polynomial& a);

~Fp_polynomial();

void kill();

void set_modulus(const bigint &p);
    //sets to zero polynomial, calls kill()

inline const bigint & modulus() const
{
    return MOD.mod();
}

inline lidia_size_t degree() const
// note that the zero polynomial has degree -1.
{
    debug_handler_l( "Fp_polynomial", "degree( void )", 2 );
    return c_length - 1;
}

#ifndef HEADBANGER
void comp_modulus(const Fp_polynomial& x, const char* fctname) const;
void set_max_degree(lidia_size_t n);
#endif 		//HEADBANGER

/***************************************************************

		routines for manipulating coefficients

****************************************************************/

#ifndef HEADBANGER
void get_coefficient(bigint& a, lidia_size_t i) const;
void set_coefficient(const bigint& a, lidia_size_t i);
void set_coefficient(lidia_size_t i);
#endif 		//HEADBANGER

const bigint& lead_coeff() const; 
const bigint& const_term() const;

void remove_leading_zeros();
void make_monic();

friend Fp_pol_ref;
Fp_pol_ref operator[] (lidia_size_t i)
{ return Fp_pol_ref(*this, i); }
    //operator[] with context sensitivity
    //tries to differentiate between use as l-value and r-value
    
const bigint & operator[] (lidia_size_t i) const;
    //always r-value


/***************************************************************

			 assignments

****************************************************************/

Fp_polynomial& operator=(const Fp_polynomial& a);
const bigint& operator=(const bigint& c);

#ifndef HEADBANGER
void assign(const bigint&  a);
//void assign(const bigmod& a);
//void assign(const base_vector<bigmod>& a);
void assign(const base_vector<bigint>& a, const bigint &p);
void assign(const Fp_polynomial& a);
#endif		//HEADBANGER

inline void assign_zero()
{
    debug_handler_l ( "Fp_polynomial", "assign_zero ( void )", 2 );
    if (modulus().is_zero())
	lidia_error_handler( "Fp_polynomial", "assign_zero( void )::modulus = 0" );

    c_length = 0;
}

inline void assign_one()
{
    debug_handler_l ( "Fp_polynomial", "assign_one ( void )", 2 );
    if (modulus().is_zero())
	lidia_error_handler( "Fp_polynomial", "assign_one( void )::modulus = 0" );

    set_degree(0);
    coeff[0].assign_one();
}

void assign_x();

void randomize(lidia_size_t n);
//random polynomial of degree n
	

/***************************************************************

			   comparisons

****************************************************************/

friend bool operator==(const Fp_polynomial& a, const Fp_polynomial& b);
friend bool operator!=(const Fp_polynomial& a, const Fp_polynomial& b);

bool is_zero() const;
bool is_one() const;
bool is_x() const;

inline bool is_monic() const
{    return ( lead_coeff().is_one() );	}

bool is_binomial() const;


/***************************************************************

			operators

***************************************************************/

friend Fp_polynomial operator - (const Fp_polynomial &a);
friend Fp_polynomial operator + (const Fp_polynomial &a, const Fp_polynomial &b);
friend Fp_polynomial operator + (const bigint &a, const Fp_polynomial &b);
friend Fp_polynomial operator + (const Fp_polynomial &a, const bigint &b);
friend Fp_polynomial operator - (const Fp_polynomial &a, const Fp_polynomial &b);
friend Fp_polynomial operator - (const bigint &a, const Fp_polynomial &b);
friend Fp_polynomial operator - (const Fp_polynomial &a, const bigint &b);
friend Fp_polynomial operator * (const Fp_polynomial &a, const Fp_polynomial &b);
friend Fp_polynomial operator * (const bigint &a, const Fp_polynomial &b);
friend Fp_polynomial operator * (const Fp_polynomial &a, const bigint &b);
friend Fp_polynomial operator / (const Fp_polynomial &a, const Fp_polynomial &b);
friend Fp_polynomial operator / (const bigint &a, const Fp_polynomial &b);
friend Fp_polynomial operator / (const Fp_polynomial &a, const bigint &b);
friend Fp_polynomial operator % (const Fp_polynomial &a, const Fp_polynomial &b);

Fp_polynomial & operator += (const Fp_polynomial &a);
Fp_polynomial & operator += (const bigint &a);
Fp_polynomial & operator -= (const Fp_polynomial &a);
Fp_polynomial & operator -= (const bigint &a);
Fp_polynomial & operator *= (const Fp_polynomial &a);
Fp_polynomial & operator *= (const bigint &a);
Fp_polynomial & operator /= (const Fp_polynomial &a);
Fp_polynomial & operator /= (const bigint &a);
Fp_polynomial & operator %= (const Fp_polynomial &a);

bigint operator () (const bigint &value) const;


/***************************************************************

		 procedural versions for arithmetics

****************************************************************/

void build_from_roots(const base_vector<bigint>& a);
// computes the polynomial (x-a[0]) ... (x-a[n-1]), where n = a.length()
// implementation: see file "fftbuild.c"


/******** addition ********/
#ifndef HEADBANGER
void negate();
friend void negate(Fp_polynomial& x, const Fp_polynomial& a);
friend void add(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
friend void subtract(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);

friend void add(Fp_polynomial & x, const Fp_polynomial& a, const bigint& b);
friend void add(Fp_polynomial& x, const bigint& a, const Fp_polynomial& b)
    { add(x, b, a); }
friend void subtract(Fp_polynomial & x, const Fp_polynomial& a, const bigint& b);
friend void subtract(Fp_polynomial& x, const bigint& a, const Fp_polynomial& b)
    { ::negate(x, b); add(x, x, a); }
#endif

/******** multiplication ********/
#ifndef HEADBANGER
friend void multiply(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
	// x = a * b
friend void multiply(Fp_polynomial & x, const Fp_polynomial& a, const bigint& b)
    {   multiply_by_scalar(x, a, b);    }
friend void multiply(Fp_polynomial & x, const bigint& a, const Fp_polynomial& b)
    {   multiply_by_scalar(x, b, a);    }
friend void multiply_by_scalar(Fp_polynomial &c, const Fp_polynomial &a, const bigint &b);

friend void plain_mul(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
	// always uses the "classical" algorithm
friend void plain_sqr(Fp_polynomial& x, const Fp_polynomial& a);
	// always uses the "classical" algorithm

friend void fft_mul(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
	// always uses the FFT
friend void fft_sqr(Fp_polynomial& x, const Fp_polynomial& a);
	// always uses the FFT
#endif 		//HEADBANGER

friend void square(Fp_polynomial& x, const Fp_polynomial& a);
	// x = a^2

/******** division routines ********/
#ifndef HEADBANGER
friend void div_rem(Fp_polynomial& q, Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);
// q = a/b, r = a%b

friend void divide(Fp_polynomial& q, const Fp_polynomial& a, const Fp_polynomial& b);
friend void divide(Fp_polynomial& q, const bigint& a, const Fp_polynomial& b)
    { Fp_polynomial tmp; tmp = a; divide(q, tmp, b); }
friend void divide(Fp_polynomial& q, const Fp_polynomial& a, const bigint& b);
// q = a/b

friend void remainder(Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);
// r = a%b
#endif 		//HEADBANGER

friend void invert(Fp_polynomial& c, const Fp_polynomial& a, lidia_size_t m);
// computes c = a^{-1} % x^m
// constant term must be non-zero

#ifndef HEADBANGER
// These always use "classical" arithmetic
friend void plain_div_rem(Fp_polynomial& q, Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);
friend void plain_div(Fp_polynomial& q, const Fp_polynomial& a, const Fp_polynomial& b);
friend void plain_rem(Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);

// These always use FFT arithmetic
friend void fft_div_rem(Fp_polynomial& q, Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);
friend void fft_div(Fp_polynomial& q, const Fp_polynomial& a, const Fp_polynomial& b);
friend void fft_rem(Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b);

friend void plain_inv(Fp_polynomial& x, const Fp_polynomial& a, lidia_size_t m);
// always uses "classical" algorithm
// ALIAS RESTRICTION: input may not alias output

friend void newton_inv(Fp_polynomial& x, const Fp_polynomial& a, lidia_size_t m);
// uses a Newton Iteration with the FFT.
// ALIAS RESTRICTION: input may not alias output
#endif		//HEADBANGER



#ifndef HEADBANGER
/******** Modular Arithmetic without pre-conditioning ********/
// arithmetic mod f.
// all inputs and outputs are polynomials of degree less than deg(f).
// ASSUMPTION: f is assumed monic, and deg(f) > 0.
// ALIAS RESTRICTIONS: f and the exponent e
//                     cannot alias an output in the powering routines.
// NOTE: if you want to do many computations with a fixed f,
//       use the poly_modulus data structure and associated routines below.

friend void multiply_mod(Fp_polynomial& c, const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& f);
// c = (a * b) % f

friend void square_mod(Fp_polynomial& c, const Fp_polynomial& a, const Fp_polynomial& f);
// c = a^2 % f

friend void multiply_by_x_mod(Fp_polynomial& c, const Fp_polynomial& a, const Fp_polynomial& f);
// c = (a * x) mod f

friend void invert_mod(Fp_polynomial& c, const Fp_polynomial& a, const Fp_polynomial& f);
// c = a^{-1} % f, error if a is not invertible

friend bool invert_mod_status(Fp_polynomial& c, const Fp_polynomial& a, const Fp_polynomial& f);
// if (a, f) = 1, returns 0 and sets c = a^{-1} % f
// otherwise, returns 1 and sets c = (a, f)

friend void power_mod(Fp_polynomial& c, const Fp_polynomial& a, const bigint&  e, const Fp_polynomial& f);
// c = a^e % f
// WARNING: obsolete.  Use power_mod with poly_modulus (see below).

friend void power_x_mod(Fp_polynomial& c, const bigint&  e, const Fp_polynomial& f);
//c = x^e mod f
// WARNING: obsolete.  Use power_mod with poly_modulus (see below).

friend void power_x_plus_a_mod(Fp_polynomial& c, const bigint& a, const bigint&  e, const Fp_polynomial& f);
// c = (x + a)^e mod f
// WARNING: obsolete.  Use power_mod with poly_modulus (see below).

#endif 		//HEADBANGER


/******** other functions ********/
friend void trunc(Fp_polynomial& c, const Fp_polynomial& a, lidia_size_t m);
// c = a % x^m
friend void shift_right(Fp_polynomial& c, const Fp_polynomial& a, lidia_size_t n);
// c = a/x^n
friend void shift_left(Fp_polynomial& c, const Fp_polynomial& a, lidia_size_t n);
// c = a*x^n
friend void derivative(Fp_polynomial& c, const Fp_polynomial& a);
// c = derivative of a
friend Fp_polynomial derivative(const Fp_polynomial& a)
{   Fp_polynomial x; derivative(x, a); return x;	}

friend void add_multiple(Fp_polynomial &f, const Fp_polynomial &g, const bigint &s,
        lidia_size_t n, const Fp_polynomial &h);
// f = g + s*x^n*h, n >= 0
	
// computes c = a mod x^m-1
friend void cyclic_reduce(Fp_polynomial& x, const Fp_polynomial& a, lidia_size_t m);
//at now only used in fft_div_rem...

friend void power(Fp_polynomial &x, const Fp_polynomial &a, lidia_size_t e);
//x = a^e, e >= 0


/***************************************************************

			   gcd's

****************************************************************/

friend void gcd(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
// x = gcd(a, b),  x is always monic (or zero if a==b==0).
friend Fp_polynomial gcd(const Fp_polynomial& a, const Fp_polynomial& b)
{   Fp_polynomial x; gcd(x, a, b); return x;	}

friend void xgcd(Fp_polynomial& d, Fp_polynomial& s, Fp_polynomial& t, const Fp_polynomial& a, const Fp_polynomial& b);
// d = gcd(a,b), a s + b t = d

#ifndef HEADBANGER
friend void plain_xgcd(Fp_polynomial& d, Fp_polynomial& s, Fp_polynomial& t, const Fp_polynomial& a, const Fp_polynomial& b);
// same as above, but uses classical algorithm

friend void plain_gcd(Fp_polynomial& x, const Fp_polynomial& a, const Fp_polynomial& b);
// always uses "classical" arithmetic
#endif		//HEADBANGER


/***************************************************************

			functions

****************************************************************/

friend void swap(Fp_polynomial& x, Fp_polynomial& y);
// swap x & y (only pointers are swapped)

friend void randomize(Fp_polynomial& x, const bigint& p, lidia_size_t n);
// generate a random polynomial of degree = n with coefficients in Z/pZ




/********************************************************************

			   input and output

simple 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,
and a_n not zero (the zero polynomial is [ ]).
On input, the coefficients are arbitrary integers which are
then reduced modulo p, and leading zeros stripped.

*********************************************************************/

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

void read(istream &s);
void write(ostream &s) const;
void pretty_read(istream &s);
void pretty_print(ostream &s=cout) const;




////////////////////////////////////////////////////////
//		some friends - mostly because of set_degree()
////////////////////////////////////////////////////////



//////////////// classes
friend class fft_rep;
friend class modular_fft_rep;
friend class poly_modulus;
friend class poly_multiplier;
friend class poly_matrix;
friend class poly_mod_rep;
friend class eq_frac_info;
friend class poly_argument;

friend void half_gcd(Fp_polynomial& U, Fp_polynomial& V);
	//MOD
friend void plain_div_rem_vec(Fp_polynomial& q, Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b, base_vector<bigint>& x);
        //coeff, MOD, set_degree
friend void plain_rem_vec(Fp_polynomial& r, const Fp_polynomial& a, const Fp_polynomial& b, base_vector<bigint>& x);
	//coeff, set_degree
		
friend void copy_reverse(Fp_polynomial& x, const Fp_polynomial& a, lidia_size_t lo, lidia_size_t hi);
	//coeff, MOD
friend void remainder(Fp_polynomial &, const Fp_polynomial &, const poly_modulus &);
	//set_degree
friend void irred_combine(Fp_polynomial& x, const Fp_polynomial& f, const Fp_polynomial& g);
	//MOD, set_degree

friend void min_poly(Fp_polynomial& h, const Fp_polynomial& g, lidia_size_t m, const poly_modulus& F);
	//set_degree

friend void irred_poly(Fp_polynomial& h, const Fp_polynomial& g, lidia_size_t m, const poly_modulus& F);
	//set_degree

friend void update_map(base_vector<bigint>& xx, const base_vector<bigint>& a, const Fp_polynomial& b, const Fp_polynomial& f);
	//coeff, c_length
friend void project_powers(base_vector<bigint>& x, const base_vector<bigint>& a, lidia_size_t k, const poly_argument& H, const poly_modulus& F);
	//coeff, c_length


// for class factorization< Fp_polynomial >:
//	used in class single_factor< Fp_polynomial >:
    friend bool operator < (const Fp_polynomial &a, const Fp_polynomial &b);
    friend bool operator <= (const Fp_polynomial &a, const Fp_polynomial &b);

};  //end class Fp_polynomial









/*********************************************************************

					miscellaneous

*********************************************************************/


// ************   min_poly, checked_min_poly, irred_poly   ************

void min_poly(Fp_polynomial& h, const Fp_polynomial& g, lidia_size_t m,
                                                    const poly_modulus& F);
// computes the monic minimal polynomial of (g mod f).
// m = a bound on the degree of the minimal polynomial.
// The algorithm is probabilistic, always returns a divisor of
// the minimal polynomial, and returns a proper divisor with
// probability at most m/p.


void checked_min_poly(Fp_polynomial& h, const Fp_polynomial& g, lidia_size_t m, const poly_modulus& F);
// same as above, but guarantees that result is correct


void irred_poly(Fp_polynomial& h, const Fp_polynomial& g, lidia_size_t m,
                                                    const poly_modulus& 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 hence is always correct).



// ************   Multiplication of several polynomials   ************
//void multiply(Fp_polynomial& x, base_vector<Fp_polynomial>& a);
// x = product of all a[i]'s, contents of a[i] are destroyed



// ************   Modular Composition   ************

// algorithms for computing g(h) mod f
void compose(Fp_polynomial& x, const Fp_polynomial& g,
             const Fp_polynomial& h, const poly_modulus& F);
// x = g(h) mod f


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


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





/*********************************************************************

					factorization.c

*********************************************************************/


base_vector< bigint > find_roots(const Fp_polynomial& f);
// f is monic, and has deg(f) distinct roots.
// returns the list of roots

void find_roots(base_vector< bigint >& x, const Fp_polynomial& f);

bigint find_root(const Fp_polynomial& ff);
// finds a single root of ff.
// assumes that ff is monic and splits into distinct linear factors



bool prob_irred_test(const Fp_polynomial& f, lidia_size_t iter=1);

// performs a fast, probabilistic irreduciblity test
// the test can err only if f is reducible, and the
// error probability is bounded by p^{-iter}.
// Works for any p.


bool det_irred_test(const Fp_polynomial& f);

// performs a recursive deterministic irreducibility test


bool iter_irred_test(const Fp_polynomial& f);

// performs an iterative deterministic irreducibility test,
// based on DDF


void build_irred(Fp_polynomial& f, const bigint& p, lidia_size_t n);

// Build a monic irreducible poly of degree n.


void build_random_irred(Fp_polynomial& f, const Fp_polynomial& g);

// g is a monic irreducible polynomial.
// constructs a random monic irreducible polynomial f of the same degree.


lidia_size_t compute_degree(const Fp_polynomial& h, const poly_modulus& F);

// f = F.f is assumed to be an "equal degree" polynomial
// h = x^p mod f
// the common degree of the irreducible factors of f is computed

// This routine is useful in counting points on elliptic curves


lidia_size_t prob_compute_degree(const Fp_polynomial& h, const poly_modulus& F);

// same as above, but uses a slightly faster probabilistic algorithm
// the return value may be 0 or may be too big, but for large p
// (relative to n), this happens with very low probability.



void trace_map(Fp_polynomial& w, const Fp_polynomial& a, lidia_size_t d,
		const poly_modulus& F, const Fp_polynomial& b);

// w = a+a^q+...+^{q^{d-1}} mod f;
// it is assumed that d >= 0, and b = x^q mod f, q a power of p
// Space allocation can be controlled via ComposeBound



void power_compose(Fp_polynomial& w, const Fp_polynomial& b, lidia_size_t d, const poly_modulus& F);

// w = x^{q^d} mod f;
// it is assumed that d >= 0, and b = x^q mod f, q a power of p
// Space allocation can be controlled via ComposeBound




#endif
