//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : gf_poly_argument.c
// Author      : Thomas Pfahler (TPf)
//               mainly based on V. Shoup's poly_argument for Fp_polynomial
// Last change : TPf, initial version
//

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:gf_polynomial.h>
#else
#include <LiDIA/gf_polynomial.h>
#endif



gf_poly_argument::gf_poly_argument() :
    vec(0), len(-1)
{ 
    debug_handler("gf_poly_modulus","gf_poly_modulus()");
}

gf_poly_argument::gf_poly_argument(const gf_poly_argument &x)
{
    debug_handler("gf_poly_modulus","gf_poly_modulus(gf_poly_modulus&)");
    if (x.len < 1)
    {
	delete[] vec;
	len = -1;
    }
    else
	lidia_error_handler("gf_poly_modulus","gf_poly_modulus(gf_poly_modulus&)::copy constructor not implemented");
}

gf_poly_argument::~gf_poly_argument()
{
    debug_handler("gf_poly_modulus","~gf_poly_modulus()");
    delete[] vec;
}

void gf_poly_argument::
build(const gf_polynomial &h, const gf_poly_modulus &F, lidia_size_t m)
//computes and stores h^0, h, h^2, ..., h^m mod f
{
    debug_handler("gf_poly_modulus","build(...)");
    if (m <= 0)
	lidia_error_handler("gf_poly_modulus","build::bad input");

    if (len != m+1)
    {
	delete[] vec;
	vec = new gf_polynomial[m+1];
	len = m+1;
    }
    
    vec[0].assign_one(h.base());
    vec[1].assign(h);
    for (lidia_size_t i = 2; i <= m; i++)
	multiply(vec[i], vec[i-1], h, F);

    debug_handler_c("gf_poly_modulus","build", 8,
	cout<<"gf_poly_argument: table\n";
	for (lidia_size_t j = 0; j < len; j++)
	    cout<<vec[j]<<endl; );
}


void gf_poly_argument::
compose(gf_polynomial &x, const gf_polynomial &g,
	const gf_poly_modulus &F) const
//x = g(h) mod F
{
    debug_handler("gf_poly_modulus","compose(...)");
    if (g.degree() <= 0)
    {
	x.assign(g);
	return;
    }
    lidia_size_t m = len - 1;
    lidia_size_t l = ((g.degree()+m)/m) - 1;

    gf_polynomial t, s;

    inner_prod(t, g, l*m, l*m + m - 1);

    for (lidia_size_t i = l-1; i >= 0; i--)
    {
//####### gf_poly_multiplier ???
	inner_prod(s, g, i*m, i*m + m - 1);
	multiply(t, t, vec[m], F);              /*   t = t * h^m   */
	add(t, t, s);
    }

    x.assign(t);
}

void gf_poly_argument::
inner_prod(gf_polynomial &x, const gf_polynomial &g, 
	    lidia_size_t lo, lidia_size_t hi) const
{
    debug_handler("gf_poly_modulus","inner_prod(...)");
    
    gf_polynomial t(vec[0].base()), s;
    
    hi = comparator<lidia_size_t>::min(hi, g.degree());
    for (lidia_size_t i = lo; i <= hi; i++)
    {
	multiply(s, vec[i-lo], g[i]);
	add(t, t, s);
    }
    x.assign(t);
}

