#include "fmpz.h"
#include "fmpz_mod.h"
#include "fmpz_mod_poly.h"
#include "fmpz_mod_poly_factor.h"
void
fmpz_mod_poly_factor_squarefree(fmpz_mod_poly_factor_t res,
const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx)
{
fmpz_mod_poly_t f_d, g, g_1, r;
fmpz_t x;
slong deg, i, p_ui;
res->num = 0;
if (f->length <= 1)
return;
if (f->length == 2)
{
fmpz_mod_poly_factor_insert(res, f, 1, ctx);
fmpz_mod_poly_make_monic(res->poly + (res->num - 1),
res->poly + (res->num - 1), ctx);
return;
}
p_ui = fmpz_get_ui(fmpz_mod_ctx_modulus(ctx));
deg = fmpz_mod_poly_degree(f, ctx);
fmpz_init(x);
fmpz_mod_poly_init(g_1, ctx);
fmpz_mod_poly_init(f_d, ctx);
fmpz_mod_poly_init(g, ctx);
fmpz_mod_poly_derivative(f_d, f, ctx);
if (fmpz_mod_poly_is_zero(f_d, ctx))
{
fmpz_mod_poly_factor_t new_res;
fmpz_mod_poly_t h;
fmpz_mod_poly_init(h, ctx);
for (i = 0; i <= deg / p_ui; i++)
{
fmpz_mod_poly_get_coeff_fmpz(x, f, i * p_ui, ctx);
fmpz_mod_poly_set_coeff_fmpz(h, i, x, ctx);
}
fmpz_mod_poly_factor_init(new_res, ctx);
fmpz_mod_poly_factor_squarefree(new_res, h, ctx);
fmpz_mod_poly_factor_pow(new_res, p_ui, ctx);
fmpz_mod_poly_factor_concat(res, new_res, ctx);
fmpz_mod_poly_clear(h, ctx);
fmpz_mod_poly_factor_clear(new_res, ctx);
}
else
{
fmpz_mod_poly_t h, z;
fmpz_mod_poly_init(r, ctx);
fmpz_mod_poly_gcd(g, f, f_d, ctx);
fmpz_mod_poly_divrem(g_1, r, f, g, ctx);
i = 1;
fmpz_mod_poly_init(h, ctx);
fmpz_mod_poly_init(z, ctx);
while (g_1->length > 1)
{
fmpz_mod_poly_gcd(h, g_1, g, ctx);
fmpz_mod_poly_divrem(z, r, g_1, h, ctx);
if (z->length > 1)
{
fmpz_mod_poly_factor_insert(res, z, 1, ctx);
fmpz_mod_poly_make_monic(res->poly + (res->num - 1),
res->poly + (res->num - 1), ctx);
if (res->num)
res->exp[res->num - 1] *= i;
}
i++;
fmpz_mod_poly_set(g_1, h, ctx);
fmpz_mod_poly_divrem(g, r, g, h, ctx);
}
fmpz_mod_poly_clear(h, ctx);
fmpz_mod_poly_clear(z, ctx);
fmpz_mod_poly_clear(r, ctx);
fmpz_mod_poly_make_monic(g, g, ctx);
if (g->length > 1)
{
fmpz_mod_poly_t g_p;
fmpz_mod_poly_factor_t new_res_2;
fmpz_mod_poly_init(g_p, ctx);
for (i = 0; i <= fmpz_mod_poly_degree(g, ctx) / p_ui; i++)
{
fmpz_mod_poly_get_coeff_fmpz(x, g, i * p_ui, ctx);
fmpz_mod_poly_set_coeff_fmpz(g_p, i, x, ctx);
}
fmpz_mod_poly_factor_init(new_res_2, ctx);
fmpz_mod_poly_factor_squarefree(new_res_2, g_p, ctx);
fmpz_mod_poly_factor_pow(new_res_2, p_ui, ctx);
fmpz_mod_poly_factor_concat(res, new_res_2, ctx);
fmpz_mod_poly_clear(g_p, ctx);
fmpz_mod_poly_factor_clear(new_res_2, ctx);
}
}
fmpz_clear(x);
fmpz_mod_poly_clear(g_1, ctx);
fmpz_mod_poly_clear(f_d, ctx);
fmpz_mod_poly_clear(g, ctx);
}