/*=============================================================================

    This file is part of FLINT.

    FLINT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    FLINT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FLINT; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

=============================================================================*/
/******************************************************************************

    Copyright (C) 2011 Andy Novocin
    Copyright (C) 2011 Sebastian Pancratz

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

*******************************************************************************

    Memory management

*******************************************************************************

void fmpz_poly_factor_init(fmpz_poly_factor_t fac)

    Initialises a new factor structure.

void fmpz_poly_factor_init2(fmpz_poly_factor_t fac, slong alloc)

    Initialises a new factor structure, providing space for 
    at least \code{alloc} factors.

void fmpz_poly_factor_realloc(fmpz_poly_factor_t fac, slong alloc)

    Reallocates the factor structure to provide space for 
    precisely \code{alloc} factors.

void fmpz_poly_factor_fit_length(fmpz_poly_factor_t fac, slong len)

    Ensures that the factor structure has space for at 
    least \code{len} factors.  This functions takes care 
    of the case of repeated calls by always at least 
    doubling the number of factors the structure can hold.

void fmpz_poly_factor_clear(fmpz_poly_factor_t fac)

    Releases all memory occupied by the factor structure.

*******************************************************************************

    Manipulating factors

*******************************************************************************

void fmpz_poly_factor_set(fmpz_poly_factor_t res, const fmpz_poly_factor_t fac)

    Sets \code{res} to the same factorisation as \code{fac}.

void fmpz_poly_factor_insert(fmpz_poly_factor_t fac, 
                             const fmpz_poly_t p, slong e)

    Adds the primitive polynomial $p^e$ to the factorisation \code{fac}.

    Assumes that $\deg(p) \geq 2$ and $e \neq 0$.

void fmpz_poly_factor_concat(fmpz_poly_factor_t res,
                             const fmpz_poly_factor_t fac)

    Concatenates two factorisations.

    This is equivalent to calling \code{fmpz_poly_factor_insert()} 
    repeatedly with the individual factors of \code{fac}.

    Does not support aliasing between \code{res} and \code{fac}.

*******************************************************************************

    Input and output

*******************************************************************************

void fmpz_poly_factor_print(const fmpz_poly_factor_t fac)

    Prints the entries of \code{fac} to standard output.

*******************************************************************************

    Factoring algorithms

*******************************************************************************

void fmpz_poly_factor_squarefree(fmpz_poly_factor_t fac, fmpz_poly_t F)

    Takes as input a polynomial $F$ and a freshly initialized factor 
    structure \code{fac}.  Updates \code{fac} to contain a factorization 
    of $F$ into (not necessarily irreducible) factors that themselves 
    have no repeated factors.  None of the returned factors will have 
    the same exponent. That is we return $g_i$ and unique $e_i$ such that 
    \begin{equation*}
    F = c \prod_{i} g_i^{e_i}
    \end{equation*} 
    where $c$ is the signed content of $F$ and $\gcd(g_i, g_i') = 1$.

void fmpz_poly_factor_zassenhaus_recombination(fmpz_poly_factor_t 
    final_fac, const fmpz_poly_factor_t lifted_fac, 
    const fmpz_poly_t F, const fmpz_t P, slong exp)

    Takes as input a factor structure \code{lifted_fac} containing a 
    squarefree factorization of the polynomial $F \bmod p$. The algorithm 
    does a brute force search for irreducible factors of $F$ over the 
    integers, and each factor is raised to the power \code{exp}.

    The impact of the algorithm is to augment a factorization of 
    \code{F^exp} to the factor structure \code{final_fac}.

void _fmpz_poly_factor_zassenhaus(fmpz_poly_factor_t final_fac, 
                                  slong exp, fmpz_poly_t f, slong cutoff)

    This is the internal wrapper of Zassenhaus.

    It will attempt to find a small prime such that $f$ modulo $p$ has 
    a minimal number of factors.  If it cannot find a prime giving less 
    than \code{cutoff} factors it aborts.  Then it decides a $p$-adic 
    precision to lift the factors to, hensel lifts, and finally calls 
    Zassenhaus recombination.

    Assumes that $\len(f) \geq 2$.

    Assumes that $f$ is primitive.

    Assumes that the constant coefficient of $f$ is non-zero.  Note that 
    this can be easily achieved by taking out factors of the form $x^k$ 
    before calling this routine.

void fmpz_poly_factor_zassenhaus(fmpz_poly_factor_t final_fac, fmpz_poly_t F)

    A wrapper of the Zassenhaus factoring algorithm, which takes as input 
    any polynomial $F$, and stores a factorization in \code{final_fac}.

    The complexity will be exponential in the number of local factors 
    we find for the components of a squarefree factorization of $F$.

