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

    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) 2010 William Hart
    Copyright (C) 2011 Fredrik Johansson
    Copyright (C) 2011 Sebastian Pancratz

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

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

    Factorisation

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

void nmod_poly_factor_init(nmod_poly_factor_t fac)

    Initialises \code{fac} for use. An \code{nmod_poly_factor_t}
    represents a polynomial in factorised form as a product of
    polynomials with associated exponents.

void nmod_poly_factor_clear(nmod_poly_factor_t fac)

    Frees all memory associated with \code{fac}.

void nmod_poly_factor_realloc(nmod_poly_factor_t fac, slong alloc)

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

void nmod_poly_factor_fit_length(nmod_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 nmod_poly_factor_set(nmod_poly_factor_t res, const nmod_poly_factor_t fac)

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

void nmod_poly_factor_print(const nmod_poly_factor_t fac)

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

void nmod_poly_factor_insert(nmod_poly_factor_t fac,
                             const nmod_poly_t poly, slong exp)

    Inserts the factor \code{poly} with multiplicity \code{exp} into
    the factorisation \code{fac}.

    If \code{fac} already contains \code{poly}, then \code{exp} simply
    gets added to the exponent of the existing entry.

void nmod_poly_factor_concat(nmod_poly_factor_t res,
                             const nmod_poly_factor_t fac)

    Concatenates two factorisations.

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

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

void nmod_poly_factor_pow(nmod_poly_factor_t fac, slong exp)

    Raises \code{fac} to the power \code{exp}.

ulong nmod_poly_remove(nmod_poly_t f, const nmod_poly_t p)

    Removes the highest possible power of \code{p} from \code{f} and
    returns the exponent.

int nmod_poly_is_irreducible(const nmod_poly_t f)

    Returns 1 if the polynomial \code{f} is irreducible, otherwise returns 0.

int nmod_poly_is_irreducible_ddf(const nmod_poly_t f)

    Returns 1 if the polynomial \code{f} is irreducible, otherwise returns 0.
    Uses fast distinct-degree factorisation.

int nmod_poly_is_irreducible_rabin(const nmod_poly_t f)

    Returns 1 if the polynomial \code{f} is irreducible, otherwise returns 0.
    Uses Rabin irreducibility test.

int _nmod_poly_is_squarefree(mp_srcptr f, slong len, nmod_t mod)

    Returns 1 if \code{(f, len)} is squarefree, and 0 otherwise. As a
    special case, the zero polynomial is not considered squarefree.
    There are no restrictions on the length.

int nmod_poly_is_squarefree(const nmod_poly_t f)

    Returns 1 if \code{f} is squarefree, and 0 otherwise. As a special
    case, the zero polynomial is not considered squarefree.

void nmod_poly_factor_squarefree(nmod_poly_factor_t res, const nmod_poly_t f)

    Sets \code{res} to a square-free factorization of \code{f}.

int nmod_poly_factor_equal_deg_prob(nmod_poly_t factor,
    flint_rand_t state, const nmod_poly_t pol, slong d)

    Probabilistic equal degree factorisation of \code{pol} into
    irreducible factors of degree \code{d}. If it passes, a factor is
    placed in factor and 1 is returned, otherwise 0 is returned and
    the value of factor is undetermined.

    Requires that \code{pol} be monic, non-constant and squarefree.

void nmod_poly_factor_equal_deg(nmod_poly_factor_t factors,
                                const nmod_poly_t pol, slong d)

    Assuming \code{pol} is a product of irreducible factors all of
    degree \code{d}, finds all those factors and places them in factors.
    Requires that \code{pol} be monic, non-constant and squarefree.

void nmod_poly_factor_distinct_deg(nmod_poly_factor_t res,
                                   const nmod_poly_t poly, slong * const *degs)

    Factorises a monic non-constant squarefree polymnomial \code{poly}
    of degree n into factors $f[d]$ such that for $1 \leq d \leq n$
    $f[d]$ is the product of the monic irreducible factors of \code{poly}
    of degree $d$. Factors $f[d]$ are stored in \code{res}, and the degree $d$
    of the irreducible factors is stored in \code{degs} in the same order
    as the factors.

    Requires that \code{degs} has enough space for $(n/2)+1 * sizeof(slong)$.

void nmod_poly_factor_distinct_deg_threaded(nmod_poly_factor_t res,
                                   const nmod_poly_t poly, slong * const *degs)

    Multithreaded version of \code{nmod_poly_factor_distinct_deg}.

void nmod_poly_factor_cantor_zassenhaus(nmod_poly_factor_t res,
                                        const nmod_poly_t f)

    Factorises a non-constant polynomial \code{f} into monic irreducible
    factors using the Cantor-Zassenhaus algorithm.

void nmod_poly_factor_berlekamp(nmod_poly_factor_t res, const nmod_poly_t f)

    Factorises a non-constant, squarefree polynomial \code{f} into monic
    irreducible factors using the Berlekamp algorithm.

void nmod_poly_factor_kaltofen_shoup(nmod_poly_factor_t res,
				     const nmod_poly_t poly)

    Factorises a non-constant polynomial \code{f} into monic irreducible
    factors using the fast version of Cantor-Zassenhaus algorithm proposed by
    Kaltofen and Shoup (1998). More precisely this algorithm uses a
    “baby step/giant step” strategy for the distinct-degree factorization
    step. If \code{flint_get_num_threads()} is greater than one
    \code{nmod_poly_factor_distinct_deg_threaded} is used.

mp_limb_t nmod_poly_factor_with_berlekamp(nmod_poly_factor_t res,
                                          const nmod_poly_t f)

    Factorises a general polynomial \code{f} into monic irreducible factors
    and returns the leading coefficient of \code{f}, or 0 if \code{f}
    is the zero polynomial.

    This function first checks for small special cases, deflates \code{f}
    if it is of the form $p(x^m)$ for some $m > 1$, then performs a
    square-free factorisation, and finally runs Berlekamp on all the
    individual square-free factors.

mp_limb_t nmod_poly_factor_with_cantor_zassenhaus(nmod_poly_factor_t res,
                                                  const nmod_poly_t f)

    Factorises a general polynomial \code{f} into monic irreducible factors
    and returns the leading coefficient of \code{f}, or 0 if \code{f}
    is the zero polynomial.

    This function first checks for small special cases, deflates \code{f}
    if it is of the form $p(x^m)$ for some $m > 1$, then performs a
    square-free factorisation, and finally runs Cantor-Zassenhaus on all the
    individual square-free factors.

mp_limb_t nmod_poly_factor_with_kaltofen_shoup(nmod_poly_factor_t res,
					       const nmod_poly_t f)

    Factorises a general polynomial \code{f} into monic irreducible factors
    and returns the leading coefficient of \code{f}, or 0 if \code{f}
    is the zero polynomial.

    This function first checks for small special cases, deflates \code{f}
    if it is of the form $p(x^m)$ for some $m > 1$, then performs a
    square-free factorisation, and finally runs Kaltofen-Shoup on all the
    individual square-free factors.

mp_limb_t nmod_poly_factor(nmod_poly_factor_t res, const nmod_poly_t f)

    Factorises a general polynomial \code{f} into monic irreducible factors
    and returns the leading coefficient of \code{f}, or 0 if \code{f}
    is the zero polynomial.

    This function first checks for small special cases, deflates \code{f}
    if it is of the form $p(x^m)$ for some $m > 1$, then performs a
    square-free factorisation, and finally runs either Cantor-Zassenhaus
    or Berlekamp on all the individual square-free factors.
    Currently Cantor-Zassenhaus is used by default unless the modulus is 2, in
    which case Berlekamp is used.

void *
_nmod_poly_interval_poly_worker(void* arg_ptr)

    Worker function to compute interval polynomials in distinct degree
    factorisation. Input/output is stored in
    \code{nmod_poly_interval_poly_arg_t}.

