/* Copyright (C) 2020 Daniel Schultz This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include "nmod_mpoly_factor.h" #include "fq_nmod_mpoly_factor.h" static void n_fq_bpoly_eval_sm_to_lg( fq_nmod_poly_t E, const n_bpoly_t B, const bad_fq_nmod_embed_t emb) { slong i; fq_nmod_poly_fit_length(E, B->length, emb->lgctx); for (i = 0; i < B->length; i++) bad_fq_nmod_embed_n_fq_sm_to_fq_nmod_lg(E->coeffs + i, B->coeffs + i, emb); E->length = B->length; _fq_nmod_poly_normalise(E, emb->lgctx); } static void n_fq_bpoly_set_n_fq_poly_gen0_lg_to_sm( n_bpoly_t A, const n_poly_t E, const bad_fq_nmod_embed_t emb) { slong lgd = fq_nmod_ctx_degree(emb->lgctx); slong i; n_bpoly_fit_length(A, E->length); for (i = 0; i < E->length; i++) bad_n_fq_embed_lg_to_sm(A->coeffs + i, E->coeffs + lgd*i, emb); A->length = E->length; n_bpoly_normalise(A); } static void n_fq_bpoly_make_monic_mod_poly( n_bpoly_t A, const n_bpoly_t B, const n_poly_t m, const fq_nmod_ctx_t ctx) { slong i; n_poly_t lcinv, t, g; FLINT_ASSERT(A != B); FLINT_ASSERT(B->length > 0); FLINT_ASSERT(n_fq_bpoly_is_canonical(B, ctx)); n_poly_init(lcinv); n_poly_init(g); n_poly_init(t); n_fq_poly_xgcd(g, lcinv, t, B->coeffs + B->length - 1, m, ctx); FLINT_ASSERT(n_fq_poly_is_one(g, ctx)); n_bpoly_fit_length(A, B->length); for (i = 0; i < B->length; i++) n_fq_poly_mulmod(A->coeffs + i, B->coeffs + i, lcinv, m, ctx); A->length = B->length; n_bpoly_normalise(A); n_poly_clear(lcinv); n_poly_clear(t); n_poly_clear(g); } /* multiplication in (Fq[y]/m(y))[x], inputs need not be reduced */ static void n_fq_bpoly_mul_mod_poly( n_bpoly_t A, const n_bpoly_t B, const n_bpoly_t C, const n_poly_t m, const fq_nmod_ctx_t ctx) { slong i, j; n_poly_t t; FLINT_ASSERT(A != B); FLINT_ASSERT(A != C); n_poly_init(t); n_bpoly_fit_length(A, B->length + C->length - 1); for (i = 0; i < B->length + C->length - 1; i++) n_poly_zero(A->coeffs + i); for (i = 0; i < B->length; i++) for (j = 0; j < C->length; j++) { n_fq_poly_mul(t, B->coeffs + i, C->coeffs + j, ctx); n_fq_poly_add(A->coeffs + i + j, A->coeffs + i + j, t, ctx); n_fq_poly_rem(A->coeffs + i + j, A->coeffs + i + j, m, ctx); } A->length = B->length + C->length - 1; n_bpoly_normalise(A); n_poly_clear(t); } /* division in (Fq[y]/m(y))[x], inputs need not be reduced */ static void n_fq_bpoly_divrem_mod_poly( n_bpoly_t Q, n_bpoly_t R, const n_bpoly_t A, const n_bpoly_t B, const n_poly_t m, const fq_nmod_ctx_t ctx) { slong i, qoff; n_poly_t q, t, Binv; FLINT_ASSERT(R != A); FLINT_ASSERT(R != B); FLINT_ASSERT(Q != A); FLINT_ASSERT(Q != B); FLINT_ASSERT(B->length > 0); n_poly_init(q); n_poly_init(t); n_poly_init(Binv); n_fq_bpoly_set(R, A, ctx); Q->length = 0; n_fq_poly_xgcd(q, Binv, t, B->coeffs + B->length - 1, m, ctx); FLINT_ASSERT(n_fq_poly_is_one(q, ctx)); while (R->length >= B->length) { n_fq_poly_mulmod(q, R->coeffs + R->length - 1, Binv, m, ctx); for (i = 0; i < B->length; i++) { n_fq_poly_mulmod(t, B->coeffs + i, q, m, ctx); n_fq_poly_sub(R->coeffs + i + R->length - B->length, R->coeffs + i + R->length - B->length, t, ctx); } qoff = R->length - B->length; FLINT_ASSERT(qoff >= 0); if (qoff >= Q->length) { n_bpoly_fit_length(Q, qoff + 1); for (i = Q->length; i <= qoff; i++) n_poly_zero(Q->coeffs + i); Q->length = qoff + 1; } n_fq_poly_set(Q->coeffs + qoff, q, ctx); FLINT_ASSERT(n_poly_is_zero(R->coeffs + R->length - 1)); n_bpoly_normalise(R); } n_poly_clear(q); n_poly_clear(t); n_poly_clear(Binv); } static void _lattice( nmod_mat_t N, n_bpoly_struct * const * g, slong r, const n_poly_t lift_alpha_pow, slong * starts, const n_bpoly_t f, const fq_nmod_ctx_t ctx) { slong deg = fq_nmod_ctx_degree(ctx); slong i, j, k, l; n_bpoly_t Q, R, dg; n_bpoly_struct * ld; nmod_mat_t M, T1, T2; int nlimbs; mp_limb_t * trow; slong lift_order = lift_alpha_pow->length - 1; nlimbs = _nmod_vec_dot_bound_limbs(r, ctx->mod); trow = (mp_limb_t *) flint_malloc(r*sizeof(mp_limb_t)); n_bpoly_init(Q); n_bpoly_init(R); n_bpoly_init(dg); ld = FLINT_ARRAY_ALLOC(r, n_bpoly_struct); for (i = 0; i < r; i++) n_bpoly_init(ld + i); /* init done */ for (i = 0; i < r; i++) { n_fq_bpoly_divrem_mod_poly(Q, R, f, g[i], lift_alpha_pow, ctx); FLINT_ASSERT(R->length == 0); n_fq_bpoly_derivative_gen0(R, g[i], ctx); n_fq_bpoly_mul_mod_poly(ld + i, Q, R, lift_alpha_pow, ctx); } for (k = 0; k + 1 < f->length; k++) { slong d = nmod_mat_nrows(N); if (d < 2) break; if (lift_order <= starts[k]) continue; nmod_mat_init(M, deg*(lift_order - starts[k]), d, ctx->modulus->mod.n); for (j = starts[k]; j < lift_order; j++) for (l = 0; l < deg; l++) { for (i = 0; i < r; i++) { if (k >= ld[i].length || j >= ld[i].coeffs[k].length) { trow[i] = 0; } else { trow[i] = ld[i].coeffs[k].coeffs[deg*j + l]; } } for (i = 0; i < d; i++) nmod_mat_entry(M, (j - starts[k])*deg + l, i) = _nmod_vec_dot(trow, N->rows[i], r, ctx->mod, nlimbs); } nmod_mat_init_nullspace_tr(T1, M); nmod_mat_init(T2, nmod_mat_nrows(T1), nmod_mat_ncols(N), ctx->mod.n); nmod_mat_mul(T2, T1, N); nmod_mat_swap(T2, N); nmod_mat_rref(N); nmod_mat_clear(M); nmod_mat_clear(T1); nmod_mat_clear(T2); } flint_free(trow); n_bpoly_clear(Q); n_bpoly_clear(R); n_bpoly_clear(dg); for (i = 0; i < r; i++) n_bpoly_clear(ld + i); flint_free(ld); } static int _zassenhaus( slong limit, n_tpoly_t F, const n_poly_t final_alpha_pow, const nmod_mat_t N, n_bpoly_struct * const * loc_fac_org, slong r, const n_bpoly_t B, const fq_nmod_ctx_t ctx) { int success; n_bpoly_t Q, R, t1, t2; n_poly_t g; slong i, j, s, len, d = nmod_mat_nrows(N); slong * subset; n_bpoly_struct * loc_fac; n_bpoly_struct * f; n_bpoly_t B_copy; FLINT_ASSERT(nmod_mat_ncols(N) == r); n_poly_init(g); n_bpoly_init(Q); n_bpoly_init(R); n_bpoly_init(t1); n_bpoly_init(t2); n_bpoly_init(B_copy); subset = FLINT_ARRAY_ALLOC(d, slong); loc_fac = FLINT_ARRAY_ALLOC(d, n_bpoly_struct); for (i = 0; i < d; i++) { subset[i] = i; n_bpoly_init(loc_fac + i); } for (i = 0; i < d; i++) { n_fq_bpoly_one(loc_fac + i, ctx); for (j = 0; j < r; j++) { if (nmod_mat_entry(N, i, j) == 0) continue; FLINT_ASSERT(nmod_mat_entry(N, i, j) == 1); n_fq_bpoly_mul_mod_poly(t1, loc_fac + i, loc_fac_org[j], final_alpha_pow, ctx); n_bpoly_swap(t1, loc_fac + i); } } f = (n_bpoly_struct *) B; len = d; for (s = 1; s <= len/2; s++) { if (s > limit) { success = 0; goto cleanup; } zassenhaus_subset_first(subset, len, s); while (1) { FLINT_ASSERT(f->length > 0); n_fq_bpoly_set_n_fq_poly_gen1(t1, f->coeffs + f->length - 1, ctx); for (i = 0; i < len; i++) { if (subset[i] >= 0) { n_fq_bpoly_mul_mod_poly(t2, t1, loc_fac + subset[i], final_alpha_pow, ctx); n_bpoly_swap(t1, t2); } } n_fq_bpoly_make_primitive(g, t1, ctx); if (n_fq_bpoly_divides(Q, f, t1, ctx)) { n_tpoly_fit_length(F, F->length + 1); n_bpoly_swap(F->coeffs + F->length, t1); F->length++; f = B_copy; n_bpoly_swap(f, Q); len -= s; if (!zassenhaus_subset_next_disjoint(subset, len + s)) break; } else { if (!zassenhaus_subset_next(subset, len)) break; } } } if (f->length > 1) { n_tpoly_fit_length(F, F->length + 1); n_fq_bpoly_set(F->coeffs + F->length, f, ctx); F->length++; } else { FLINT_ASSERT(f->length == 1); FLINT_ASSERT(n_fq_poly_is_one(f->coeffs + 0, ctx)); } success = 1; cleanup: n_poly_clear(g); n_bpoly_clear(Q); n_bpoly_clear(R); n_bpoly_clear(t1); n_bpoly_clear(t2); n_bpoly_clear(B_copy); for (i = 0; i < d; i++) n_bpoly_clear(loc_fac + i); flint_free(loc_fac); flint_free(subset); return success; } static void _hensel_build_tree( slong * link, n_bpoly_struct * v, n_bpoly_struct * w, const fq_nmod_poly_struct * local_facs, slong r, const bad_fq_nmod_embed_t emb) { slong i, j; n_poly_t d; n_poly_struct * V; n_poly_struct * W; V = FLINT_ARRAY_ALLOC(2*r - 2, n_poly_struct); W = FLINT_ARRAY_ALLOC(2*r - 2, n_poly_struct); n_poly_init(d); for (i = 0; i < 2*r - 2; i++) { n_poly_init(V + i); n_poly_init(W + i); } for (i = 0; i < r; i++) { n_fq_poly_set_fq_nmod_poly(V + i, local_facs + i, emb->lgctx); link[i] = -i - 1; } for (i = r, j = 0; j < 2*r - 4; i++, j += 2) { slong s, minp, mind; minp = j; mind = n_poly_degree(V + j); for (s = j + 1; s < i; s++) { if (n_poly_degree(V + s) < mind) { minp = s; mind = n_poly_degree(V + s); } } n_poly_swap(V + j, V + minp); SLONG_SWAP(link[j], link[minp]); minp = j + 1; mind = n_poly_degree(V + j + 1); for (s = j + 2; s < i; s++) { if (n_poly_degree(V + s) < mind) { minp = s; mind = n_poly_degree(V + s); } } n_poly_swap(V + j + 1, V + minp); SLONG_SWAP(link[j + 1], link[minp]); n_fq_poly_mul(V + i, V + j, V + j + 1, emb->lgctx); link[i] = j; } for (j = 0; j < 2*r - 2; j += 2) { n_fq_poly_xgcd(d, W + j, W + j + 1, V + j, V + j + 1, emb->lgctx); FLINT_ASSERT(n_fq_poly_is_one(d, emb->lgctx)); } for (j = 0; j < 2*r - 2; j++) { n_fq_bpoly_set_n_fq_poly_gen0_lg_to_sm(v + j, V + j, emb); n_fq_bpoly_set_n_fq_poly_gen0_lg_to_sm(w + j, W + j, emb); } n_poly_clear(d); for (i = 0; i < 2*r - 2; i++) { n_poly_clear(V + i); n_poly_clear(W + i); } flint_free(V); flint_free(W); } static void _hensel_lift_fac( n_bpoly_t G, n_bpoly_t H, const n_bpoly_t f, n_bpoly_t g, n_bpoly_t h, const n_bpoly_t a, const n_bpoly_t b, const n_poly_t p0, const n_poly_t p1, const fq_nmod_ctx_t ctx) { slong i; n_bpoly_t c, t1, t2, q, r; n_poly_t tq, tr; n_bpoly_init(c); n_bpoly_init(t1); n_bpoly_init(t2); n_bpoly_init(q); n_bpoly_init(r); n_poly_init(tq); n_poly_init(tr); #if FLINT_WANT_ASSERT n_fq_bpoly_mul(t1, g, a, ctx); n_fq_bpoly_mul(t2, h, b, ctx); n_fq_bpoly_add(c, t1, t2, ctx); FLINT_ASSERT(c->length > 0); for (i = 0; i < c->length; i++) n_fq_poly_neg(c->coeffs + i, c->coeffs + i, ctx); n_fq_poly_add_si(c->coeffs + 0, c->coeffs + 0, 1, ctx); n_bpoly_normalise(c); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p0, ctx); FLINT_ASSERT(n_fq_poly_is_canonical(tr, ctx)); FLINT_ASSERT(n_poly_is_zero(tr)); } #endif n_fq_bpoly_mul(t1, g, h, ctx); n_fq_bpoly_sub(c, f, t1, ctx); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p0, ctx); FLINT_ASSERT(n_fq_poly_is_canonical(tr, ctx)); FLINT_ASSERT(n_poly_is_zero(tr)); n_fq_poly_divrem(tr, c->coeffs + i, tq, p1, ctx); } n_fq_bpoly_mul_mod_poly(t1, c, b, p1, ctx); n_fq_bpoly_divrem_mod_poly(q, r, t1, g, p1, ctx); for (i = 0; i < r->length; i++) n_fq_poly_mul(r->coeffs + i, r->coeffs + i, p0, ctx); for (i = 0; i < g->length; i++) n_fq_poly_divrem(tq, g->coeffs + i, g->coeffs + i, p0, ctx); n_fq_bpoly_add(t1, r, g, ctx); n_fq_bpoly_mul_mod_poly(t2, c, a, p1, ctx); n_fq_bpoly_divrem_mod_poly(q, r, t2, h, p1, ctx); for (i = 0; i < r->length; i++) n_fq_poly_mul(r->coeffs + i, r->coeffs + i, p0, ctx); for (i = 0; i < h->length; i++) n_fq_poly_divrem(tq, h->coeffs + i, h->coeffs + i, p0, ctx); n_fq_bpoly_add(t2, r, h, ctx); n_bpoly_swap(G, t1); n_bpoly_swap(H, t2); #if FLINT_WANT_ASSERT { n_poly_t p01; n_poly_init(p01); n_fq_poly_mul(p01, p0, p1, ctx); n_fq_bpoly_mul(t1, G, H, ctx); n_fq_bpoly_sub(c, f, t1, ctx); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p01, ctx); FLINT_ASSERT(n_poly_is_zero(tr)); } n_poly_clear(p01); } #endif n_bpoly_clear(c); n_bpoly_clear(t1); n_bpoly_clear(t2); n_bpoly_clear(q); n_bpoly_clear(r); n_poly_clear(tq); n_poly_clear(tr); } static void _hensel_lift_inv( n_bpoly_t A, n_bpoly_t B, const n_bpoly_t G, const n_bpoly_t H, n_bpoly_t a, n_bpoly_t b, const n_poly_t p0, const n_poly_t p1, const fq_nmod_ctx_t ctx) { slong i; n_bpoly_t c, t1, t2, q, r; n_poly_t tq, tr; n_bpoly_init(c); n_bpoly_init(t1); n_bpoly_init(t2); n_bpoly_init(q); n_bpoly_init(r); n_poly_init(tq); n_poly_init(tr); #if FLINT_WANT_ASSERT { n_fq_bpoly_mul(t1, G, A, ctx); n_fq_bpoly_mul(t2, H, B, ctx); n_fq_bpoly_add(c, t1, t2, ctx); FLINT_ASSERT(c->length > 0); for (i = 0; i < c->length; i++) n_fq_poly_neg(c->coeffs + i, c->coeffs + i, ctx); n_fq_poly_add_si(c->coeffs + 0, c->coeffs + 0, 1, ctx); n_bpoly_normalise(c); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p0, ctx); FLINT_ASSERT(n_poly_is_zero(tr)); } } #endif for (i = 0; i < b->length; i++) n_fq_poly_divrem(tq, b->coeffs + i, b->coeffs + i, p0, ctx); for (i = 0; i < a->length; i++) n_fq_poly_divrem(tq, a->coeffs + i, a->coeffs + i, p0, ctx); n_fq_bpoly_mul(t1, G, a, ctx); n_fq_bpoly_mul(t2, H, b, ctx); n_fq_bpoly_add(c, t1, t2, ctx); FLINT_ASSERT(c->length > 0); for (i = 0; i < c->length; i++) n_fq_poly_neg(c->coeffs + i, c->coeffs + i, ctx); n_fq_poly_add_si(c->coeffs + 0, c->coeffs + 0, 1, ctx); n_bpoly_normalise(c); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p0, ctx); FLINT_ASSERT(n_poly_is_zero(tr)); n_fq_poly_divrem(tr, c->coeffs + i, tq, p1, ctx); } n_fq_bpoly_mul_mod_poly(t1, c, b, p1, ctx); n_fq_bpoly_divrem_mod_poly(q, r, t1, G, p1, ctx); for (i = 0; i < r->length; i++) n_fq_poly_mul(r->coeffs + i, r->coeffs + i, p0, ctx); n_fq_bpoly_add(t1, r, b, ctx); n_fq_bpoly_mul_mod_poly(t2, c, a, p1, ctx); n_fq_bpoly_divrem_mod_poly(q, r, t2, H, p1, ctx); for (i = 0; i < r->length; i++) n_fq_poly_mul(r->coeffs + i, r->coeffs + i, p0, ctx); n_fq_bpoly_add(t2, r, a, ctx); n_bpoly_swap(t1, B); n_bpoly_swap(t2, A); #if FLINT_WANT_ASSERT { n_poly_t p01; n_poly_init(p01); n_fq_poly_mul(p01, p0, p1, ctx); n_fq_bpoly_mul(t1, G, A, ctx); n_fq_bpoly_mul(t2, H, B, ctx); n_fq_bpoly_add(c, t1, t2, ctx); FLINT_ASSERT(c->length > 0); for (i = 0; i < c->length; i++) n_fq_poly_neg(c->coeffs + i, c->coeffs + i, ctx); n_fq_poly_add_si(c->coeffs + 0, c->coeffs + 0, 1, ctx); n_bpoly_normalise(c); for (i = 0; i < c->length; i++) { n_fq_poly_divrem(tq, tr, c->coeffs + i, p01, ctx); FLINT_ASSERT(n_poly_is_zero(tr)); } n_poly_clear(p01); } #endif n_bpoly_clear(c); n_bpoly_clear(t1); n_bpoly_clear(t2); n_bpoly_clear(q); n_bpoly_clear(r); n_poly_clear(tq); n_poly_clear(tr); } static void _hensel_lift_tree( int opt, slong * link, n_bpoly_struct * v, n_bpoly_struct * w, const n_bpoly_t f, slong j, const n_poly_t p0, const n_poly_t p1, const fq_nmod_ctx_t ctx) { FLINT_ASSERT(p1->length <= p0->length); if (j < 0) return; if (opt >= 0) _hensel_lift_fac(v + j, v + j + 1, f, v + j, v + j + 1, w + j, w + j + 1, p0, p1, ctx); if (opt <= 0) _hensel_lift_inv(w + j, w + j + 1, v + j, v + j + 1, w + j, w + j + 1, p0, p1, ctx); _hensel_lift_tree(opt, link, v, w, v + j, link[j], p0, p1, ctx); _hensel_lift_tree(opt, link, v, w, v + j + 1, link[j + 1], p0, p1, ctx); } int n_fq_bpoly_factor_lgprime( n_poly_t c, n_tpoly_t F, n_bpoly_t B, const fq_nmod_ctx_t ctx, flint_rand_t state) { int success; slong i, r, edeg; slong Blenx = B->length; slong Bleny; slong final_pow, curr_lift_pow, prev_lift_pow, next_lift_pow; slong * starts; fq_nmod_poly_t Beval; fq_nmod_poly_factor_t local_fac; fq_nmod_t Blc; n_bpoly_t monicB; nmod_mat_t N; slong * link; n_bpoly_struct * v, * w, ** lift_fac; n_tpoly_t tmp; slong e[FLINT_BITS]; slong old_nrows; slong zas_limit; n_poly_t final_alpha_pow, curr_alpha_pow, prev_alpha_pow, next_alpha_pow; n_poly_t alpha, p1; bad_fq_nmod_mpoly_embed_chooser_t embc; bad_fq_nmod_embed_struct * cur_emb; fq_nmod_mpoly_ctx_t ectx_mock, ctx_mock; FLINT_ASSERT(Blenx > 1); /* TODO remove mpoly_ctx_t from bad_fq_nmod_mpoly_embed_chooser interface */ mpoly_ctx_init(ctx_mock->minfo, 2, ORD_LEX); *ctx_mock->fqctx = *ctx; cur_emb = bad_fq_nmod_mpoly_embed_chooser_init(embc, ectx_mock, ctx_mock, state); n_poly_init(final_alpha_pow); n_poly_init(curr_alpha_pow); n_poly_init(prev_alpha_pow); n_poly_init(next_alpha_pow); n_poly_init(p1); n_poly_init(alpha); n_bpoly_init(monicB); n_tpoly_init(tmp); fq_nmod_poly_init(Beval, ectx_mock->fqctx); fq_nmod_poly_factor_init(local_fac, ectx_mock->fqctx); fq_nmod_init(Blc, ectx_mock->fqctx); nmod_mat_init(N, 0, 0, ctx->mod.n); starts = (slong *) flint_malloc(Blenx*sizeof(slong)); link = (slong *) flint_malloc(sizeof(slong)); lift_fac = FLINT_ARRAY_ALLOC(1, n_bpoly_struct *); /* init done */ n_fq_poly_set_fq_nmod_poly(alpha, cur_emb->h, ctx); edeg = cur_emb->h->length - 1; n_fq_bpoly_make_primitive(c, B, ctx); /* deg_y(B) + 1 */ Bleny = 0; for (i = 0; i < B->length; i++) Bleny = FLINT_MAX(Bleny, (B->coeffs + i)->length); /* CLD bounds */ for (i = 0; i < Blenx; i++) starts[i] = Bleny; goto got_alpha; next_alpha: cur_emb = bad_fq_nmod_mpoly_embed_chooser_next(embc, ectx_mock, ctx_mock, state); if (cur_emb == NULL) { /* ran out of primes */ success = 0; goto cleanup; } n_fq_poly_set_fq_nmod_poly(alpha, cur_emb->h, ctx); edeg = cur_emb->h->length - 1; got_alpha: n_fq_bpoly_eval_sm_to_lg(Beval, B, cur_emb); /* if killed leading coeff, get new alpha */ if (Beval->length != Blenx) goto next_alpha; local_fac->num = 0; /* stupid */ fq_nmod_poly_factor(local_fac, Blc, Beval, ectx_mock->fqctx); r = local_fac->num; /* if multiple factors, get new alpha */ for (i = 0; i < r; i++) { if (local_fac->exp[i] != 1) goto next_alpha; } /* if one factor, A is irreducible */ if (r < 2) { n_tpoly_fit_length(F, 1); n_bpoly_swap(F->coeffs + 0, B); F->length = 1; success = 1; goto cleanup; } for (i = 0; i < r; i++) { FLINT_ASSERT(local_fac->poly[i].length > 1); FLINT_ASSERT(fq_nmod_is_one(local_fac->poly[i].coeffs + local_fac->poly[i].length - 1, ectx_mock->fqctx)); } /* precision for constructing true factors */ final_pow = (Bleny + edeg - 1)/edeg; n_fq_poly_pow(final_alpha_pow, alpha, final_pow, ctx); nmod_mat_clear(N); nmod_mat_init(N, r, r, ctx->mod.n); for (i = 0; i < r; i++) nmod_mat_entry(N, i, i) = 1; link = (slong *) flint_realloc(link, (2*r - 2)*sizeof(slong)); lift_fac = (n_bpoly_struct **) flint_realloc(lift_fac, r*sizeof(n_bpoly_struct *)); n_tpoly_fit_length(tmp, 2*(2*r - 2)); v = tmp->coeffs + 0; w = tmp->coeffs + (2*r - 2); curr_lift_pow = final_pow + r; n_fq_poly_pow(curr_alpha_pow, alpha, curr_lift_pow, ctx); n_fq_bpoly_make_monic_mod_poly(monicB, B, curr_alpha_pow, ctx); _hensel_build_tree(link, v, w, local_fac->poly, r, cur_emb); for (i = 0; i < 2*r - 2; i++) if (-link[i] - 1 >= 0) lift_fac[-link[i] - 1] = v + i; FLINT_ASSERT(curr_lift_pow > 1); for (i = 0, e[i] = curr_lift_pow; e[i] > 1; i++) e[i+1] = (e[i] + 1) / 2; for (i--; i > 0; i--) { n_fq_poly_pow(prev_alpha_pow, alpha, e[i+1], ctx); n_fq_poly_pow(p1, alpha, e[i]-e[i+1], ctx); _hensel_lift_tree(0, link, v, w, monicB, 2*r-4, prev_alpha_pow, p1, ctx); } prev_lift_pow = e[1]; n_fq_poly_pow(prev_alpha_pow, alpha, prev_lift_pow, ctx); n_fq_poly_pow(p1, alpha, curr_lift_pow - prev_lift_pow, ctx); _hensel_lift_tree(1, link, v, w, monicB, 2*r-4, prev_alpha_pow, p1, ctx); zas_limit = 1; try_zas: F->length = 0; success = _zassenhaus(zas_limit, F, final_alpha_pow, N, lift_fac, r, B, ctx); if (success) goto cleanup; zas_limit = 2; more: old_nrows = nmod_mat_nrows(N); _lattice(N, lift_fac, r, curr_alpha_pow, starts, B, ctx); if (nmod_mat_nrows(N) < old_nrows && nmod_mat_is_reduced(N)) goto try_zas; next_lift_pow = curr_lift_pow + r; next_lift_pow = FLINT_MIN(next_lift_pow, 2*curr_lift_pow); n_fq_poly_pow(p1, alpha, curr_lift_pow - prev_lift_pow, ctx); _hensel_lift_tree(-1, link, v, w, monicB, 2*r-4, prev_alpha_pow, p1, ctx); n_fq_poly_pow(p1, alpha, next_lift_pow - curr_lift_pow, ctx); n_fq_poly_mul(next_alpha_pow, next_alpha_pow, p1, ctx); n_fq_bpoly_make_monic_mod_poly(monicB, B, next_alpha_pow, ctx); _hensel_lift_tree(0, link, v, w, monicB, 2*r-4, curr_alpha_pow, p1, ctx); prev_lift_pow = curr_lift_pow; curr_lift_pow = next_lift_pow; n_poly_swap(prev_alpha_pow, curr_alpha_pow); n_poly_swap(curr_alpha_pow, next_alpha_pow); goto more; cleanup: flint_free(starts); flint_free(link); flint_free(lift_fac); nmod_mat_clear(N); n_poly_clear(final_alpha_pow); n_poly_clear(curr_alpha_pow); n_poly_clear(prev_alpha_pow); n_poly_clear(next_alpha_pow); n_poly_clear(p1); n_poly_clear(alpha); n_bpoly_clear(monicB); n_tpoly_clear(tmp); fq_nmod_poly_clear(Beval, ctx); fq_nmod_poly_factor_clear(local_fac, ctx); fq_nmod_clear(Blc, ctx); bad_fq_nmod_mpoly_embed_chooser_clear(embc, ectx_mock, ctx_mock, state); return success; }