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



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

#include <LiDIA:Fp_polynomial.h>
#include <LiDIA:Fp_polynomial_util.h>
#include <LiDIA:poly_modulus.h>

#include <LiDIA:single_factor.h>
#include <LiDIA:factorization.h>

#else

#include <LiDIA/Fp_polynomial.h>
#include <LiDIA/Fp_polynomial_util.h>
#include <LiDIA/poly_modulus.h>

#include <LiDIA/single_factor.h>
#include <LiDIA/factorization.h>

#endif

#define LIDIA_OLD_DDF 0
#define LIDIA_DDF 1
//you can choose between simple 'old_ddf' or faster 'ddf' - routine
//see sf_can_zass, can_zass

void
sf_can_zass_work(factorization< Fp_polynomial > &factors,
	const Fp_polynomial &x_to_the_power_of_p, const poly_modulus &F, int DDF_FLAG = LIDIA_DDF)
// x_to_the_power_of_p = x^p mod f
// Assumes f is square-free, monic, deg(f) > 0 and f(0) != 0 .
// returns list of factors of f.
{
    debug_handler("Fp_polynomial", "sf_can_zass_work( factorization<Fp_polynomial>&, Fp_polynomial& f, lidia_size_t )");

    const Fp_polynomial &f = F.modulus();

    factors.kill();

    my_timer t;
    bool verbose = single_factor< Fp_polynomial >::verbose();

    const bigint & p = f.modulus();

    factorization< Fp_polynomial > ddf_fact, edf_fact;
    if (verbose)
    {
	cerr << "computing ddf...\n";
	t.start("ddf time: ");
    }


    switch (DDF_FLAG)
    {
	case (LIDIA_OLD_DDF): 	old_ddf(ddf_fact, f, x_to_the_power_of_p); break;
	case (LIDIA_DDF):
	default:		ddf(ddf_fact, f, x_to_the_power_of_p); break;
    }
//the exponents of 'ddf_fact' are the degrees of the irred. polynomials !!!

   
    if (verbose) t.stop();

    Fp_polynomial hh;
    poly_modulus FF;

    lidia_size_t i;
    for (i = 0; i < ddf_fact.no_of_composite_components(); i++)
    {
	const Fp_polynomial & g = ddf_fact.composite_base(i).base();
	lidia_size_t d = ddf_fact.composite_exponent(i);
	lidia_size_t r = g.degree() / d;

	if (r == 1)
	{
	    // g is already irreducible
	    append_irred_factor(factors, g);
	}
	else
	{
	    // must perform edf

	    if (verbose)
		cerr << "edf, degree = " << d << ", number = " << r << endl;

	    if (d == 1)
	    {
		// root finding

		root_edf(edf_fact, g);
	    }
	    else
	    {
		// general case

		FF.build(g);
		remainder(hh, x_to_the_power_of_p, g);
		edf(edf_fact, FF, hh, d);
	    }
	    multiply(factors, factors, edf_fact);
	}
    }
}

void
sf_can_zass(factorization< Fp_polynomial > &factors, const Fp_polynomial & f)
{
    bool verbose = single_factor< Fp_polynomial >::verbose();
    my_timer t;
    Fp_polynomial g;
    poly_modulus F(f);
    if (verbose) t.start("computing x^p...");
    power_x(g, f.modulus(), F);
    if (verbose) t.stop();
    sf_can_zass_work(factors, g, F);
}
	

void 
can_zass(factorization< Fp_polynomial > &factors, const Fp_polynomial & f)
//f must be monic
{
    debug_handler("Fp_polynomial", "can_zass( factorization< Fp_polynomial >&, Fp_polynomial&, lidia_size_t )");

    if (f.is_zero())
	lidia_error_handler("Fp_polynomial", "can_zass( factorization< Fp_polynomial >&, Fp_polynomial& )::input is zero polynomial");
    
    my_timer t;
    bool verbose = single_factor< Fp_polynomial >::verbose();
    factorization< Fp_polynomial > sfd, x;
    Fp_polynomial tmp;
    bigint lc = f.lead_coeff();

    if (verbose) t.start("square-free decomposition...");
    if (lc.is_one())
    {
	square_free_decomp(sfd, f);
	factors.kill();
    }
    else
    {
	tmp.assign(f);
	tmp.make_monic();
	square_free_decomp(sfd, tmp);
	factors.kill();
	tmp.assign_one();
	tmp.set_coefficient(lc, 0);
	factors.append(tmp);
    }
    if (verbose) t.stop();

    lidia_size_t i;
    for (i = 0; i < sfd.no_of_composite_components(); i++)
    {
	if (verbose)
	{
	    cerr << "factoring multiplicity " << sfd.composite_exponent(i);
	    cerr << ", deg = " << (sfd.composite_base(i).base()).degree() << "\n";
	}

	sf_can_zass(x, sfd.composite_base(i).base());
	
	x.power(sfd.composite_exponent(i));
	multiply(factors, factors, x);
    }
}


factorization< Fp_polynomial > can_zass(const Fp_polynomial & f)
{
    factorization< Fp_polynomial > F;
    can_zass(F, f);
    return F;
}

factorization< Fp_polynomial >
single_factor< Fp_polynomial >::can_zass() const
{
    factorization< Fp_polynomial > F;
    ::can_zass(F, rep);
    return F;
}

factorization< Fp_polynomial >
single_factor< Fp_polynomial >::sf_can_zass() const
{
    factorization< Fp_polynomial > F;
    ::sf_can_zass(F, rep);
    return F;
}

factorization< Fp_polynomial > sf_can_zass(const Fp_polynomial & f)
{
    factorization< Fp_polynomial > F;
    sf_can_zass(F, f);
    return F;
}


#undef LIDIA_OLD_DDF
#undef LIDIA_DDF
