#include "fmpz.h"
#include "fmpz_mod.h"
#include "fmpz_mod_poly.h"
#include "fmpz_mod_poly_factor.h"
#if FLINT_WANT_ASSERT
# include "longlong.h"
#endif
static void _fmpz_mod_poly_push_roots(
fmpz_mod_poly_factor_t r,
fmpz_mod_poly_t f,
slong mult,
const fmpz_t halfp,
fmpz_mod_poly_t t,
fmpz_mod_poly_t t2,
fmpz_mod_poly_struct * stack,
flint_rand_t randstate,
const fmpz_mod_ctx_t ctx)
{
slong i, sp;
fmpz_mod_poly_struct * a, * b;
const fmpz * p = fmpz_mod_ctx_modulus(ctx);
FLINT_ASSERT(fmpz_mod_poly_degree(f, ctx) >= 1);
FLINT_ASSERT(fmpz_is_one(f->coeffs + f->length - 1));
FLINT_ASSERT(fmpz_is_probabprime(p));
if (fmpz_cmp_ui(p, 10) < 0)
{
fmpz_t x, e;
fmpz_init(x);
fmpz_init(e);
for (fmpz_zero(x); fmpz_cmp(x, p) < 0; fmpz_add_ui(x, x, 1))
{
fmpz_mod_poly_evaluate_fmpz(e, f, x, ctx);
if (!fmpz_is_zero(e))
continue;
fmpz_mod_poly_factor_fit_length(r, r->num + 1, ctx);
fmpz_mod_poly_fit_length(r->poly + r->num, 2, ctx);
fmpz_mod_neg(r->poly[r->num].coeffs + 0, x, ctx);
fmpz_one(r->poly[r->num].coeffs + 1);
r->poly[r->num].length = 2;
r->exp[r->num] = mult;
r->num++;
}
fmpz_clear(e);
fmpz_clear(x);
return;
}
if (fmpz_is_zero(f->coeffs + 0))
{
fmpz_mod_poly_factor_fit_length(r, r->num + 1, ctx);
fmpz_mod_poly_fit_length(r->poly + r->num, 2, ctx);
fmpz_zero(r->poly[r->num].coeffs + 0);
fmpz_one(r->poly[r->num].coeffs + 1);
r->poly[r->num].length = 2;
r->exp[r->num] = mult;
r->num++;
i = 1;
while (i < f->length && fmpz_is_zero(f->coeffs + i))
i++;
fmpz_mod_poly_shift_right(f, f, i, ctx);
}
if (fmpz_mod_poly_degree(f, ctx) <= 1)
{
if (fmpz_mod_poly_degree(f, ctx) == 1)
{
fmpz_mod_poly_factor_fit_length(r, r->num + 1, ctx);
fmpz_mod_poly_swap(r->poly + r->num, f, ctx);
r->exp[r->num] = mult;
r->num++;
}
return;
}
FLINT_ASSERT(!fmpz_is_zero(f->coeffs + 0));
fmpz_mod_poly_reverse(t, f, f->length, ctx);
fmpz_mod_poly_inv_series(t2, t, t->length, ctx);
a = stack + 0;
fmpz_mod_poly_powmod_x_fmpz_preinv(t, halfp, f, t2, ctx);
fmpz_mod_poly_sub_si(t, t, 1, ctx);
fmpz_mod_poly_gcd(a, t, f, ctx);
b = stack + 1;
fmpz_mod_poly_add_si(t, t, 2, ctx);
fmpz_mod_poly_gcd(b, t, f, ctx);
if (fmpz_mod_poly_degree(a, ctx) < fmpz_mod_poly_degree(b, ctx))
fmpz_mod_poly_swap(a, b, ctx);
fmpz_mod_poly_factor_fit_length(r, r->num +
fmpz_mod_poly_degree(a, ctx) + fmpz_mod_poly_degree(b, ctx), ctx);
sp = (fmpz_mod_poly_degree(b, ctx) > 0) ? 2 : 1;
while (sp > 0)
{
sp--;
FLINT_ASSERT(sp < FLINT_BITS);
fmpz_mod_poly_swap(f, stack + sp, ctx);
FLINT_ASSERT(fmpz_mod_poly_degree(f, ctx) >= 0);
FLINT_ASSERT(FLINT_BIT_COUNT(fmpz_mod_poly_degree(f, ctx)) <= FLINT_BITS - sp);
if (fmpz_mod_poly_degree(f, ctx) <= 1)
{
if (fmpz_mod_poly_degree(f, ctx) == 1)
{
FLINT_ASSERT(r->num < r->alloc);
fmpz_mod_poly_set(r->poly + r->num, f, ctx);
r->exp[r->num] = mult;
r->num++;
}
}
else
{
FLINT_ASSERT(sp + 1 < FLINT_BITS);
_fmpz_mod_poly_split_rabin(stack + sp + 0, stack + sp + 1, f,
halfp, t, t2, randstate, ctx);
FLINT_ASSERT(FLINT_BIT_COUNT(fmpz_mod_poly_degree(stack + sp + 1, ctx))
<= FLINT_BITS - (sp + 1));
FLINT_ASSERT(FLINT_BIT_COUNT(fmpz_mod_poly_degree(stack + sp + 0, ctx))
<= FLINT_BITS - (sp + 0));
sp += 2;
}
}
}
void fmpz_mod_poly_roots(fmpz_mod_poly_factor_t r, const fmpz_mod_poly_t f,
int with_mult, const fmpz_mod_ctx_t ctx)
{
slong i;
fmpz_t p2;
flint_rand_t randstate;
fmpz_mod_poly_struct t[FLINT_BITS + 3];
FLINT_ASSERT(fmpz_is_probabprime(fmpz_mod_ctx_modulus(ctx)));
r->num = 0;
if (fmpz_mod_poly_degree(f, ctx) < 2)
{
if (fmpz_mod_poly_degree(f, ctx) == 1)
{
fmpz_mod_poly_factor_fit_length(r, 1, ctx);
fmpz_mod_poly_make_monic(r->poly + 0, f, ctx);
r->exp[0] = 1;
r->num = 1;
}
else if (fmpz_mod_poly_degree(f, ctx) < 0)
{
flint_throw(FLINT_ERROR,
"Exception in fmpz_mod_poly_roots: input polynomial is zero.");
}
return;
}
fmpz_init_set(p2, fmpz_mod_ctx_modulus(ctx));
fmpz_sub_ui(p2, p2, 1);
fmpz_fdiv_q_2exp(p2, p2, 1);
flint_rand_init(randstate);
for (i = 0; i < FLINT_BITS + 3; i++)
fmpz_mod_poly_init(t + i, ctx);
fmpz_mod_poly_make_monic(t + 0, f, ctx);
if (with_mult)
{
fmpz_mod_poly_factor_t sqf;
fmpz_mod_poly_factor_init(sqf, ctx);
fmpz_mod_poly_factor_squarefree(sqf, t + 0, ctx);
for (i = 0; i < sqf->num; i++)
{
_fmpz_mod_poly_push_roots(r, sqf->poly + i, sqf->exp[i],
p2, t + 1, t + 2, t + 3, randstate, ctx);
}
fmpz_mod_poly_factor_clear(sqf, ctx);
}
else
{
_fmpz_mod_poly_push_roots(r, t + 0, 1,
p2, t + 1, t + 2, t + 3, randstate, ctx);
}
fmpz_clear(p2);
flint_rand_clear(randstate);
for (i = 0; i < FLINT_BITS + 3; i++)
fmpz_mod_poly_clear(t + i, ctx);
}