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


#ifndef LIDIA_FP_POL_UTIL__H
#define LIDIA_FP_POL_UTIL__H

#ifndef HEADBANGER



#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)

#include <LiDIA:lidia.h>
#include <LiDIA:Fp_polynomial.h>
#include <LiDIA:timer.h>

#else

#include <LiDIA/lidia.h>
#include <LiDIA/Fp_polynomial.h>
#include <LiDIA/timer.h>

#endif




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

						tools

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




class my_timer
{
    timer t;
    char *msg;
public:
    my_timer();

    void start(const char* message);    //message must have less than 255 characters
    void stop();
};







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

					class poly_matrix

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

class poly_matrix
{
private:

	poly_matrix();
	poly_matrix(const poly_matrix&);  // disable
	Fp_polynomial elts[2][2];

public:

	poly_matrix(const mcp& mod)
	{ elts[0][0].MOD = mod; elts[0][1].MOD = mod;
	  elts[1][0].MOD = mod; elts[1][1].MOD = mod; }

	~poly_matrix() { }

	void kill();

	void operator=(const poly_matrix&);

	inline Fp_polynomial& operator() (lidia_size_t i, lidia_size_t j)
	{
		//NO CHECK
		return elts[i][j];
	}
	inline const Fp_polynomial& operator() (lidia_size_t i, lidia_size_t j) const
	{
		//NO CHECK
		return elts[i][j];
	}

	void half_gcd(const Fp_polynomial& U, const Fp_polynomial& V, lidia_size_t d_red);
	// deg(U) > deg(V),   1 <= d_red <= deg(U)+1.
	//
	// This computes a 2 x 2 polynomial matrix M_out such that
	//    M_out * (U, V)^T = (U', V')^T,
	// where U', V' are consecutive polynomials in the Euclidean remainder
	// sequence of U, V, and V' is the polynomial of highest degree
	// satisfying deg(V') <= deg(U) - d_red.


	void xhalf_gcd(Fp_polynomial& U, Fp_polynomial& V, lidia_size_t d_red);
	// same as above, except that U is replaced by U', and V by V'


	void iter_half_gcd(Fp_polynomial& U, Fp_polynomial& V, lidia_size_t d_red);


	void multiply(Fp_polynomial& U, Fp_polynomial& V) const;
	// (U, V)^T = (*this) * (U, V)^T

	void multiply_and_kill(poly_matrix& B, poly_matrix& C);
	// (*this) = B*C, B and C are destroyed
};











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

				class poly_argument

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

// The routine poly_argument::build (see below) which is implicitly called
// by the various compose and update_map routines builds a table
// of polynomials.  If poly_arg_bound > 0, then only up to
// about poly_arg_bound bigints mod p are allocated.  Setting this too
// low may lead to slower execution.
// If poly_arg_bound <= 0, then it is ignored, and space is allocated
// so as to maximize speed.
// Initially, poly_arg_bound = 0.

// If a single h is going to be used with many g's
// then you should build a poly_argument for h,
// and then use the compose routine below.
// poly_argument::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.
// If poly_arg_bound > 0, a table of size less than m may be built.


template< class T > class factorization;

class poly_argument
{
	base_vector<Fp_polynomial> H;
	
	//PolyargBound can only be changed if no poly_arguments have been declared yet
	static lidia_size_t poly_arg_bound;
	static bool change_enable;

    void compose_InnerProd(Fp_polynomial& x, const bigint* v, lidia_size_t v_length, lidia_size_t low, lidia_size_t high, const base_vector<Fp_polynomial>& H, lidia_size_t n, base_vector<bigint>& t) const;



public:
	poly_argument()
	{ 
		change_enable = false;
		debug_handler( "poly_argument", "poly_argument ( void )" );
	}
	
	~poly_argument() 
	{ 
		debug_handler( "poly_argument", "destructor" );
	}

	static void set_poly_arg_bound(lidia_size_t n);
	static lidia_size_t get_poly_arg_bound()
	{
	    return poly_arg_bound;
	}

	void build(const Fp_polynomial& h, const poly_modulus& F, lidia_size_t m);
	//m must be > 0, otherwise an error is raised


    void compose(Fp_polynomial& x, const Fp_polynomial& g, const poly_modulus& F ) const;
    //called by the various compose-routines below


    friend void compose(Fp_polynomial& x, const Fp_polynomial& g,
                const Fp_polynomial& h, const poly_modulus& F);
    friend void compose2(Fp_polynomial& x1, Fp_polynomial& x2,
                const Fp_polynomial& g1, const Fp_polynomial& g2,
                const Fp_polynomial& h, const poly_modulus& F);
    friend 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);
	

    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);
};










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

					class int_factor
				fac_vec "=" vector<int_factor>

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

class fac_vec;

class int_factor
{
    public:	//###########
	lidia_size_t q;		//base
	int a;		//exponent
    private:
	int b;
	double len;

	int_factor() {};
	~int_factor() {};
	inline void operator = (const int_factor c)
	{
		q = c.q;
		a = c.a;
		len = c.len;
		b = c.b;
	}

public:

//////////////////////////////////////////////
//					friends					//
//////////////////////////////////////////////

friend class fac_vec;

	friend int compute_split(int lo, int hi, const fac_vec& fvec);
//	reads: 	len

	friend void rec_compute_degree(int lo, int hi, const Fp_polynomial& h,
					const poly_modulus& F, fac_vec& fvec);
//	writes:	b
//	reads:	q, a

	friend lidia_size_t compute_degree(const Fp_polynomial& h, const poly_modulus& F);
//	writes:	len
//	reads:	q, a, b

	friend bool rec_irred_test(int lo, int hi, const Fp_polynomial& h,
				const poly_modulus& F, const fac_vec& fvec);
//	reads:	q, a

	friend void build_irred(Fp_polynomial& f, const bigint& p, lidia_size_t n);
//	writes:	len
//	reads:	q, a

	friend void swap (int_factor& a, int_factor& b);
};


class fac_vec
{
	int_factor *fvec;
	int num_factors;

	fac_vec();	//diable
	void compute_factorization(lidia_size_t n);
	void sort();

public:
	fac_vec(lidia_size_t n);

	~fac_vec()
	{
		debug_handler( "fac_vec", "destructor" );
		delete[] fvec;
	}

	void clear(int lo, int hi);
		//fvec[i].b = 0   for   i=lo,..,hi

	inline const int_factor& operator[] (int n) const
	{
		debug_handler( "fac_vec", "operator [] ( int ) const" );
		return fvec[n];
	}

	inline int_factor& operator[] (int n)
	{
	  	debug_handler( "fac_vec", "operator [] ( int )" );
		return fvec[n];
	}

	inline int number_of_factors() const
	{
		debug_handler( "fac_vec", "number_of_factors ( void )" );
		return num_factors;
	}

	lidia_size_t prod(int lo, int hi) const;
		//returns product of q^a for i=lo,..,hi

};










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

						compose.c

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

// ************   update_map   ************

void update_map(base_vector<bigint>& x, const base_vector<bigint>& a,
		   const poly_multiplier& B, const poly_modulus& 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 update_map(base_vector<bigint>& xx, const base_vector<bigint>& a,
			const Fp_polynomial& b, const Fp_polynomial& f);

// same as above, but uses only classical arithmetic



// ************   inner_product   ************

void inner_product(bigint& x, const base_vector<bigint>& a,
	const Fp_polynomial &b, lidia_size_t offset = 0);






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

						fractions

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

// ************   addition, test for equality   ************

void add_frac(Fp_polynomial& x, Fp_polynomial& y, const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

// computes (x, y) = (a*d + b*c mod f, b*d mod f)
// ALIAS RESTRICTION: inputs may not alias outputs

void subtract_frac(Fp_polynomial& x, Fp_polynomial& y, const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

// computes (x, y) = (a*d - b*c mod f, b*d mod f)
// ALIAS RESTRICTION: inputs may not alias outputs



void plain_add_frac(Fp_polynomial& x, Fp_polynomial& y, const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

void plain_subtract_frac(Fp_polynomial& x, Fp_polynomial& y, const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

// same as above, but slower



bool eq_frac(const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

// if (a*d - b*c = 0 mod f) then 1 else 0


bool plain_eq_frac(const Fp_polynomial& a, const Fp_polynomial& b, const Fp_polynomial& c, const Fp_polynomial& d, const poly_modulus& F);

// same as above, but slower



// ************   class eq_frac_info   ************

// next come data structures and routines for testing if a fixed
// a/b is equal to one of several c/d mod f, using a fast probabilistic test
// With a very small probability, two unequal fractions may be
// claimed to be equal.

class eq_frac_info
{
	base_vector<bigint> L1, L2;
	mcp modulus;

public:

	void build(const Fp_polynomial& a, const Fp_polynomial& b, const poly_modulus& F);

	bool eq_frac(const Fp_polynomial& c, const Fp_polynomial& d) const;

};





#endif

#endif
