/*============================================================================= 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) 2007 David Howden Copyright (C) 2007, 2008, 2009, 2010 William Hart Copyright (C) 2008 Richard Howell-Peak Copyright (C) 2011 Fredrik Johansson ******************************************************************************/ #include #include "nmod_poly.h" #include "nmod_mat.h" #include "ulong_extras.h" #include "profiler.h" #include "perm.h" static void nmod_poly_to_nmod_mat_col(nmod_mat_t mat, slong col, nmod_poly_t poly) { slong i; for (i = 0; i < poly->length; i++) nmod_mat_entry(mat, i, col) = poly->coeffs[i]; for ( ; i < mat->r; i++) nmod_mat_entry(mat, i, col) = UWORD(0); } static void nmod_mat_col_to_nmod_poly_shifted(nmod_poly_t poly, nmod_mat_t mat, slong col, slong * shift) { slong i, j, rows = mat->r; nmod_poly_fit_length(poly, rows); for (i = 0, j = 0; j < rows; j++) { if (shift[j]) poly->coeffs[j] = 0; else { poly->coeffs[j] = nmod_mat_entry(mat, i, col); i++; } } poly->length = rows; _nmod_poly_normalise(poly); } static void __nmod_poly_factor_berlekamp(nmod_poly_factor_t factors, flint_rand_t state, const nmod_poly_t f) { const mp_limb_t p = nmod_poly_modulus(f); const slong n = nmod_poly_degree(f); nmod_poly_factor_t fac1, fac2; nmod_poly_t x, x_p; nmod_poly_t x_pi, x_pi2; nmod_poly_t Q; nmod_mat_t matrix; mp_limb_t coeff; slong i, nullity, col, row, *shift; nmod_poly_t *basis; if (f->length <= 2) { nmod_poly_factor_insert(factors, f, 1); return; } /* Step 1, we compute x^p mod f in F_p[X]/ */ nmod_poly_init(x, p); nmod_poly_init(x_p, p); nmod_poly_set_coeff_ui(x, 1, 1); nmod_poly_powmod_ui_binexp(x_p, x, p, f); nmod_poly_clear(x); /* Step 2, compute the matrix for the Berlekamp Map */ nmod_mat_init(matrix, n, n, p); nmod_poly_init(x_pi, p); nmod_poly_init(x_pi2, p); nmod_poly_set_coeff_ui(x_pi, 0, 1); for (i = 0; i < n; i++) { /* Q - I */ nmod_poly_set(x_pi2, x_pi); coeff = nmod_poly_get_coeff_ui(x_pi2, i); if (coeff) nmod_poly_set_coeff_ui(x_pi2, i, coeff - 1); else nmod_poly_set_coeff_ui(x_pi2, i, p - 1); nmod_poly_to_nmod_mat_col(matrix, i, x_pi2); nmod_poly_mulmod(x_pi, x_pi, x_p, f); } nmod_poly_clear(x_p); nmod_poly_clear(x_pi); nmod_poly_clear(x_pi2); /* Row reduce Q - I */ nullity = n - nmod_mat_rref(matrix); /* Find a basis for the nullspace */ basis = (nmod_poly_t *) flint_malloc(nullity * sizeof(nmod_poly_t)); shift = (slong *) flint_calloc(n, sizeof(slong)); col = 1; /* first column is always zero */ row = 0; shift[0] = 1; for (i = 1; i < nullity; i++) { nmod_poly_init(basis[i], p); while (nmod_mat_entry(matrix, row, col)) { row++; col++; } nmod_mat_col_to_nmod_poly_shifted(basis[i], matrix, col, shift); nmod_poly_set_coeff_ui(basis[i], col, p - 1); shift[col] = 1; col++; } flint_free(shift); nmod_mat_clear(matrix); /* we are done */ if (nullity == 1) { nmod_poly_factor_insert(factors, f, 1); flint_free(basis); } else { /* Generate random linear combinations */ nmod_poly_t factor, b, power, g; nmod_poly_init(factor, p); nmod_poly_init(b, p); nmod_poly_init(power, p); nmod_poly_init(g, p); while (1) { do { nmod_poly_zero(factor); for (i = 1; i < nullity; i++) { nmod_poly_scalar_mul_nmod(b, basis[i], n_randint(state, p)); nmod_poly_add(factor, factor, b); } nmod_poly_set_coeff_ui(factor, 0, n_randint(state, p)); if (!nmod_poly_is_zero(factor)) nmod_poly_make_monic(factor, factor); } while (nmod_poly_is_one(factor) || nmod_poly_is_zero(factor)); nmod_poly_gcd(g, f, factor); if (nmod_poly_length(g) != 1) break; if (p > 3) nmod_poly_powmod_ui_binexp(power, factor, p >> 1, f); else nmod_poly_set(power, factor); power->coeffs[0] = n_addmod(power->coeffs[0], p - 1, p); _nmod_poly_normalise(power); nmod_poly_gcd(g, power, f); if (nmod_poly_length(g) != 1) break; } for (i = 1; i < nullity; i++) nmod_poly_clear(basis[i]); flint_free(basis); nmod_poly_clear(power); nmod_poly_clear(factor); nmod_poly_clear(b); if (!nmod_poly_is_zero(g)) nmod_poly_make_monic(g, g); nmod_poly_factor_init(fac1); nmod_poly_factor_init(fac2); __nmod_poly_factor_berlekamp(fac1, state, g); nmod_poly_init(Q, p); nmod_poly_div(Q, f, g); if (!nmod_poly_is_zero(Q)) nmod_poly_make_monic(Q, Q); __nmod_poly_factor_berlekamp(fac2, state, Q); nmod_poly_factor_concat(factors, fac1); nmod_poly_factor_concat(factors, fac2); nmod_poly_factor_clear(fac1); nmod_poly_factor_clear(fac2); nmod_poly_clear(Q); nmod_poly_clear(g); } } void nmod_poly_factor_berlekamp(nmod_poly_factor_t factors, const nmod_poly_t f) { flint_rand_t r; flint_randinit(r); __nmod_poly_factor_berlekamp(factors, r, f); flint_randclear(r); }