/* Copyright (C) 2018, 2021 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 "fmpz_mpoly_factor.h" /* return an n with |gcd(A,B)|_infty < 2^n or return UWORD_MAX */ static flint_bitcnt_t fmpz_mpoly_gcd_bitbound( const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) { flint_bitcnt_t bound = UWORD_MAX; fmpz_t norm, M; slong * degs; TMP_INIT; TMP_START; fmpz_init(norm); fmpz_init(M); degs = TMP_ARRAY_ALLOC(ctx->minfo->nvars, slong); fmpz_mpoly_degrees_si(degs, A, ctx); _fmpz_vec_height(norm, A->coeffs, A->length); if (fmpz_mpoly_factor_bound_si(M, norm, degs, ctx->minfo->nvars)) bound = FLINT_MIN(bound, fmpz_bits(M)); fmpz_mpoly_degrees_si(degs, B, ctx); _fmpz_vec_height(norm, B->coeffs, B->length); if (fmpz_mpoly_factor_bound_si(M, norm, degs, ctx->minfo->nvars)) bound = FLINT_MIN(bound, fmpz_bits(M)); fmpz_clear(norm); fmpz_clear(M); TMP_END; return bound; } int fmpz_mpolyl_gcd_zippel( fmpz_mpoly_t G, fmpz_mpoly_t Abar, fmpz_mpoly_t Bbar, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx, flint_rand_t state) { flint_bitcnt_t coeffbitbound; flint_bitcnt_t coeffbits; flint_bitcnt_t bits = G->bits; int success, changed; slong i, j, Gdegbound, Gdeg, req_zip_images; mp_limb_t p, t, gammap; fmpz_t c, gamma, modulus; nmod_mpoly_t Ap, Bp, Gp, Abarp, Bbarp; nmod_mpoly_ctx_t ctxp; n_poly_t Amarks, Bmarks, Gmarks; slong * perm = NULL; FLINT_ASSERT(ctx->minfo->ord == ORD_LEX); FLINT_ASSERT(ctx->minfo->nvars > 1); FLINT_ASSERT(bits <= FLINT_BITS); FLINT_ASSERT(bits == A->bits); FLINT_ASSERT(bits == Abar->bits); FLINT_ASSERT(bits == Bbar->bits); FLINT_ASSERT(bits == B->bits); FLINT_ASSERT(bits == G->bits); fmpz_init(c); fmpz_init(gamma); fmpz_init(modulus); nmod_mpoly_ctx_init(ctxp, ctx->minfo->nvars, ORD_LEX, 2); nmod_mpoly_init3(Ap, 0, bits, ctxp); nmod_mpoly_init3(Bp, 0, bits, ctxp); nmod_mpoly_init3(Gp, 0, bits, ctxp); nmod_mpoly_init3(Abarp, 0, bits, ctxp); nmod_mpoly_init3(Bbarp, 0, bits, ctxp); n_poly_init(Amarks); n_poly_init(Bmarks); n_poly_init(Gmarks); fmpz_gcd(gamma, fmpz_mpoly_leadcoeff(A), fmpz_mpoly_leadcoeff(B)); Gdegbound = fmpz_mpoly_degree_si(A, 0, ctx); Gdeg = fmpz_mpoly_degree_si(B, 0, ctx); Gdegbound = FLINT_MIN(Gdegbound, Gdeg); coeffbitbound = fmpz_mpoly_gcd_bitbound(A, B, ctx); if (n_add_checked(&coeffbitbound, coeffbitbound, fmpz_bits(gamma))) coeffbitbound = UWORD_MAX; p = UWORD(1) << (FLINT_BITS - 1); outer_loop: if (p >= UWORD_MAX_PRIME) { /* ran out of primes: absolute failure */ success = 0; goto cleanup; } p = n_nextprime(p, 1); nmod_mpoly_ctx_change_modulus(ctxp, p); /* make sure mod p reduction does not kill both lc(A) and lc(B) */ gammap = fmpz_get_nmod(gamma, ctxp->mod); if (gammap == 0) goto outer_loop; /* make sure mod p reduction does not kill either A or B */ fmpz_mpoly_interp_reduce_p(Ap, ctxp, A, ctx); fmpz_mpoly_interp_reduce_p(Bp, ctxp, B, ctx); if (Ap->length == 0 || Bp->length == 0) goto outer_loop; success = nmod_mpolyl_gcdp_zippel_smprime(Gp, Abarp, Bbarp, Ap, Bp, ctx->minfo->nvars - 1, ctxp, state); if (!success) goto outer_loop; Gdeg = nmod_mpoly_degree_si(Gp, 0, ctxp); if (Gdeg > Gdegbound) goto outer_loop; if (Gdeg == 0 && nmod_mpoly_is_one(Gp, ctxp)) { fmpz_mpoly_one(G, ctx); fmpz_mpoly_set(Abar, A, ctx); fmpz_mpoly_set(Bbar, B, ctx); success = 1; goto cleanup; } Gdegbound = Gdeg; t = nmod_div(gammap, nmod_mpoly_leadcoeff(Gp, ctxp), ctxp->mod); nmod_mpoly_scalar_mul_nmod_invertible(Gp, Gp, t, ctxp); fmpz_mpoly_interp_lift_p(G, ctx, Gp, ctxp); fmpz_set_ui(modulus, p); /* TODO: a divisibility test here if coeffs are small */ mpoly1_fill_marks(&Gmarks->coeffs, &Gmarks->length, &Gmarks->alloc, G->exps, G->length, bits, ctx->minfo); perm = FLINT_ARRAY_REALLOC(perm, Gmarks->length, slong); for (i = 0; i < Gmarks->length; i++) perm[i] = i; #define length(k) Gmarks->coeffs[(k)+1] - Gmarks->coeffs[k] for (i = 1; i < Gmarks->length; i++) for (j = i; j > 0 && length(perm[j-1]) > length(perm[j]); j--) SLONG_SWAP(perm[j-1], perm[j]); req_zip_images = Gmarks->length - 2; j = 0; for (i = 0; i < Gmarks->length; i++) { req_zip_images += length(i); j = FLINT_MAX(j, length(i)); } if (Gmarks->length > 1) req_zip_images = req_zip_images / (Gmarks->length - 1); req_zip_images = FLINT_MAX(req_zip_images, j); req_zip_images += 1; inner_loop: if (p >= UWORD_MAX_PRIME) { /* ran out of primes: absolute failure */ success = 0; goto cleanup; } p = n_nextprime(p, 1); nmod_mpoly_ctx_change_modulus(ctxp, p); /* make sure mod p reduction does not kill both lc(A) and lc(B) */ gammap = fmpz_get_nmod(gamma, ctxp->mod); if (gammap == 0) goto inner_loop; /* make sure mod p reduction does not kill either A or B */ fmpz_mpoly_interp_reduce_p(Ap, ctxp, A, ctx); fmpz_mpoly_interp_reduce_p(Bp, ctxp, B, ctx); if (Ap->length == 0 || Bp->length == 0) goto inner_loop; success = nmod_mpolyl_gcds_zippel(Gp, Gmarks->coeffs, Gmarks->length, Ap, Bp, perm, req_zip_images, ctx->minfo->nvars, ctxp, state, &Gdegbound, Amarks, Bmarks); if (success == 0) goto outer_loop; /* resets modulus */ if (success < 0 || nmod_mpoly_leadcoeff(Gp, ctxp) == 0) goto inner_loop; t = nmod_div(gammap, nmod_mpoly_leadcoeff(Gp, ctxp), ctxp->mod); nmod_mpoly_scalar_mul_nmod_invertible(Gp, Gp, t, ctxp); changed = fmpz_mpoly_interp_mcrt_p(&coeffbits, G, ctx, modulus, Gp, ctxp); fmpz_mul_ui(modulus, modulus, p); if (changed) { if (coeffbits > coeffbitbound) goto outer_loop; /* resets modulus */ goto inner_loop; } _fmpz_vec_content(c, G->coeffs, G->length); _fmpz_vec_scalar_divexact_fmpz(G->coeffs, G->coeffs, G->length, c); success = fmpz_mpoly_divides(Abar, A, G, ctx) && fmpz_mpoly_divides(Bbar, B, G, ctx); if (success) goto cleanup; /* restore interpolated state */ _fmpz_vec_scalar_mul_fmpz(G->coeffs, G->coeffs, G->length, c); goto inner_loop; cleanup: flint_free(perm); n_poly_clear(Amarks); n_poly_clear(Bmarks); n_poly_clear(Gmarks); nmod_mpoly_clear(Ap, ctxp); nmod_mpoly_clear(Bp, ctxp); nmod_mpoly_clear(Gp, ctxp); nmod_mpoly_clear(Abarp, ctxp); nmod_mpoly_clear(Bbarp, ctxp); nmod_mpoly_ctx_clear(ctxp); fmpz_clear(c); fmpz_clear(gamma); fmpz_clear(modulus); return success; }