From 4a72a3790ceafeed6aa4ff939b552ac03b85109b Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 15 Jul 2024 20:21:59 +1000 Subject: [PATCH 01/29] Initial addition of fmpz_mod_mpoly and nmod_mpoly fmpz_mod_mpoly.pyx to come in a later commit --- src/flint/__init__.py | 2 + src/flint/flintlib/flint.pxd | 5 + src/flint/flintlib/fmpz_mod_mpoly.pxd | 176 ++++++++++++++++++++++++++ src/flint/flintlib/nmod_mpoly.pxd | 159 +++++++++++++++++++++++ src/flint/types/fmpz_mod_mpoly.pxd | 35 +++++ src/flint/types/meson.build | 2 + src/flint/types/nmod_mpoly.pxd | 35 +++++ src/flint/types/nmod_mpoly.pyx | 26 ++++ 8 files changed, 440 insertions(+) create mode 100644 src/flint/flintlib/fmpz_mod_mpoly.pxd create mode 100644 src/flint/flintlib/nmod_mpoly.pxd create mode 100644 src/flint/types/fmpz_mod_mpoly.pxd create mode 100644 src/flint/types/nmod_mpoly.pxd create mode 100644 src/flint/types/nmod_mpoly.pyx diff --git a/src/flint/__init__.py b/src/flint/__init__.py index e71bb15c..af81d4d8 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -14,12 +14,14 @@ from .types.nmod import * from .types.nmod_poly import * +from .types.nmod_mpoly import nmod_mpoly_ctx, nmod_mpoly, fmpz_mod_mpoly_vec from .types.nmod_mat import * from .types.nmod_series import * from .types.fmpz_mpoly import fmpz_mpoly_ctx, fmpz_mpoly, fmpz_mpoly_vec from .types.fmpz_mod import * from .types.fmpz_mod_poly import * +from .types.fmpz_mod_mpoly import fmpz_mod_mpoly_ctx, fmpz_mod_mpoly, fmpz_mod_mpoly_vec from .types.fmpz_mod_mat import fmpz_mod_mat from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly, fmpq_mpoly_vec diff --git a/src/flint/flintlib/flint.pxd b/src/flint/flintlib/flint.pxd index 65ed5d84..993a61e3 100644 --- a/src/flint/flintlib/flint.pxd +++ b/src/flint/flintlib/flint.pxd @@ -60,6 +60,11 @@ cdef extern from "flint/flint.h": long flint_get_num_threads() void flint_cleanup() + ctypedef struct nmod_t: + mp_limb_t n + mp_limb_t ninv + flint_bitcnt_t norm + cdef extern from *: """ /* FLINT_BITS is not known until C compile time. We need to check if long diff --git a/src/flint/flintlib/fmpz_mod_mpoly.pxd b/src/flint/flintlib/fmpz_mod_mpoly.pxd new file mode 100644 index 00000000..bb00391b --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_mpoly.pxd @@ -0,0 +1,176 @@ +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.flint cimport flint_bitcnt_t, ulong, mp_limb_t, slong, fmpz_struct, flint_rand_t +from flint.flintlib.fmpz_poly cimport fmpz_poly_struct, fmpz_poly_t +from flint.flintlib.mpoly cimport mpoly_ctx_t, ordering_t +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t + +cdef extern from "flint/fmpz_mod_mpoly.h": + ctypedef struct fmpz_mod_mpoly_ctx_struct: + mpoly_ctx_t minfo + fmpz_mod_ctx_t ffinfo + + ctypedef fmpz_mod_mpoly_ctx_struct fmpz_mod_mpoly_ctx_t[1] + + ctypedef struct fmpz_mod_mpoly_struct: + fmpz_struct * coeffs + ulong * exps + slong length + flint_bitcnt_t bits + slong coeffs_alloc + slong exps_alloc + + ctypedef fmpz_mod_mpoly_struct fmpz_mod_mpoly_t[1] + + ctypedef struct fmpz_mod_mpoly_univar_struct: + fmpz_mod_mpoly_struct * coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef fmpz_mod_mpoly_univar_struct fmpz_mod_mpoly_univar_t[1] + +# from here on is parsed + void fmpz_mod_mpoly_ctx_init(fmpz_mod_mpoly_ctx_t ctx, slong nvars, const ordering_t ord, const fmpz_t p) + slong fmpz_mod_mpoly_ctx_nvars(const fmpz_mod_mpoly_ctx_t ctx) + ordering_t fmpz_mod_mpoly_ctx_ord(const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_ctx_get_modulus(fmpz_t n, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_ctx_clear(fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_init(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_init2(fmpz_mod_mpoly_t A, slong alloc, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_init3(fmpz_mod_mpoly_t A, slong alloc, flint_bitcnt_t bits, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_clear(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + char * fmpz_mod_mpoly_get_str_pretty(const fmpz_mod_mpoly_t A, const char ** x, const fmpz_mod_mpoly_ctx_t ctx) + # int fmpz_mod_mpoly_fprint_pretty(FILE * file, const fmpz_mod_mpoly_t A, const char ** x, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_print_pretty(const fmpz_mod_mpoly_t A, const char ** x, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_set_str_pretty(fmpz_mod_mpoly_t A, const char * str, const char ** x, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_gen(fmpz_mod_mpoly_t A, slong var, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_gen(const fmpz_mod_mpoly_t A, slong var, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_equal(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_swap(fmpz_mod_mpoly_t poly1, fmpz_mod_mpoly_t poly2, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_fmpz(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_fmpz(fmpz_t c, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_fmpz(fmpz_mod_mpoly_t A, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_ui(fmpz_mod_mpoly_t A, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_si(fmpz_mod_mpoly_t A, slong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_zero(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_one(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_equal_fmpz(const fmpz_mod_mpoly_t A, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_equal_ui(const fmpz_mod_mpoly_t A, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_equal_si(const fmpz_mod_mpoly_t A, slong c, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_zero(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_one(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_degrees_fit_si(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_degrees_fmpz(fmpz_struct ** degs, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_degrees_si(slong * degs, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_degree_fmpz(fmpz_t deg, const fmpz_mod_mpoly_t A, slong var, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_degree_si(const fmpz_mod_mpoly_t A, slong var, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_total_degree_fits_si(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_total_degree_fmpz(fmpz_t tdeg, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_total_degree_si(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_used_vars(int * used, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_coeff_fmpz_monomial(fmpz_t c, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t M, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_fmpz_monomial(fmpz_mod_mpoly_t A, const fmpz_t c, const fmpz_mod_mpoly_t M, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_coeff_fmpz_fmpz(fmpz_t c, const fmpz_mod_mpoly_t A, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_coeff_fmpz_ui(fmpz_t c, const fmpz_mod_mpoly_t A, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_fmpz_fmpz(fmpz_mod_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_ui_fmpz(fmpz_mod_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_si_fmpz(fmpz_mod_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_fmpz_ui(fmpz_mod_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_ui_ui(fmpz_mod_mpoly_t A, ulong c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_coeff_si_ui(fmpz_mod_mpoly_t A, slong c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_coeff_vars_ui(fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_cmp(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_canonical(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_length(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_resize(fmpz_mod_mpoly_t A, slong new_length, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term_coeff_fmpz(fmpz_t c, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_term_coeff_fmpz(fmpz_mod_mpoly_t A, slong i, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_term_coeff_ui(fmpz_mod_mpoly_t A, slong i, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_term_coeff_si(fmpz_mod_mpoly_t A, slong i, slong c, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_term_exp_fits_si(const fmpz_mod_mpoly_t poly, slong i, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_term_exp_fits_ui(const fmpz_mod_mpoly_t poly, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term_exp_fmpz(fmpz_struct ** exp, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term_exp_ui(ulong * exp, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term_exp_si(slong * exp, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + ulong fmpz_mod_mpoly_get_term_var_exp_ui(const fmpz_mod_mpoly_t A, slong i, slong var, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_get_term_var_exp_si(const fmpz_mod_mpoly_t A, slong i, slong var, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_term_exp_fmpz(fmpz_mod_mpoly_t A, slong i, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_set_term_exp_ui(fmpz_mod_mpoly_t A, slong i, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term(fmpz_mod_mpoly_t M, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_get_term_monomial(fmpz_mod_mpoly_t M, const fmpz_mod_mpoly_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_fmpz_fmpz(fmpz_mod_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_fmpz_ffmpz(fmpz_mod_mpoly_t A, const fmpz_t c, const fmpz_struct * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_ui_fmpz(fmpz_mod_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_ui_ffmpz(fmpz_mod_mpoly_t A, ulong c, const fmpz_struct * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_si_fmpz(fmpz_mod_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_si_ffmpz(fmpz_mod_mpoly_t A, slong c, const fmpz_struct * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_fmpz_ui(fmpz_mod_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_ui_ui(fmpz_mod_mpoly_t A, ulong c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_push_term_si_ui(fmpz_mod_mpoly_t A, slong c, const ulong * exp, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_sort_terms(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_combine_like_terms(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_reverse(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_randtest_bound(fmpz_mod_mpoly_t A, flint_rand_t state, slong length, ulong exp_bound, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_randtest_bounds(fmpz_mod_mpoly_t A, flint_rand_t state, slong length, ulong * exp_bounds, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_randtest_bits(fmpz_mod_mpoly_t A, flint_rand_t state, slong length, mp_limb_t exp_bits, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_add_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_add_ui(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_add_si(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_sub_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_sub_ui(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_sub_si(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_add(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_sub(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_neg(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_scalar_mul_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_t c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_scalar_mul_ui(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, ulong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_scalar_mul_si(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong c, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_scalar_addmul_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_t d, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_make_monic(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_derivative(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong var, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_evaluate_all_fmpz(fmpz_t eval, const fmpz_mod_mpoly_t A, fmpz_struct * const * vals, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_evaluate_one_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong var, const fmpz_t val, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_compose_fmpz_poly(fmpz_poly_t A, const fmpz_mod_mpoly_t B, fmpz_poly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB) + int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) + int fmpz_mod_mpoly_compose_fmpz_mod_mpoly(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) + void fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const slong * c, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) + void fmpz_mod_mpoly_mul(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_mul_johnson(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_mul_dense(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_t C, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_pow_fmpz(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_t k, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_pow_ui(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, ulong k, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_divides(fmpz_mod_mpoly_t Q, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_div(fmpz_mod_mpoly_t Q, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_divrem(fmpz_mod_mpoly_t Q, fmpz_mod_mpoly_t R, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_divrem_ideal(fmpz_mod_mpoly_struct ** Q, fmpz_mod_mpoly_t R, const fmpz_mod_mpoly_t A, fmpz_mod_mpoly_struct * const * B, slong len, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_term_content(fmpz_mod_mpoly_t M, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_content_vars(fmpz_mod_mpoly_t g, const fmpz_mod_mpoly_t A, slong * vars, slong vars_length, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_cofactors(fmpz_mod_mpoly_t G, fmpz_mod_mpoly_t Abar, fmpz_mod_mpoly_t Bbar, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_brown(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_hensel(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_subresultant(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_zippel(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_gcd_zippel2(fmpz_mod_mpoly_t G, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_resultant(fmpz_mod_mpoly_t R, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, slong var, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_discriminant(fmpz_mod_mpoly_t D, const fmpz_mod_mpoly_t A, slong var, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_sqrt(fmpz_mod_mpoly_t Q, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_is_square(const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_quadratic_root(fmpz_mod_mpoly_t Q, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_init(fmpz_mod_mpoly_univar_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_clear(fmpz_mod_mpoly_univar_t A, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_swap(fmpz_mod_mpoly_univar_t A, fmpz_mod_mpoly_univar_t B, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_to_univar(fmpz_mod_mpoly_univar_t A, const fmpz_mod_mpoly_t B, slong var, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_from_univar(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_univar_t B, slong var, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_univar_degree_fits_si(const fmpz_mod_mpoly_univar_t A, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_univar_length(const fmpz_mod_mpoly_univar_t A, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_univar_get_term_exp_si(fmpz_mod_mpoly_univar_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_get_term_coeff(fmpz_mod_mpoly_t c, const fmpz_mod_mpoly_univar_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_swap_term_coeff(fmpz_mod_mpoly_t c, fmpz_mod_mpoly_univar_t A, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_univar_set_coeff_ui(fmpz_mod_mpoly_univar_t Ax, ulong e, const fmpz_mod_mpoly_t c, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_univar_resultant(fmpz_mod_mpoly_t R, const fmpz_mod_mpoly_univar_t Ax, const fmpz_mod_mpoly_univar_t Bx, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_univar_discriminant(fmpz_mod_mpoly_t D, const fmpz_mod_mpoly_univar_t Ax, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_inflate(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_deflate(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/nmod_mpoly.pxd b/src/flint/flintlib/nmod_mpoly.pxd new file mode 100644 index 00000000..4185c3b1 --- /dev/null +++ b/src/flint/flintlib/nmod_mpoly.pxd @@ -0,0 +1,159 @@ +from flint.flintlib.flint cimport fmpz_struct, flint_rand_t, mp_limb_t, slong, ulong, flint_bitcnt_t, nmod_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.mpoly cimport mpoly_ctx_t, ordering_t +from flint.flintlib.nmod_poly cimport nmod_poly_struct, nmod_poly_t + + +cdef extern from "flint/nmod_mpoly.h": + ctypedef struct nmod_mpoly_struct: + mp_limb_t * coeffs + ulong * exps + slong length + flint_bitcnt_t bits + slong coeffs_alloc + slong exps_alloc + + ctypedef nmod_mpoly_struct nmod_mpoly_t[1] + + ctypedef struct nmod_mpoly_ctx_struct: + mpoly_ctx_t minfo + nmod_t mod + + ctypedef nmod_mpoly_ctx_struct nmod_mpoly_ctx_t[1] + + ctypedef struct nmod_mpoly_univar_struct: + nmod_mpoly_struct * coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef nmod_mpoly_univar_struct nmod_mpoly_univar_t[1] + +# from here on is parsed + void nmod_mpoly_ctx_init(nmod_mpoly_ctx_t ctx, slong nvars, const ordering_t ord, mp_limb_t n) + slong nmod_mpoly_ctx_nvars(const nmod_mpoly_ctx_t ctx) + ordering_t nmod_mpoly_ctx_ord(const nmod_mpoly_ctx_t ctx) + mp_limb_t nmod_mpoly_ctx_modulus(const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_ctx_clear(nmod_mpoly_ctx_t ctx) + void nmod_mpoly_init(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_init2(nmod_mpoly_t A, slong alloc, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_init3(nmod_mpoly_t A, slong alloc, flint_bitcnt_t bits, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_fit_length(nmod_mpoly_t A, slong len, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_realloc(nmod_mpoly_t A, slong alloc, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_clear(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + char * nmod_mpoly_get_str_pretty(const nmod_mpoly_t A, const char ** x, const nmod_mpoly_ctx_t ctx) + # int nmod_mpoly_fprint_pretty(FILE * file, const nmod_mpoly_t A, const char ** x, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_print_pretty(const nmod_mpoly_t A, const char ** x, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_set_str_pretty(nmod_mpoly_t A, const char * str, const char ** x, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_gen(nmod_mpoly_t A, slong var, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_gen(const nmod_mpoly_t A, slong var, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_equal(const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_swap(nmod_mpoly_t A, nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_ui(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_ui(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_ui(nmod_mpoly_t A, ulong c, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_zero(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_one(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_equal_ui(const nmod_mpoly_t A, ulong c, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_zero(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_one(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_degrees_fit_si(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_degrees_fmpz(fmpz_struct ** degs, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_degrees_si(slong * degs, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_degree_fmpz(fmpz_t deg, const nmod_mpoly_t A, slong var, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_degree_si(const nmod_mpoly_t A, slong var, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_total_degree_fits_si(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_total_degree_fmpz(fmpz_t tdeg, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_total_degree_si(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_used_vars(int * used, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_coeff_ui_monomial(const nmod_mpoly_t A, const nmod_mpoly_t M, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_coeff_ui_monomial(nmod_mpoly_t A, ulong c, const nmod_mpoly_t M, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_coeff_ui_fmpz(const nmod_mpoly_t A, fmpz_struct * const * exp, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_coeff_ui_ui(const nmod_mpoly_t A, const ulong * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_coeff_ui_fmpz(nmod_mpoly_t A, ulong c, fmpz_struct * const * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_coeff_ui_ui(nmod_mpoly_t A, ulong c, const ulong * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_coeff_vars_ui(nmod_mpoly_t C, const nmod_mpoly_t A, const slong * vars, const ulong * exps, slong length, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_cmp(const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + mp_limb_t * nmod_mpoly_term_coeff_ref(nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_canonical(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_length(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_resize(nmod_mpoly_t A, slong new_length, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_term_coeff_ui(const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_term_coeff_ui(nmod_mpoly_t A, slong i, ulong c, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_term_exp_fits_si(const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_term_exp_fits_ui(const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_term_exp_fmpz(fmpz_struct ** exp, const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_term_exp_ui(ulong * exp, const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_term_exp_si(slong * exp, const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_get_term_var_exp_ui(const nmod_mpoly_t A, slong i, slong var, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_get_term_var_exp_si(const nmod_mpoly_t A, slong i, slong var, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_term_exp_fmpz(nmod_mpoly_t A, slong i, fmpz_struct * const * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_set_term_exp_ui(nmod_mpoly_t A, slong i, const ulong * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_term(nmod_mpoly_t M, const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_get_term_monomial(nmod_mpoly_t M, const nmod_mpoly_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_push_term_ui_fmpz(nmod_mpoly_t A, ulong c, fmpz_struct * const * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_push_term_ui_ffmpz(nmod_mpoly_t A, ulong c, const fmpz_struct * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_push_term_ui_ui(nmod_mpoly_t A, ulong c, const ulong * exp, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_sort_terms(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_combine_like_terms(nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_reverse(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_randtest_bound(nmod_mpoly_t A, flint_rand_t state, slong length, ulong exp_bound, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_randtest_bounds(nmod_mpoly_t A, flint_rand_t state, slong length, ulong * exp_bounds, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_randtest_bits(nmod_mpoly_t A, flint_rand_t state, slong length, mp_limb_t exp_bits, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_add_ui(nmod_mpoly_t A, const nmod_mpoly_t B, ulong c, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_sub_ui(nmod_mpoly_t A, const nmod_mpoly_t B, ulong c, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_add(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_sub(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_neg(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_scalar_mul_ui(nmod_mpoly_t A, const nmod_mpoly_t B, ulong c, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_make_monic(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_derivative(nmod_mpoly_t A, const nmod_mpoly_t B, slong var, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_evaluate_all_ui(const nmod_mpoly_t A, const ulong * vals, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_evaluate_one_ui(nmod_mpoly_t A, const nmod_mpoly_t B, slong var, ulong val, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_compose_nmod_poly(nmod_poly_t A, const nmod_mpoly_t B, nmod_poly_struct * const * C, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_compose_nmod_mpoly_geobucket(nmod_mpoly_t A, const nmod_mpoly_t B, nmod_mpoly_struct * const * C, const nmod_mpoly_ctx_t ctxB, const nmod_mpoly_ctx_t ctxAC) + int nmod_mpoly_compose_nmod_mpoly_horner(nmod_mpoly_t A, const nmod_mpoly_t B, nmod_mpoly_struct * const * C, const nmod_mpoly_ctx_t ctxB, const nmod_mpoly_ctx_t ctxAC) + int nmod_mpoly_compose_nmod_mpoly(nmod_mpoly_t A, const nmod_mpoly_t B, nmod_mpoly_struct * const * C, const nmod_mpoly_ctx_t ctxB, const nmod_mpoly_ctx_t ctxAC) + void nmod_mpoly_compose_nmod_mpoly_gen(nmod_mpoly_t A, const nmod_mpoly_t B, const slong * c, const nmod_mpoly_ctx_t ctxB, const nmod_mpoly_ctx_t ctxAC) + void nmod_mpoly_mul(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_mul_johnson(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_mul_heap_threaded(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_mul_array(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_mul_array_threaded(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_mul_dense(nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_t C, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_pow_fmpz(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_t k, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_pow_ui(nmod_mpoly_t A, const nmod_mpoly_t B, ulong k, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_divides(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_div(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_divrem(nmod_mpoly_t Q, nmod_mpoly_t R, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_divrem_ideal(nmod_mpoly_struct ** Q, nmod_mpoly_t R, const nmod_mpoly_t A, nmod_mpoly_struct * const * B, slong len, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_divides_dense(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_divides_monagan_pearce(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_divides_heap_threaded(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_term_content(nmod_mpoly_t M, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_content_vars(nmod_mpoly_t g, const nmod_mpoly_t A, slong * vars, slong vars_length, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_gcd(nmod_mpoly_t G, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_gcd_cofactors(nmod_mpoly_t G, nmod_mpoly_t Abar, nmod_mpoly_t Bbar, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_gcd_brown(nmod_mpoly_t G, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_gcd_hensel(nmod_mpoly_t G, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_gcd_zippel(nmod_mpoly_t G, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_resultant(nmod_mpoly_t R, const nmod_mpoly_t A, const nmod_mpoly_t B, slong var, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_discriminant(nmod_mpoly_t D, const nmod_mpoly_t A, slong var, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_sqrt(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_is_square(const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_quadratic_root(nmod_mpoly_t Q, const nmod_mpoly_t A, const nmod_mpoly_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_univar_init(nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_univar_clear(nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_univar_swap(nmod_mpoly_univar_t A, nmod_mpoly_univar_t B, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_to_univar(nmod_mpoly_univar_t A, const nmod_mpoly_t B, slong var, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_from_univar(nmod_mpoly_t A, const nmod_mpoly_univar_t B, slong var, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_univar_degree_fits_si(const nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_univar_length(const nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_univar_get_term_exp_si(nmod_mpoly_univar_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_univar_get_term_coeff(nmod_mpoly_t c, const nmod_mpoly_univar_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_univar_swap_term_coeff(nmod_mpoly_t c, nmod_mpoly_univar_t A, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_pow_rmul(nmod_mpoly_t A, const nmod_mpoly_t B, ulong k, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_div_monagan_pearce(nmod_mpoly_t polyq, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_divrem_monagan_pearce(nmod_mpoly_t q, nmod_mpoly_t r, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_divrem_ideal_monagan_pearce(nmod_mpoly_struct ** q, nmod_mpoly_t r, const nmod_mpoly_t poly2, nmod_mpoly_struct * const * poly3, slong len, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/types/fmpz_mod_mpoly.pxd b/src/flint/types/fmpz_mod_mpoly.pxd new file mode 100644 index 00000000..643f1b52 --- /dev/null +++ b/src/flint/types/fmpz_mod_mpoly.pxd @@ -0,0 +1,35 @@ +from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context + +from flint.flintlib.fmpz_mod_mpoly cimport ( + fmpz_mod_mpoly_ctx_t, + fmpz_mod_mpoly_t, + fmpz_mod_mpoly_init, + fmpz_mod_mpoly_struct +) +from flint.flintlib.flint cimport slong + +cdef inline init_fmpz_mod_mpoly(fmpz_mod_mpoly var, fmpz_mod_mpoly_ctx ctx): + var.ctx = ctx + fmpz_mod_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline fmpz_mod_mpoly create_fmpz_mod_mpoly(fmpz_mod_mpoly_ctx ctx): + cdef fmpz_mod_mpoly var + var = fmpz_mod_mpoly.__new__(fmpz_mod_mpoly) + var.ctx = ctx + fmpz_mod_mpoly_init(var.val, ctx.val) + var._init = True + return var + +cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): + cdef fmpz_mod_mpoly_ctx_t val + +cdef class fmpz_mod_mpoly(flint_mpoly): + cdef fmpz_mod_mpoly_t val + cdef fmpz_mod_mpoly_ctx ctx + cdef bint _init + +cdef class fmpz_mod_mpoly_vec: + cdef fmpz_mod_mpoly_t *val + cdef fmpz_mod_mpoly_ctx ctx + cdef fmpz_mod_mpoly_struct **double_indirect diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index 4dbfcd35..aead0251 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -19,6 +19,7 @@ exts = [ 'nmod', 'nmod_poly', + 'nmod_mpoly', 'nmod_mat', 'nmod_series', @@ -41,6 +42,7 @@ exts = [ 'dirichlet', 'fmpz_mpoly', + 'fmpz_mod_mpoly', 'fmpq_mpoly', ] diff --git a/src/flint/types/nmod_mpoly.pxd b/src/flint/types/nmod_mpoly.pxd new file mode 100644 index 00000000..26d4dce8 --- /dev/null +++ b/src/flint/types/nmod_mpoly.pxd @@ -0,0 +1,35 @@ +from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context + +from flint.flintlib.nmod_mpoly cimport ( + nmod_mpoly_ctx_t, + nmod_mpoly_t, + nmod_mpoly_init, + nmod_mpoly_struct +) +from flint.flintlib.flint cimport slong + +cdef inline init_nmod_mpoly(nmod_mpoly var, nmod_mpoly_ctx ctx): + var.ctx = ctx + nmod_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline nmod_mpoly create_nmod_mpoly(nmod_mpoly_ctx ctx): + cdef nmod_mpoly var + var = nmod_mpoly.__new__(nmod_mpoly) + var.ctx = ctx + nmod_mpoly_init(var.val, ctx.val) + var._init = True + return var + +cdef class nmod_mpoly_ctx(flint_mpoly_context): + cdef nmod_mpoly_ctx_t val + +cdef class nmod_mpoly(flint_mpoly): + cdef nmod_mpoly_t val + cdef nmod_mpoly_ctx ctx + cdef bint _init + +cdef class nmod_mpoly_vec: + cdef nmod_mpoly_t *val + cdef nmod_mpoly_ctx ctx + cdef nmod_mpoly_struct **double_indirect diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx new file mode 100644 index 00000000..77acbec2 --- /dev/null +++ b/src/flint/types/nmod_mpoly.pyx @@ -0,0 +1,26 @@ +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + ordering_py_to_c, + ordering_c_to_py, +) + +from flint.utils.typecheck cimport typecheck +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError + +from flint.types.fmpz cimport any_as_fmpz, fmpz +from flint.types.fmpz_vec cimport fmpz_vec + +from cpython.object cimport Py_EQ, Py_NE +cimport libc.stdlib + +cdef dict _nmod_mpoly_ctx_cache = {} + +cdef class nmod_mpoly_ctx(flint_mpoly_context): + pass + +cdef class nmod_mpoly(flint_mpoly): + pass + +cdef class nmod_mpoly_vec: + pass From a82d4ed9a57538b1f63a7731aea3cdc7a72c8e43 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 15 Jul 2024 21:44:41 +1000 Subject: [PATCH 02/29] First pass at a copy-paste of fmpz_mpoly -> fmpz_mod_mpoly --- src/flint/types/fmpz_mod_mpoly.pyx | 1087 ++++++++++++++++++++++++++++ 1 file changed, 1087 insertions(+) create mode 100644 src/flint/types/fmpz_mod_mpoly.pyx diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx new file mode 100644 index 00000000..1024e12e --- /dev/null +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -0,0 +1,1087 @@ +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + ordering_py_to_c, + ordering_c_to_py, +) + +from flint.utils.typecheck cimport typecheck +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError + +from flint.types.fmpz cimport any_as_fmpz, fmpz +from flint.types.fmpz_vec cimport fmpz_vec + +from flint.flintlib.fmpz cimport fmpz_set +from flint.flintlib.fmpz_mod_mpoly cimport ( + fmpz_mod_mpoly_add, + fmpz_mod_mpoly_add_fmpz, + fmpz_mod_mpoly_clear, + fmpz_mod_mpoly_compose_fmpz_mod_mpoly, + fmpz_mod_mpoly_ctx_init, + fmpz_mod_mpoly_degrees_fmpz, + fmpz_mod_mpoly_derivative, + fmpz_mod_mpoly_div, + fmpz_mod_mpoly_divides, + fmpz_mod_mpoly_divrem, + fmpz_mod_mpoly_equal, + fmpz_mod_mpoly_evaluate_all_fmpz, + fmpz_mod_mpoly_evaluate_one_fmpz, + fmpz_mod_mpoly_gcd, + fmpz_mod_mpoly_gen, + fmpz_mod_mpoly_get_coeff_fmpz_fmpz, + fmpz_mod_mpoly_get_str_pretty, + fmpz_mod_mpoly_get_term, + fmpz_mod_mpoly_get_term_coeff_fmpz, + fmpz_mod_mpoly_get_term_exp_fmpz, + fmpz_mod_mpoly_is_one, + fmpz_mod_mpoly_is_zero, + fmpz_mod_mpoly_length, + fmpz_mod_mpoly_mul, + fmpz_mod_mpoly_neg, + fmpz_mod_mpoly_pow_fmpz, + fmpz_mod_mpoly_push_term_fmpz_ffmpz, + fmpz_mod_mpoly_scalar_mul_fmpz, + fmpz_mod_mpoly_set, + fmpz_mod_mpoly_set_coeff_fmpz_fmpz, + fmpz_mod_mpoly_set_fmpz, + fmpz_mod_mpoly_set_str_pretty, + fmpz_mod_mpoly_sort_terms, + fmpz_mod_mpoly_sub, + fmpz_mod_mpoly_sub_fmpz, + fmpz_mod_mpoly_total_degree_fmpz, +) +from flint.flintlib.fmpz_mod_mpoly_factor cimport ( + fmpz_mod_mpoly_factor, + fmpz_mod_mpoly_factor_clear, + fmpz_mod_mpoly_factor_init, + fmpz_mod_mpoly_factor_squarefree, + fmpz_mod_mpoly_factor_t, +) + +from cpython.object cimport Py_EQ, Py_NE +cimport libc.stdlib + +cdef dict _fmpz_mod_mpoly_ctx_cache = {} + + +cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :param ordering: The term order for the ring + :param names: A tuple containing the names of the variables of the ring. + + Do not construct one of these directly, use `fmpz_mod_mpoly_ctx.get_context`. + """ + + _ctx_cache = _fmpz_mod_mpoly_ctx_cache + + def __init__(self, slong nvars, ordering, names): + fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) + super().__init__(nvars, names) + + def nvars(self): + """ + Return the number of variables in the context + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx.nvars() + 4 + """ + return self.val.minfo.nvars + + def ordering(self): + """ + Return the term order of the context object. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx.ordering() + + """ + return ordering_c_to_py(self.val.minfo.ord) + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx.gen(1) + z1 + """ + cdef fmpz_mod_mpoly res + if not 0 <= i < self.val.minfo.nvars: + raise IndexError("generator index out of range") + res = create_fmpz_mod_mpoly(self) + fmpz_mod_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef fmpz_mod_mpoly res + z = any_as_fmpz(z) + if z is NotImplemented: + raise TypeError("cannot coerce argument to fmpz") + res = create_fmpz_mod_mpoly(self) + fmpz_mod_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) + return res + + def from_dict(self, d): + """ + Create a fmpz_mod_mpoly from a dictionary in this context. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef: + fmpz_vec exp_vec + slong i, nvars = self.nvars() + fmpz_mod_mpoly res + + if not isinstance(d, dict): + raise ValueError("expected a dictionary") + + res = create_fmpz_mod_mpoly(self) + + for i, (k, v) in enumerate(d.items()): + o = any_as_fmpz(v) + if o is NotImplemented: + raise TypeError(f"cannot coerce coefficient '{v}' to fmpz") + elif len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + + exp_vec = fmpz_vec(k) + + if o: + fmpz_mod_mpoly_push_term_fmpz_ffmpz(res.val, (o).val, exp_vec.val, self.val) + + fmpz_mod_mpoly_sort_terms(res.val, self.val) + return res + + +cdef class fmpz_mod_mpoly(flint_mpoly): + """ + The *fmpz_mod_mpoly* type represents sparse multivariate polynomials over + the integers. + """ + + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + fmpz_mod_mpoly_clear(self.val, self.ctx.val) + self._init = False + + def __init__(self, val=0, ctx=None): + if typecheck(val, fmpz_mod_mpoly): + if ctx is None or ctx == (val).ctx: + init_fmpz_mod_mpoly(self, (val).ctx) + fmpz_mod_mpoly_set(self.val, (val).val, self.ctx.val) + else: + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") + elif isinstance(val, dict): + if ctx is None: + raise ValueError("a context is required to create a fmpz_mod_mpoly from a dict") + x = ctx.from_dict(val) + # XXX: this copy is silly, have a ctx function that assigns an fmpz_mod_mpoly_t + init_fmpz_mod_mpoly(self, ctx) + fmpz_mod_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + init_fmpz_mod_mpoly(self, ctx) + if fmpz_mod_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("unable to parse fmpz_mod_mpoly from string") + fmpz_mod_mpoly_sort_terms(self.val, self.ctx.val) + else: + v = any_as_fmpz(val) + if v is NotImplemented: + raise TypeError("cannot create fmpz_mod_mpoly from type %s" % type(val)) + if ctx is None: + raise ValueError("need context to convert fmpz to fmpz_mod_mpoly") + init_fmpz_mod_mpoly(self, ctx) + fmpz_mod_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) + + def __bool__(self): + return not fmpz_mod_mpoly_is_zero(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + if not (op == Py_EQ or op == Py_NE): + return NotImplemented + elif other is None: + return op == Py_NE + elif typecheck(self, fmpz_mod_mpoly) and typecheck(other, fmpz_mod_mpoly): + if (self).ctx is (other).ctx: + return (op == Py_NE) ^ bool( + fmpz_mod_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) + else: + return op == Py_NE + else: + return NotImplemented + + def __len__(self): + return fmpz_mod_mpoly_length(self.val, self.ctx.val) + + def __getitem__(self, x): + """ + Return the coefficient of the term with the exponent vector `x`. + Always returns a value, missing keys will return `0`. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] + 3 + + """ + cdef: + slong nvars = self.ctx.nvars() + + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + res = fmpz() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mod_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) + return res + + def __setitem__(self, x, y): + """ + Set the coefficient of the term with the exponent vector `x` to `y`. + Will always set a value, missing keys will create a new term. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 2*x1 + + """ + cdef: + slong nvars = self.ctx.nvars() + + coeff = any_as_fmpz(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpz") + elif not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) + + def __neg__(self): + cdef fmpz_mod_mpoly res + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_neg(res.val, (self).val, res.ctx.val) + return res + + def __add__(self, other): + cdef fmpz_mod_mpoly res + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res + return NotImplemented + + def __radd__(self, other): + cdef fmpz_mod_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res + return NotImplemented + + def __sub__(self, other): + cdef fmpz_mod_mpoly res + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res + return NotImplemented + + def __rsub__(self, other): + cdef fmpz_mod_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + + def __mul__(self, other): + cdef fmpz_mod_mpoly res + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rmul__(self, other): + cdef fmpz_mod_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __pow__(self, other, modulus): + cdef fmpz_mod_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpz_mod_mpoly to negative power") + res = create_fmpz_mod_mpoly(self.ctx) + if fmpz_mod_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def __divmod__(self, other): + cdef fmpz_mod_mpoly res, res2 + if typecheck(other, fmpz_mod_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + res2 = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mod_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + res = create_fmpz_mod_mpoly(self.ctx) + res2 = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef fmpz_mod_mpoly res, res2 + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + res2 = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __floordiv__(self, other): + cdef fmpz_mod_mpoly res + if typecheck(other, fmpz_mod_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rfloordiv__(self, other): + cdef fmpz_mod_mpoly res + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res + return NotImplemented + + def __truediv__(self, other): + cdef: + fmpz_mod_mpoly res, div + + if typecheck(other, fmpz_mod_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_fmpz_mod_mpoly(self.ctx) + if fmpz_mod_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mod_mpoly division is not exact") + else: + o = any_as_fmpz(other) + if o is NotImplemented: + return NotImplemented + elif not o: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + res = create_fmpz_mod_mpoly(self.ctx) + div = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set_fmpz(div.val, (o).val, self.ctx.val) + if fmpz_mod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mod_mpoly division is not exact") + + def __rtruediv__(self, other): + cdef fmpz_mod_mpoly res + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + o = any_as_fmpz(other) + if o is NotImplemented: + return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set_fmpz(res.val, (o).val, self.ctx.val) + return res / self + + def __mod__(self, other): + return divmod(self, other)[1] + + def __rmod__(self, other): + return divmod(other, self)[1] + + def __call__(self, *args) -> fmpz: + cdef: + fmpz_vec V + fmpz vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + + args_fmpz = tuple(any_as_fmpz(v) for v in args) + for arg in args_fmpz: + if arg is NotImplemented: + raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") + + V = fmpz_vec(args_fmpz, double_indirect=True) + vres = fmpz.__new__(fmpz) + if fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return vres + + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return + else: + zval = any_as_fmpz(other) + if zval is not NotImplemented: + fmpz_mod_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) + return + raise NotImplementedError() + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mod_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, fmpz_mod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mod_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + + def monoms(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.monoms() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + res = [] + for i in range(len(self)): + fmpz_mod_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) + res.append(vec.to_tuple()) + + return res + + def coeffs(self): + """ + Return the coefficients of each term as a fmpz + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.coeffs() + [4, 2, 3, 1] + + """ + cdef: + fmpz coeff + slong i + + res = [] + for i in range(len(self)): + coeff = fmpz.__new__(fmpz) + fmpz_mod_mpoly_get_term_coeff_fmpz(coeff.val, self.val, i, self.ctx.val) + res.append(coeff) + + return res + + # def terms(self): + # """ + # Return the terms of this polynomial as a list of fmpz_mod_mpolys. + + # >>> from flint import Ordering + # >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] + + # """ + # cdef: + # fmpz_mod_mpoly term + # slong i + + # res = [] + # for i in range(len(self)): + # term = create_fmpz_mod_mpoly(self.ctx) + # fmpz_mod_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) + + # return res + + def subs(self, dict_args) -> fmpz_mod_mpoly: + """ + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, + all values must be fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + + """ + cdef: + fmpz_mod_mpoly res + slong i + + args = tuple((self.ctx.variable_to_index(k), any_as_fmpz(v)) for k, v in dict_args.items()) + for _, v in args: + if v is NotImplemented: + raise TypeError(f"cannot coerce argument ('{v}') to fmpz") + + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_fmpz_mod_mpoly(self.ctx) + + fmpz_mod_mpoly_set(res.val, self.val, self.ctx.val) + for i, arg in args: + if fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def compose(self, *args, ctx=None) -> fmpz_mod_mpoly: + """ + Compose this polynomial with other fmpz_mod_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(1, Ordering.lex, 'x') + >>> ctx1 = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + + """ + cdef: + fmpz_mod_mpoly res + fmpz_mod_mpoly_ctx res_ctx + fmpz_mod_mpoly_vec C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") + elif not all(typecheck(arg, fmpz_mod_mpoly) for arg in args): + raise TypeError("all arguments must be fmpz_mod_mpolys") + + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, fmpz_mod_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a fmpz_mod_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) + + C = fmpz_mod_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_fmpz_mod_mpoly(res_ctx) + if fmpz_mod_mpoly_compose_fmpz_mod_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def context(self): + """ + Return the context object for this polynomials. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return fmpz_mod_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + cdef fmpz v + if not 0 <= i < fmpz_mod_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + else: + v = fmpz.__new__(fmpz) + fmpz_mod_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) + return v + + def monomial(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.monomial(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if not 0 <= i < fmpz_mod_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mod_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) + return res.to_tuple() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + (1, 2, 3, 0) + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mod_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return res.to_tuple() + + def total_degree(self): + """ + Return the total degree. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + fmpz_mod_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = fmpz_mod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + res = s.decode().replace("+", " + ").replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def gcd(self, other): + """ + Return the gcd of self and other. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) + >>> (f * g).gcd(f) + 4*x0*x1 + 1 + """ + cdef fmpz_mod_mpoly res + if not typecheck(other, fmpz_mod_mpoly): + raise TypeError("argument must be a fmpz_mod_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res + + def sqrt(self): + """ + Return the square root of self. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ + cdef fmpz_mod_mpoly res + res = create_fmpz_mod_mpoly(self.ctx) + + if fmpz_mod_mpoly_sqrt(res.val, self.val, self.ctx.val): + return res + else: + raise ValueError("polynomial is not a perfect square") + + def factor(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> from flint import Ordering + >>> Zm = fmpz_mod_mpoly + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> (p1 * p2).factor() + (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + """ + cdef: + fmpz_mod_mpoly_factor_t fac + fmpz c + fmpz_mod_mpoly u + + fmpz_mod_mpoly_factor_init(fac, self.ctx.val) + fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, fac.constant) + fmpz_mod_mpoly_factor_clear(fac, self.ctx.val) + return c, res + + def factor_squarefree(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> from flint import Ordering + >>> Zm = fmpz_mod_mpoly + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (p1 * p2).factor_squarefree() + (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) + >>> (p1 * p2 * p1).factor_squarefree() + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) + """ + cdef: + fmpz_mod_mpoly_factor_t fac + fmpz c + fmpz_mod_mpoly u + + fmpz_mod_mpoly_factor_init(fac, self.ctx.val) + fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_init(u.val, u.ctx.val) + fmpz_mod_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, fac.constant) + fmpz_mod_mpoly_factor_clear(fac, self.ctx.val) + return c, res + + # TODO: Rethink context conversions, particularly the proposed methods in #132 + # def coerce_to_context(self, ctx): + # cdef: + # fmpz_mod_mpoly res + # slong *C + # slong i + + # if not typecheck(ctx, fmpz_mod_mpoly_ctx): + # raise ValueError("provided context is not a fmpz_mod_mpoly_ctx") + + # if self.ctx is ctx: + # return self + + # C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + # if C is NULL: + # raise MemoryError("malloc returned a null pointer") + # res = create_fmpz_mod_mpoly(self.ctx) + + # vars = {x: i for i, x in enumerate(ctx.py_names)} + # for i, var in enumerate(self.ctx.py_names): + # C[i] = vars[var] + + # fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) + + # libc.stdlib.free(C) + # return res + + def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument can either be the variable as a string, or the index of the + variable in the context. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ + cdef: + fmpz_mod_mpoly res + slong i = self.ctx.variable_to_index(var) + + res = create_fmpz_mod_mpoly(self.ctx) + + fmpz_mod_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + + +cdef class fmpz_mod_mpoly_vec: + """ + A class representing a vector of fmpz_mod_mpolys. + """ + + def __cinit__(self, iterable_or_len, fmpz_mod_mpoly_ctx ctx, bint double_indirect = False): + if isinstance(iterable_or_len, int): + length = iterable_or_len + else: + length = len(iterable_or_len) + + self.ctx = ctx + fmpz_mod_mpoly_vec_init(self.val, length, self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_mod_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + for i in range(length): + self.double_indirect[i] = fmpz_mod_mpoly_vec_entry(self.val, i) + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + for i in range(self.length): + fmpz_mod_mpoly_clear(&self.val[i], self.ctx.val) + libc.stdlib.free(self.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + + cdef fmpz_mod_mpoly z = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set(z.val, f&self.val[x], self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + elif not typecheck(y, fmpz_mod_mpoly): + raise TypeError("argument is not fmpz_mod_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + fmpz_mod_mpoly_set(&self.val[x], (y).val, self.ctx.val) + + def __len__(self): + return self.val.length + + def __str__(self): + s = [None] * self.val.length + for i in range(self.val.length): + x = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set(x.val, &self.val[x], self.ctx.val) + s[i] = str(x) + return f"[{', '.join(s)}]" + + def __repr__(self): + return f"fmpz_mod_mpoly_vec({self}, ctx={self.ctx})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.val.length)) From 8c073ae84e6aa09288cd0374a04fdab80993e87f Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:36:36 +1000 Subject: [PATCH 03/29] Catch a failed factorisation, using runtime error for now The flint docs don't specify *when* this can fail so I'm not sure it fits with a DomainError or the like --- src/flint/types/fmpq_mpoly.pyx | 3 ++- src/flint/types/fmpz_mpoly.pyx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 234d7f63..3ac96a0c 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -924,7 +924,8 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly u fmpq_mpoly_factor_init(fac, self.ctx.val) - fmpq_mpoly_factor(fac, self.val, self.ctx.val) + if not fmpq_mpoly_factor(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") res = [0] * fac.num for i in range(fac.num): diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 28c6bf76..d1ebf51a 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -907,7 +907,8 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly u fmpz_mpoly_factor_init(fac, self.ctx.val) - fmpz_mpoly_factor(fac, self.val, self.ctx.val) + if not fmpz_mpoly_factor(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") res = [0] * fac.num for i in range(fac.num): From d2e8a0054606707028492a9fe18fd657e5a4cc13 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:37:45 +1000 Subject: [PATCH 04/29] Add fmpz_mod_mpoly_factor and nmod_mpoly_factor --- src/flint/flintlib/fmpz_mod_mpoly_factor.pxd | 26 ++++++++++++++++++++ src/flint/flintlib/nmod_mpoly_factor.pxd | 18 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/flint/flintlib/fmpz_mod_mpoly_factor.pxd create mode 100644 src/flint/flintlib/nmod_mpoly_factor.pxd diff --git a/src/flint/flintlib/fmpz_mod_mpoly_factor.pxd b/src/flint/flintlib/fmpz_mod_mpoly_factor.pxd new file mode 100644 index 00000000..aa9b9cf2 --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_mpoly_factor.pxd @@ -0,0 +1,26 @@ +from flint.flintlib.flint cimport slong +from flint.flintlib.fmpz cimport fmpz_t, fmpz_struct +from flint.flintlib.fmpz_mod_mpoly cimport fmpz_mod_mpoly_ctx_t, fmpz_mod_mpoly_t, fmpz_mod_mpoly_struct + + +cdef extern from "flint/fmpz_mod_mpoly_factor.h": + ctypedef struct fmpz_mod_mpoly_factor_struct: + fmpz_t constant + fmpz_mod_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + + ctypedef fmpz_mod_mpoly_factor_struct fmpz_mod_mpoly_factor_t[1] + + void fmpz_mod_mpoly_factor_init(fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_clear(fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_swap(fmpz_mod_mpoly_factor_t f, fmpz_mod_mpoly_factor_t g, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_factor_length(const fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_get_constant_fmpz(fmpz_t c, const fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_get_base(fmpz_mod_mpoly_t B, const fmpz_mod_mpoly_factor_t f, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_swap_base(fmpz_mod_mpoly_t B, fmpz_mod_mpoly_factor_t f, slong i, const fmpz_mod_mpoly_ctx_t ctx) + slong fmpz_mod_mpoly_factor_get_exp_si(fmpz_mod_mpoly_factor_t f, slong i, const fmpz_mod_mpoly_ctx_t ctx) + void fmpz_mod_mpoly_factor_sort(fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_factor_squarefree(fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) + int fmpz_mod_mpoly_factor(fmpz_mod_mpoly_factor_t f, const fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/nmod_mpoly_factor.pxd b/src/flint/flintlib/nmod_mpoly_factor.pxd new file mode 100644 index 00000000..db875fde --- /dev/null +++ b/src/flint/flintlib/nmod_mpoly_factor.pxd @@ -0,0 +1,18 @@ +from flint.flintlib.nmod_mpoly cimport nmod_mpoly_ctx_t, nmod_mpoly_t +from flint.flintlib.flint cimport slong + + +# unimported types {'nmod_mpoly_factor_t'} + +cdef extern from "flint/nmod_mpoly_factor.h": + void nmod_mpoly_factor_init(nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_factor_clear(nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_factor_swap(nmod_mpoly_factor_t f, nmod_mpoly_factor_t g, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_factor_length(const nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) + ulong nmod_mpoly_factor_get_constant_ui(const nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_factor_get_base(nmod_mpoly_t p, const nmod_mpoly_factor_t f, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_factor_swap_base(nmod_mpoly_t p, nmod_mpoly_factor_t f, slong i, const nmod_mpoly_ctx_t ctx) + slong nmod_mpoly_factor_get_exp_si(nmod_mpoly_factor_t f, slong i, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_factor_sort(nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_factor_squarefree(nmod_mpoly_factor_t f, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + int nmod_mpoly_factor(nmod_mpoly_factor_t f, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) From 53a5dbb81f00bbd04bfd73ff9b85ab6472014e44 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:38:11 +1000 Subject: [PATCH 05/29] Copy-paste error --- src/flint/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index af81d4d8..d7453a91 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -14,7 +14,7 @@ from .types.nmod import * from .types.nmod_poly import * -from .types.nmod_mpoly import nmod_mpoly_ctx, nmod_mpoly, fmpz_mod_mpoly_vec +from .types.nmod_mpoly import nmod_mpoly_ctx, nmod_mpoly, nmod_mpoly_vec from .types.nmod_mat import * from .types.nmod_series import * From e8be1e31da2c15794403d8c754095d813dd8db9a Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:38:37 +1000 Subject: [PATCH 06/29] Add fmpz_mod_mpoly. Based on fmpz_mpoly --- src/flint/flint_base/flint_base.pyx | 18 +++- src/flint/types/fmpz_mod_mpoly.pxd | 4 +- src/flint/types/fmpz_mod_mpoly.pyx | 146 +++++++++++++++++++++++----- 3 files changed, 136 insertions(+), 32 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 481cac7f..697cde82 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -200,14 +200,13 @@ cdef class flint_mpoly_context(flint_elem): return nametup @classmethod - def get_context(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None): + def create_context_key(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None): """ - Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable - name string, `names`, or a tuple of variable names, `nametup`. + Create a key for the context cache via the number of variables, the ordering, and + either a variable name string, or a tuple of variable names. """ - # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering - # object is not provided. This is pretty obtuse so we check it's type ourselves + # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") @@ -217,6 +216,15 @@ cdef class flint_mpoly_context(flint_elem): key = nvars, ordering, cls.create_variable_names(nvars, names) else: raise ValueError("must provide either `names` or `nametup`") + return key + + @classmethod + def get_context(cls, *args, **kwargs): + """ + Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable + name string, `names`, or a tuple of variable names, `nametup`. + """ + key = cls.create_context_key(*args, **kwargs) ctx = cls._ctx_cache.get(key) if ctx is None: diff --git a/src/flint/types/fmpz_mod_mpoly.pxd b/src/flint/types/fmpz_mod_mpoly.pxd index 643f1b52..3f259c29 100644 --- a/src/flint/types/fmpz_mod_mpoly.pxd +++ b/src/flint/types/fmpz_mod_mpoly.pxd @@ -23,6 +23,7 @@ cdef inline fmpz_mod_mpoly create_fmpz_mod_mpoly(fmpz_mod_mpoly_ctx ctx): cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): cdef fmpz_mod_mpoly_ctx_t val + cdef readonly object __prime_modulus cdef class fmpz_mod_mpoly(flint_mpoly): cdef fmpz_mod_mpoly_t val @@ -30,6 +31,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): cdef bint _init cdef class fmpz_mod_mpoly_vec: - cdef fmpz_mod_mpoly_t *val + cdef fmpz_mod_mpoly_struct *val + cdef slong length cdef fmpz_mod_mpoly_ctx ctx cdef fmpz_mod_mpoly_struct **double_indirect diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 1024e12e..253452e5 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -4,6 +4,7 @@ from flint.flint_base.flint_base cimport ( ordering_py_to_c, ordering_c_to_py, ) +from flint.flint_base.flint_base import Ordering from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -17,6 +18,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_add_fmpz, fmpz_mod_mpoly_clear, fmpz_mod_mpoly_compose_fmpz_mod_mpoly, + fmpz_mod_mpoly_ctx_get_modulus, fmpz_mod_mpoly_ctx_init, fmpz_mod_mpoly_degrees_fmpz, fmpz_mod_mpoly_derivative, @@ -49,6 +51,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_sub, fmpz_mod_mpoly_sub_fmpz, fmpz_mod_mpoly_total_degree_fmpz, + fmpz_mod_mpoly_sqrt, ) from flint.flintlib.fmpz_mod_mpoly_factor cimport ( fmpz_mod_mpoly_factor, @@ -77,10 +80,50 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): _ctx_cache = _fmpz_mod_mpoly_ctx_cache - def __init__(self, slong nvars, ordering, names): - fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) + def __init__(self, slong nvars, ordering, names, modulus): + cdef fmpz m + if not typecheck(modulus, fmpz): + m = any_as_fmpz(modulus) + if m is NotImplemented: + raise TypeError(f"modulus ({modulus}) is not coercible to fmpz") + else: + m = modulus + fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering), m.val) + self.__prime_modulus = None super().__init__(nvars, names) + @classmethod + def create_context_key( + cls, + slong nvars=1, + ordering=Ordering.lex, + modulus = None, + names: Optional[str] = "x", + nametup: Optional[tuple] = None, + ): + """ + Create a key for the context cache via the number of variables, the ordering, the modulus, and either a + variable name string, or a tuple of variable names. + """ + # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # object is not provided. This is pretty obtuse so we check its type ourselves + if not isinstance(ordering, Ordering): + raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + elif not typecheck(modulus, fmpz): + m = any_as_fmpz(modulus) + if m is NotImplemented: + raise TypeError(f"`modulus` ('{modulus}') is not coercible to fmpz") + else: + modulus = m + + if nametup is not None: + key = nvars, ordering, nametup, modulus + elif nametup is None and names is not None: + key = nvars, ordering, cls.create_variable_names(nvars, names), modulus + else: + raise ValueError("must provide either `names` or `nametup`") + return key + def nvars(self): """ Return the number of variables in the context @@ -103,6 +146,36 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): """ return ordering_c_to_py(self.val.minfo.ord) + def modulus(self): + """ + Return the modulus of the context object. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 2, 'w') + >>> ctx.modulus() + 2 + + """ + cdef fmpz m = fmpz.__new__(fmpz) + fmpz_mod_mpoly_ctx_get_modulus(m.val, self.val) + return m + + def is_prime(self): + """ + Return whether the modulus is prime + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127, Ordering.degrevlex, 'z') + >>> ctx.is_prime() + False + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127 - 1, Ordering.degrevlex, 'z') + >>> ctx.is_prime() + True + """ + if self.__prime_modulus is None: + self.__prime_modulus = self.modulus().is_prime() + return self.__prime_modulus + def gen(self, slong i): """ Return the `i`th generator of the polynomial ring @@ -386,7 +459,10 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __divmod__(self, other): cdef fmpz_mod_mpoly res, res2 - if typecheck(other, fmpz_mod_mpoly): + + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") elif (self).ctx is not (other).ctx: @@ -409,7 +485,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __rdivmod__(self, other): cdef fmpz_mod_mpoly res, res2 - if not self: + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") other = any_as_fmpz(other) if other is not NotImplemented: @@ -422,7 +500,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __floordiv__(self, other): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod_mpoly): + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") elif (self).ctx is not (other).ctx: @@ -443,7 +523,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __rfloordiv__(self, other): cdef fmpz_mod_mpoly res - if not self: + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") other = any_as_fmpz(other) if other is not NotImplemented: @@ -457,7 +539,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): cdef: fmpz_mod_mpoly res, div - if typecheck(other, fmpz_mod_mpoly): + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") elif self.ctx is not (other).ctx: @@ -484,7 +568,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __rtruediv__(self, other): cdef fmpz_mod_mpoly res - if not self: + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") o = any_as_fmpz(other) if o is NotImplemented: @@ -517,8 +603,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): V = fmpz_vec(args_fmpz, double_indirect=True) vres = fmpz.__new__(fmpz) - if fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover + fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) return vres def iadd(self, other): @@ -693,8 +778,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_set(res.val, self.val, self.ctx.val) for i, arg in args: - if fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover + fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) return res def compose(self, *args, ctx=None) -> fmpz_mod_mpoly: @@ -853,7 +937,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): 4*x0*x1 + 1 """ cdef fmpz_mod_mpoly res - if not typecheck(other, fmpz_mod_mpoly): + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + elif not typecheck(other, fmpz_mod_mpoly): raise TypeError("argument must be a fmpz_mod_mpoly") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") @@ -872,6 +958,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): 4*x0*x1 + 1 """ cdef fmpz_mod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("square root with non-prime modulus is not supported") + res = create_fmpz_mod_mpoly(self.ctx) if fmpz_mod_mpoly_sqrt(res.val, self.val, self.ctx.val): @@ -901,7 +990,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly u fmpz_mod_mpoly_factor_init(fac, self.ctx.val) - fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val) + if not fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") res = [0] * fac.num for i in range(fac.num): @@ -940,7 +1030,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly u fmpz_mod_mpoly_factor_init(fac, self.ctx.val) - fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + if not fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") res = [0] * fac.num for i in range(fac.num): @@ -1019,20 +1110,23 @@ cdef class fmpz_mod_mpoly_vec: def __cinit__(self, iterable_or_len, fmpz_mod_mpoly_ctx ctx, bint double_indirect = False): if isinstance(iterable_or_len, int): - length = iterable_or_len + self.length = iterable_or_len else: - length = len(iterable_or_len) + self.length = len(iterable_or_len) self.ctx = ctx - fmpz_mod_mpoly_vec_init(self.val, length, self.ctx.val) + + self.val = libc.stdlib.malloc(self.length * sizeof(fmpz_mod_mpoly_struct)) + for i in range(self.length): + fmpz_mod_mpoly_init(&self.val[i], self.ctx.val) if double_indirect: - self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_mod_mpoly_struct *)) + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpz_mod_mpoly_struct *)) if self.double_indirect is NULL: raise MemoryError("malloc returned a null pointer") # pragma: no cover - for i in range(length): - self.double_indirect[i] = fmpz_mod_mpoly_vec_entry(self.val, i) + for i in range(self.length): + self.double_indirect[i] = &self.val[i] else: self.double_indirect = NULL @@ -1050,17 +1144,17 @@ cdef class fmpz_mod_mpoly_vec: def __getitem__(self, x): if not isinstance(x, int): raise TypeError("index is not integer") - elif not 0 <= x < self.val.length: + elif not 0 <= x < self.length: raise IndexError("index out of range") cdef fmpz_mod_mpoly z = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_set(z.val, f&self.val[x], self.ctx.val) + fmpz_mod_mpoly_set(z.val, &self.val[x], self.ctx.val) return z def __setitem__(self, x, y): if not isinstance(x, int): raise TypeError("index is not integer") - elif not 0 <= x < self.val.length: + elif not 0 <= x < self.length: raise IndexError("index out of range") elif not typecheck(y, fmpz_mod_mpoly): raise TypeError("argument is not fmpz_mod_mpoly") @@ -1073,8 +1167,8 @@ cdef class fmpz_mod_mpoly_vec: return self.val.length def __str__(self): - s = [None] * self.val.length - for i in range(self.val.length): + s = [None] * self.length + for i in range(self.length): x = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_set(x.val, &self.val[x], self.ctx.val) s[i] = str(x) From 89c088b29501c8959bf1732376298a9e8dfb06ec Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:38:57 +1000 Subject: [PATCH 07/29] Cram fmpz_mod_mpoly into existing mpoly tests for now --- src/flint/test/test_all.py | 210 ++++++++++++++++++++++--------------- 1 file changed, 123 insertions(+), 87 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index a10f867e..c8e9df37 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2635,19 +2635,34 @@ def setbad(obj, i, val): def _all_mpolys(): return [ - (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx, flint.fmpz, False), - (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx, flint.fmpq, True), + (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx.get_context, flint.fmpz, False), + (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx.get_context, flint.fmpq, True), + ( + flint.fmpz_mod_mpoly, + lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), + flint.fmpz, + True, + ), + ( + flint.fmpz_mod_mpoly, + lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), + flint.fmpz, + False, + ), ] def test_mpolys(): - for P, C, S, is_field in _all_mpolys(): + for P, get_context, S, is_field in _all_mpolys(): - ctx = C.get_context(nvars=2) + ctx = get_context(nvars=2) - assert raises(lambda: C.get_context(nvars=2, ordering="bad"), TypeError) - assert raises(lambda: C.get_context(nvars=-1), ValueError) - assert raises(lambda: C(-1, flint.Ordering.lex, []), ValueError) + assert raises(lambda: get_context(nvars=2, ordering="bad"), TypeError) + assert raises(lambda: get_context(nvars=-1), ValueError) + if ctx.__class__ is flint.fmpz_mod_mpoly_ctx: + assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, [], 4), ValueError) + else: + assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, []), ValueError) assert raises(lambda: ctx.constant("bad"), TypeError) assert raises(lambda: ctx.from_dict("bad"), ValueError) assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) @@ -2656,7 +2671,7 @@ def test_mpolys(): assert raises(lambda: ctx.gen(-1), IndexError) assert raises(lambda: ctx.gen(10), IndexError) - assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), IncompatibleContextError) + assert raises(lambda: P(val=get_context(nvars=1).constant(0), ctx=ctx), IncompatibleContextError) assert raises(lambda: P(val={}, ctx=None), ValueError) assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) assert raises(lambda: P(val="1", ctx=None), ValueError) @@ -2675,10 +2690,10 @@ def quick_poly(): assert ctx.nvars() == 2 assert ctx.ordering() == flint.Ordering.lex - ctx1 = C.get_context(4) + ctx1 = get_context(4) assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] for order in list(flint.Ordering): - ctx1 = C.get_context(4, order) + ctx1 = get_context(4, order) assert ctx1.ordering() == order assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) @@ -2815,7 +2830,7 @@ def quick_poly(): assert raises(lambda: p.subs({"a": 1}), ValueError) assert raises(lambda: p.subs({"x0": 0, "x1": 1, "x2": 2}), ValueError) - no_gens_ctx = C.get_context(0) + no_gens_ctx = get_context(0) no_gens_p = P("2", no_gens_ctx) assert no_gens_p.compose(ctx=ctx1).context() is ctx1 assert raises(lambda: no_gens_p.compose(), ValueError) @@ -2892,65 +2907,79 @@ def quick_poly(): assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) assert raises(lambda: quick_poly().imul(None), NotImplementedError) - assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) - assert quick_poly() % mpoly({(1, 1): 1}) \ - == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) - assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ - == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) - - assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) - assert quick_poly() / 1 == quick_poly() - assert quick_poly() // 1 == quick_poly() - assert quick_poly() % 1 == P(ctx=ctx) - assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) + if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + assert raises(lambda: quick_poly() // mpoly({(1, 1): 1}), DomainError) + assert raises(lambda: quick_poly() % mpoly({(1, 1): 1}), DomainError) + assert raises(lambda: divmod(quick_poly(), mpoly({(1, 1): 1})), DomainError) + else: + assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) + assert quick_poly() % mpoly({(1, 1): 1}) \ + == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) + assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ + == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) + + if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + pass + else: + assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) + assert quick_poly() / 1 == quick_poly() + assert quick_poly() // 1 == quick_poly() + assert quick_poly() % 1 == P(ctx=ctx) + assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) if is_field: - assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) + if P is flint.fmpz_mod_mpoly: + assert quick_poly() / 3 == mpoly({(0, 0): S(34), (0, 1): S(68), (1, 0): S(1), (2, 2): S(35)}) + else: + assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) else: assert raises(lambda: quick_poly() / 3, DomainError) - assert 1 // quick_poly() == P(ctx=ctx) - assert 1 % quick_poly() == P(1, ctx=ctx) - assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) - - assert raises(lambda: quick_poly() / None, TypeError) - assert raises(lambda: quick_poly() // None, TypeError) - assert raises(lambda: quick_poly() % None, TypeError) - assert raises(lambda: divmod(quick_poly(), None), TypeError) - - assert raises(lambda: None / quick_poly(), TypeError) - assert raises(lambda: None // quick_poly(), TypeError) - assert raises(lambda: None % quick_poly(), TypeError) - assert raises(lambda: divmod(None, quick_poly()), TypeError) - - assert raises(lambda: quick_poly() / 0, ZeroDivisionError) - assert raises(lambda: quick_poly() // 0, ZeroDivisionError) - assert raises(lambda: quick_poly() % 0, ZeroDivisionError) - assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) - - assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) - - assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) - - assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) - assert f * g / mpoly({(0, 1): 1, (1, 0): 1}) \ - == mpoly({(1, 1): 8, (0, 0): 2}) - - if not is_field: - assert raises(lambda: 1 / quick_poly(), DomainError) - assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) + if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + pass + else: + assert 1 // quick_poly() == P(ctx=ctx) + assert 1 % quick_poly() == P(1, ctx=ctx) + assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + + assert raises(lambda: quick_poly() / None, TypeError) + assert raises(lambda: quick_poly() // None, TypeError) + assert raises(lambda: quick_poly() % None, TypeError) + assert raises(lambda: divmod(quick_poly(), None), TypeError) + + assert raises(lambda: None / quick_poly(), TypeError) + assert raises(lambda: None // quick_poly(), TypeError) + assert raises(lambda: None % quick_poly(), TypeError) + assert raises(lambda: divmod(None, quick_poly()), TypeError) + + assert raises(lambda: quick_poly() / 0, ZeroDivisionError) + assert raises(lambda: quick_poly() // 0, ZeroDivisionError) + assert raises(lambda: quick_poly() % 0, ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) + + assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) + + assert f * g / mpoly({(0, 1): 1, (1, 0): 1}) \ + == mpoly({(1, 1): 8, (0, 0): 2}) + + if not is_field: + assert raises(lambda: 1 / quick_poly(), DomainError) + assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) assert quick_poly() ** 0 == P(1, ctx=ctx) assert quick_poly() ** 1 == quick_poly() @@ -2972,19 +3001,25 @@ def quick_poly(): # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - if is_field: - assert (f * g).gcd(f) == f / 4 + if P is not flint.fmpz_mod_mpoly or (P is flint.fmpz_mod_mpoly and f.context().is_prime()): + if is_field: + assert (f * g).gcd(f) == f / 4 + else: + assert (f * g).gcd(f) == f + assert raises(lambda: quick_poly().gcd(None), TypeError) + assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) else: - assert (f * g).gcd(f) == f - assert raises(lambda: quick_poly().gcd(None), TypeError) - assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: (f * g).gcd(f), DomainError) - assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) + # assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) - assert (f * f).sqrt() == f - if P is flint.fmpz_mpoly: - assert (f * f).sqrt(assume_perfect_square=True) == f - assert raises(lambda: quick_poly().sqrt(), ValueError) + if P is not flint.fmpz_mod_mpoly or (P is flint.fmpz_mod_mpoly and f.context().is_prime()): + assert (f * f).sqrt() == f + if P is flint.fmpz_mpoly: + assert (f * f).sqrt(assume_perfect_square=True) == f + assert raises(lambda: quick_poly().sqrt(), ValueError) + else: + assert raises(lambda: (f * g).sqrt(), DomainError) p = quick_poly() assert p.derivative(0) == p.derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) @@ -2994,20 +3029,21 @@ def quick_poly(): assert raises(lambda: p.derivative(3), IndexError) assert raises(lambda: p.derivative(None), TypeError) - if is_field: - assert quick_poly().integral(0) == quick_poly().integral("x0") == \ - mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) - assert quick_poly().integral(1) == quick_poly().integral("x1") == \ - mpoly({(2, 3): S(4, 3), (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) - else: - assert quick_poly().integral(0) == quick_poly().integral("x0") == \ - (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) - assert quick_poly().integral(1) == quick_poly().integral("x1") == \ - (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) - - assert raises(lambda: p.integral("x3"), ValueError) - assert raises(lambda: p.integral(3), IndexError) - assert raises(lambda: p.integral(None), TypeError) + if P is not flint.fmpz_mod_mpoly: + if is_field: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + mpoly({(2, 3): S(4, 3), (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) + else: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) + + assert raises(lambda: p.integral("x3"), ValueError) + assert raises(lambda: p.integral(3), IndexError) + assert raises(lambda: p.integral(None), TypeError) def _all_mpoly_vecs(): From 004ae4b1a094323ef0f864fda3e4231e05d04a98 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 19 Jul 2024 00:46:56 +1000 Subject: [PATCH 08/29] Update get_context call signature in doc strings --- src/flint/types/fmpz_mod_mpoly.pyx | 54 +++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 253452e5..932f618b 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -129,7 +129,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): Return the number of variables in the context >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') >>> ctx.nvars() 4 """ @@ -140,7 +140,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): Return the term order of the context object. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 11, 'w') >>> ctx.ordering() """ @@ -165,10 +165,10 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): Return whether the modulus is prime >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127, Ordering.degrevlex, 'z') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127, 'z') >>> ctx.is_prime() False - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2**127 - 1, Ordering.degrevlex, 'z') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127 - 1, 'z') >>> ctx.is_prime() True """ @@ -181,7 +181,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): Return the `i`th generator of the polynomial ring >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') >>> ctx.gen(1) z1 """ @@ -212,7 +212,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): to fmpz) representing exponents, and corresponding values of fmpz. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x,y') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -315,7 +315,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Negative exponents are made positive. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] 3 @@ -341,7 +341,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Negative exponents are made positive. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p @@ -611,7 +611,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): In-place addition, mutates self. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -637,7 +637,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): In-place subtraction, mutates self. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -663,7 +663,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): In-place multiplication, mutates self. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -689,7 +689,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the exponent vectors of each term as a tuple of fmpz. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] @@ -711,7 +711,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the coefficients of each term as a fmpz >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.coeffs() [4, 2, 3, 1] @@ -734,7 +734,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): # Return the terms of this polynomial as a list of fmpz_mod_mpolys. # >>> from flint import Ordering - # >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) # >>> f.terms() # [4*x0*x1, 2*x0, 3*x1, 1] @@ -758,7 +758,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): all values must be fmpz. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.subs({"x1": 0}) 2*x0 + 1 @@ -787,8 +787,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): from this polynomials context. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(1, Ordering.lex, 'x') - >>> ctx1 = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(1, Ordering.lex, 11, 'x') + >>> ctx1 = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'y') >>> f = ctx.from_dict({(2,): 1}) >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) >>> f @@ -837,7 +837,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the context object for this polynomials. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -852,7 +852,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the coefficient at index `i`. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -870,7 +870,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the exponent vector at index `i` as a tuple. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.monomial(1) (0, 1) @@ -889,7 +889,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return a dictionary of variable name to degree. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() (1, 2, 3, 0) @@ -906,7 +906,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the total degree. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -930,7 +930,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the gcd of self and other. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -952,7 +952,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the square root of self. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -976,7 +976,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> from flint import Ordering >>> Zm = fmpz_mod_mpoly - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -1016,7 +1016,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> from flint import Ordering >>> Zm = fmpz_mod_mpoly - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -1083,7 +1083,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): variable in the context. >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 From a24ea8deb0339ac4dd20a0f6aceb34f454febf5e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 26 Jul 2024 01:15:50 +1000 Subject: [PATCH 09/29] Initial nmod_mpoly work, not complete --- src/flint/flintlib/fmpz_mpoly_factor.pxd | 2 - src/flint/flintlib/nmod_mpoly_factor.pxd | 16 +- src/flint/test/test_all.py | 44 +- src/flint/types/nmod_mpoly.pxd | 6 +- src/flint/types/nmod_mpoly.pyx | 1072 +++++++++++++++++++++- 5 files changed, 1113 insertions(+), 27 deletions(-) diff --git a/src/flint/flintlib/fmpz_mpoly_factor.pxd b/src/flint/flintlib/fmpz_mpoly_factor.pxd index bba20a9b..62335351 100644 --- a/src/flint/flintlib/fmpz_mpoly_factor.pxd +++ b/src/flint/flintlib/fmpz_mpoly_factor.pxd @@ -4,8 +4,6 @@ from flint.flintlib.flint cimport slong, fmpz_struct from flint.flintlib.fmpq cimport fmpq_t -# unimported types set() - cdef extern from "flint/fmpz_mpoly_factor.h": ctypedef struct fmpz_mpoly_factor_struct: diff --git a/src/flint/flintlib/nmod_mpoly_factor.pxd b/src/flint/flintlib/nmod_mpoly_factor.pxd index db875fde..5e6fefcd 100644 --- a/src/flint/flintlib/nmod_mpoly_factor.pxd +++ b/src/flint/flintlib/nmod_mpoly_factor.pxd @@ -1,10 +1,18 @@ -from flint.flintlib.nmod_mpoly cimport nmod_mpoly_ctx_t, nmod_mpoly_t -from flint.flintlib.flint cimport slong +from flint.flintlib.nmod_mpoly cimport nmod_mpoly_ctx_t, nmod_mpoly_struct, nmod_mpoly_t +from flint.flintlib.flint cimport slong, ulong, mp_limb_t +from flint.flintlib.fmpz cimport fmpz_struct -# unimported types {'nmod_mpoly_factor_t'} - cdef extern from "flint/nmod_mpoly_factor.h": + ctypedef struct nmod_mpoly_factor_struct: + mp_limb_t constant + nmod_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + + ctypedef nmod_mpoly_factor_struct nmod_mpoly_factor_t[1] + void nmod_mpoly_factor_init(nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_factor_clear(nmod_mpoly_factor_t f, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_factor_swap(nmod_mpoly_factor_t f, nmod_mpoly_factor_t g, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index c8e9df37..515f3249 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2649,6 +2649,18 @@ def _all_mpolys(): flint.fmpz, False, ), + ( + flint.nmod_mpoly, + lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), + int, + True, + ), + ( + flint.nmod_mpoly, + lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), + int, + False, + ), ] @@ -2659,7 +2671,7 @@ def test_mpolys(): assert raises(lambda: get_context(nvars=2, ordering="bad"), TypeError) assert raises(lambda: get_context(nvars=-1), ValueError) - if ctx.__class__ is flint.fmpz_mod_mpoly_ctx: + if ctx.__class__ is flint.fmpz_mod_mpoly_ctx or ctx.__class__ is flint.nmod_mpoly_ctx: assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, [], 4), ValueError) else: assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, []), ValueError) @@ -2842,8 +2854,8 @@ def quick_poly(): assert +quick_poly() \ == quick_poly() - assert -quick_poly() \ - == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) + # assert -quick_poly() \ + # == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) assert quick_poly() \ + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ @@ -2863,10 +2875,10 @@ def quick_poly(): assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().iadd(P(ctx=ctx1)), IncompatibleContextError) - assert raises(lambda: quick_poly().iadd(None), NotImplementedError) + # assert raises(lambda: quick_poly().iadd(None), NotImplementedError) - assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ - == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) + # assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + # == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() @@ -2874,13 +2886,13 @@ def quick_poly(): q = quick_poly() assert q.isub(T(1)) is None assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) - assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) + # assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().isub(P(ctx=ctx1)), IncompatibleContextError) - assert raises(lambda: quick_poly().isub(None), NotImplementedError) + # assert raises(lambda: quick_poly().isub(None), NotImplementedError) assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2905,9 +2917,9 @@ def quick_poly(): assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) - assert raises(lambda: quick_poly().imul(None), NotImplementedError) + # assert raises(lambda: quick_poly().imul(None), NotImplementedError) - if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): assert raises(lambda: quick_poly() // mpoly({(1, 1): 1}), DomainError) assert raises(lambda: quick_poly() % mpoly({(1, 1): 1}), DomainError) assert raises(lambda: divmod(quick_poly(), mpoly({(1, 1): 1})), DomainError) @@ -2918,7 +2930,7 @@ def quick_poly(): assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) - if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): pass else: assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) @@ -2928,7 +2940,7 @@ def quick_poly(): assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) if is_field: - if P is flint.fmpz_mod_mpoly: + if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly): assert quick_poly() / 3 == mpoly({(0, 0): S(34), (0, 1): S(68), (1, 0): S(1), (2, 2): S(35)}) else: assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) @@ -2937,7 +2949,7 @@ def quick_poly(): f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) - if P is flint.fmpz_mod_mpoly and not ctx.is_prime(): + if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): pass else: assert 1 // quick_poly() == P(ctx=ctx) @@ -3001,7 +3013,7 @@ def quick_poly(): # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - if P is not flint.fmpz_mod_mpoly or (P is flint.fmpz_mod_mpoly and f.context().is_prime()): + if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly) or f.context().is_prime(): if is_field: assert (f * g).gcd(f) == f / 4 else: @@ -3013,7 +3025,7 @@ def quick_poly(): # assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) - if P is not flint.fmpz_mod_mpoly or (P is flint.fmpz_mod_mpoly and f.context().is_prime()): + if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly) or f.context().is_prime(): assert (f * f).sqrt() == f if P is flint.fmpz_mpoly: assert (f * f).sqrt(assume_perfect_square=True) == f @@ -3029,7 +3041,7 @@ def quick_poly(): assert raises(lambda: p.derivative(3), IndexError) assert raises(lambda: p.derivative(None), TypeError) - if P is not flint.fmpz_mod_mpoly: + if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly): if is_field: assert quick_poly().integral(0) == quick_poly().integral("x0") == \ mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) diff --git a/src/flint/types/nmod_mpoly.pxd b/src/flint/types/nmod_mpoly.pxd index 26d4dce8..a7249f32 100644 --- a/src/flint/types/nmod_mpoly.pxd +++ b/src/flint/types/nmod_mpoly.pxd @@ -6,7 +6,7 @@ from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_init, nmod_mpoly_struct ) -from flint.flintlib.flint cimport slong +from flint.flintlib.flint cimport slong, ulong cdef inline init_nmod_mpoly(nmod_mpoly var, nmod_mpoly_ctx ctx): var.ctx = ctx @@ -23,6 +23,7 @@ cdef inline nmod_mpoly create_nmod_mpoly(nmod_mpoly_ctx ctx): cdef class nmod_mpoly_ctx(flint_mpoly_context): cdef nmod_mpoly_ctx_t val + cdef readonly object __prime_modulus cdef class nmod_mpoly(flint_mpoly): cdef nmod_mpoly_t val @@ -30,6 +31,7 @@ cdef class nmod_mpoly(flint_mpoly): cdef bint _init cdef class nmod_mpoly_vec: - cdef nmod_mpoly_t *val + cdef nmod_mpoly_struct *val + cdef slong length cdef nmod_mpoly_ctx ctx cdef nmod_mpoly_struct **double_indirect diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 77acbec2..2312e0a4 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -4,6 +4,7 @@ from flint.flint_base.flint_base cimport ( ordering_py_to_c, ordering_c_to_py, ) +from flint.flint_base.flint_base import Ordering from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -11,16 +12,1081 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec +from flint.flintlib.fmpz cimport fmpz_set +from flint.flintlib.nmod_mpoly cimport ( + nmod_mpoly_add, + nmod_mpoly_add_ui, + nmod_mpoly_clear, + nmod_mpoly_compose_nmod_mpoly, + nmod_mpoly_ctx_modulus, + nmod_mpoly_ctx_init, + nmod_mpoly_degrees_fmpz, + nmod_mpoly_derivative, + nmod_mpoly_div, + nmod_mpoly_divides, + nmod_mpoly_divrem, + nmod_mpoly_equal, + nmod_mpoly_evaluate_all_ui, + nmod_mpoly_evaluate_one_ui, + nmod_mpoly_gcd, + nmod_mpoly_gen, + nmod_mpoly_get_coeff_ui_fmpz, + nmod_mpoly_get_str_pretty, + nmod_mpoly_get_term, + nmod_mpoly_get_term_coeff_ui, + nmod_mpoly_get_term_exp_fmpz, + nmod_mpoly_is_one, + nmod_mpoly_is_zero, + nmod_mpoly_length, + nmod_mpoly_mul, + nmod_mpoly_neg, + nmod_mpoly_pow_fmpz, + nmod_mpoly_push_term_ui_ffmpz, + nmod_mpoly_scalar_mul_ui, + nmod_mpoly_set, + nmod_mpoly_set_coeff_ui_fmpz, + nmod_mpoly_set_ui, + nmod_mpoly_set_str_pretty, + nmod_mpoly_sort_terms, + nmod_mpoly_sub, + nmod_mpoly_sub_ui, + nmod_mpoly_total_degree_fmpz, + nmod_mpoly_sqrt, +) +from flint.flintlib.nmod_mpoly_factor cimport ( + nmod_mpoly_factor, + nmod_mpoly_factor_clear, + nmod_mpoly_factor_init, + nmod_mpoly_factor_squarefree, + nmod_mpoly_factor_t, +) +from flint.flintlib.ulong_extras cimport n_is_prime + from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib +import numpy as np cdef dict _nmod_mpoly_ctx_cache = {} + cdef class nmod_mpoly_ctx(flint_mpoly_context): - pass + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :param ordering: The term order for the ring + :param names: A tuple containing the names of the variables of the ring. + + Do not construct one of these directly, use `nmod_mpoly_ctx.get_context`. + """ + + _ctx_cache = _nmod_mpoly_ctx_cache + + def __init__(self, slong nvars, ordering, names, modulus: int): + if modulus <= 0: + raise ValueError("modulus must be positive") + + nmod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering), modulus) + self.__prime_modulus = None + super().__init__(nvars, names) + + @classmethod + def create_context_key( + cls, + slong nvars=1, + ordering=Ordering.lex, + modulus = None, + names: Optional[str] = "x", + nametup: Optional[tuple] = None, + ): + """ + Create a key for the context cache via the number of variables, the ordering, the modulus, and either a + variable name string, or a tuple of variable names. + """ + # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # object is not provided. This is pretty obtuse so we check its type ourselves + if not isinstance(ordering, Ordering): + raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + + if nametup is not None: + key = nvars, ordering, nametup, modulus + elif nametup is None and names is not None: + key = nvars, ordering, cls.create_variable_names(nvars, names), modulus + else: + raise ValueError("must provide either `names` or `nametup`") + return key + + def nvars(self): + """ + Return the number of variables in the context + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx.nvars() + 4 + """ + return self.val.minfo.nvars + + def ordering(self): + """ + Return the term order of the context object. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.deglex, 11, 'w') + >>> ctx.ordering() + + """ + return ordering_c_to_py(self.val.minfo.ord) + + def modulus(self): + """ + Return the modulus of the context object. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.deglex, 2, 'w') + >>> ctx.modulus() + 2 + + """ + return nmod_mpoly_ctx_modulus(self.val) + + def is_prime(self): + """ + Return whether the modulus is prime + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127, 'z') + >>> ctx.is_prime() + False + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127 - 1, 'z') + >>> ctx.is_prime() + True + """ + if self.__prime_modulus is None: + self.__prime_modulus = n_is_prime(self.modulus()) + return self.__prime_modulus + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') + >>> ctx.gen(1) + z1 + """ + cdef nmod_mpoly res + if not 0 <= i < self.val.minfo.nvars: + raise IndexError("generator index out of range") + res = create_nmod_mpoly(self) + nmod_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef nmod_mpoly res + res = create_nmod_mpoly(self) + nmod_mpoly_set_ui(res.val, z, res.ctx.val) + return res + + def from_dict(self, d): + """ + Create a nmod_mpoly from a dictionary in this context. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x,y') + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef: + fmpz_vec exp_vec + slong i, nvars = self.nvars() + nmod_mpoly res + + if not isinstance(d, dict): + raise ValueError("expected a dictionary") + + res = create_nmod_mpoly(self) + + for i, (k, v) in enumerate(d.items()): + if len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + elif v: + exp_vec = fmpz_vec(k) + nmod_mpoly_push_term_ui_ffmpz(res.val, v, exp_vec.val, self.val) + + nmod_mpoly_sort_terms(res.val, self.val) + return res + cdef class nmod_mpoly(flint_mpoly): - pass + """ + The *nmod_mpoly* type represents sparse multivariate polynomials over + the integers. + """ + + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + nmod_mpoly_clear(self.val, self.ctx.val) + self._init = False + + def __init__(self, val=0, ctx=None): + if typecheck(val, nmod_mpoly): + if ctx is None or ctx == (val).ctx: + init_nmod_mpoly(self, (val).ctx) + nmod_mpoly_set(self.val, (val).val, self.ctx.val) + else: + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") + elif isinstance(val, dict): + if ctx is None: + raise ValueError("a context is required to create a nmod_mpoly from a dict") + x = ctx.from_dict(val) + # XXX: this copy is silly, have a ctx function that assigns an nmod_mpoly_t + init_nmod_mpoly(self, ctx) + nmod_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + init_nmod_mpoly(self, ctx) + if nmod_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("unable to parse nmod_mpoly from string") + nmod_mpoly_sort_terms(self.val, self.ctx.val) + else: + val = int(val) # Explicitly coerce to int here to be consistent with exception ordering + if ctx is None: + raise ValueError("need context to convert fmpz to nmod_mpoly") + init_nmod_mpoly(self, ctx) + nmod_mpoly_set_ui(self.val, val, self.ctx.val) + + def __bool__(self): + return not nmod_mpoly_is_zero(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + if not (op == Py_EQ or op == Py_NE): + return NotImplemented + elif other is None: + return op == Py_NE + elif typecheck(self, nmod_mpoly) and typecheck(other, nmod_mpoly): + if (self).ctx is (other).ctx: + return (op == Py_NE) ^ bool( + nmod_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) + else: + return op == Py_NE + else: + return NotImplemented + + def __len__(self): + return nmod_mpoly_length(self.val, self.ctx.val) + + def __getitem__(self, x): + """ + Return the coefficient of the term with the exponent vector `x`. + Always returns a value, missing keys will return `0`. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] + 3 + + """ + cdef: + slong nvars = self.ctx.nvars() + + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + return nmod_mpoly_get_coeff_ui_fmpz(self.val, exp_vec.double_indirect, self.ctx.val) + + def __setitem__(self, x, y): + """ + Set the coefficient of the term with the exponent vector `x` to `y`. + Will always set a value, missing keys will create a new term. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 2*x1 + + """ + cdef: + slong nvars = self.ctx.nvars() + + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + nmod_mpoly_set_coeff_ui_fmpz(self.val, y, exp_vec.double_indirect, self.ctx.val) + + def __neg__(self): + cdef nmod_mpoly res + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_neg(res.val, (self).val, res.ctx.val) + return res + + def __add__(self, other): + cdef nmod_mpoly res + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + else: + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) + return res + + def __radd__(self, other): + cdef nmod_mpoly res + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) + return res + + def __sub__(self, other): + cdef nmod_mpoly res + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + else: + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, (self).val, other, self.ctx.val) + return res + + def __rsub__(self, other): + cdef nmod_mpoly res + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) + return -res + + def __mul__(self, other): + cdef nmod_mpoly res + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + else: + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) + return res + + def __rmul__(self, other): + cdef nmod_mpoly res + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) + return res + + def __pow__(self, other, modulus): + cdef nmod_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise nmod_mpoly to negative power") + res = create_nmod_mpoly(self.ctx) + if nmod_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def __divmod__(self, other): + cdef nmod_mpoly res, res2 + + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, nmod_mpoly): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + other = nmod_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef nmod_mpoly res, res2 + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: + raise ZeroDivisionError("nmod_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = nmod_mpoly(other, self.ctx) + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __floordiv__(self, other): + cdef nmod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, nmod_mpoly): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + other = nmod_mpoly(other, self.ctx) + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rfloordiv__(self, other): + cdef nmod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: + raise ZeroDivisionError("nmod_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = nmod_mpoly(other, self.ctx) + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res + return NotImplemented + + def __truediv__(self, other): + cdef: + nmod_mpoly res, div + + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, nmod_mpoly): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_nmod_mpoly(self.ctx) + if nmod_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") + elif isinstance(other, int): # TODO FIXME fix the other truthy checks to actually be on ints + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + res = create_nmod_mpoly(self.ctx) + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, other, self.ctx.val) + if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") + return NotImplemented + + def __rtruediv__(self, other): + cdef nmod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif not self: + raise ZeroDivisionError("nmod_mpoly division by zero") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(res.val, other, self.ctx.val) + return res / self + + def __mod__(self, other): + return divmod(self, other)[1] + + def __rmod__(self, other): + return divmod(other, self)[1] + + def __call__(self, *args) -> fmpz: + cdef: + fmpz vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + + cdef ulong[::1] V = np.asarray(args, dtype=np.uint) + return nmod_mpoly_evaluate_all_ui(self.val, &(V[0]), self.ctx.val) + + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + nmod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + else: + nmod_mpoly_add_ui((self).val, (self).val, other, self.ctx.val) + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + nmod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + else: + nmod_mpoly_sub_ui((self).val, (self).val, other, self.ctx.val) + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, nmod_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + nmod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + else: + nmod_mpoly_scalar_mul_ui(self.val, (self).val, other, self.ctx.val) + + def monoms(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.monoms() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + res = [] + for i in range(len(self)): + nmod_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) + res.append(vec.to_tuple()) + + return res + + def coeffs(self): + """ + Return the coefficients of each term as a fmpz + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.coeffs() + [4, 2, 3, 1] + + """ + return [nmod_mpoly_get_term_coeff_ui(self.val, i, self.ctx.val) for i in range(len(self))] + + # def terms(self): + # """ + # Return the terms of this polynomial as a list of nmod_mpolys. + + # >>> from flint import Ordering + # >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] + + # """ + # cdef: + # nmod_mpoly term + # slong i + + # res = [] + # for i in range(len(self)): + # term = create_nmod_mpoly(self.ctx) + # nmod_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) + + # return res + + def subs(self, dict_args) -> nmod_mpoly: + """ + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, + all values must be fmpz. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + + """ + cdef: + nmod_mpoly res + slong i + + args = tuple((self.ctx.variable_to_index(k), v) for k, v in dict_args.items()) + for _, v in args: + if not (isinstance(v, int) and v >= 0): + raise TypeError("constants must be non-negative integers") + + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_nmod_mpoly(self.ctx) + + nmod_mpoly_set(res.val, self.val, self.ctx.val) + for i, arg in args: + nmod_mpoly_evaluate_one_ui(res.val, res.val, i, arg, self.ctx.val) + return res + + def compose(self, *args, ctx=None) -> nmod_mpoly: + """ + Compose this polynomial with other nmod_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(1, Ordering.lex, 11, 'x') + >>> ctx1 = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + + """ + cdef: + nmod_mpoly res + nmod_mpoly_ctx res_ctx + nmod_mpoly_vec C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") + elif not all(typecheck(arg, nmod_mpoly) for arg in args): + raise TypeError("all arguments must be nmod_mpolys") + + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, nmod_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a nmod_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) + + C = nmod_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_nmod_mpoly(res_ctx) + if nmod_mpoly_compose_nmod_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def context(self): + """ + Return the context object for this polynomials. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return nmod_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + if not 0 <= i < nmod_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + else: + return nmod_mpoly_get_term_coeff_ui(self.val, i, self.ctx.val) + + def monomial(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.monomial(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if not 0 <= i < nmod_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = fmpz_vec(nvars, double_indirect=True) + nmod_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) + return res.to_tuple() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + (1, 2, 3, 0) + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + nmod_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return res.to_tuple() + + def total_degree(self): + """ + Return the total degree. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + nmod_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = nmod_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + res = s.decode().replace("+", " + ").replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def gcd(self, other): + """ + Return the gcd of self and other. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) + >>> (f * g).gcd(f) + 4*x0*x1 + 1 + """ + cdef nmod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + elif not typecheck(other, nmod_mpoly): + raise TypeError("argument must be a nmod_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res + + def sqrt(self): + """ + Return the square root of self. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ + cdef nmod_mpoly res + if not self.ctx.is_prime(): + raise DomainError("square root with non-prime modulus is not supported") + + res = create_nmod_mpoly(self.ctx) + + if nmod_mpoly_sqrt(res.val, self.val, self.ctx.val): + return res + else: + raise ValueError("polynomial is not a perfect square") + + def factor(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> from flint import Ordering + >>> Zm = nmod_mpoly + >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> (p1 * p2).factor() + (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + """ + cdef: + nmod_mpoly_factor_t fac + fmpz c + nmod_mpoly u + + nmod_mpoly_factor_init(fac, self.ctx.val) + if not nmod_mpoly_factor(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") + res = [0] * fac.num + + for i in range(fac.num): + u = create_nmod_mpoly(self.ctx) + nmod_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + constant = fac.constant + nmod_mpoly_factor_clear(fac, self.ctx.val) + return constant, res + + def factor_squarefree(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> from flint import Ordering + >>> Zm = nmod_mpoly + >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (p1 * p2).factor_squarefree() + (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) + >>> (p1 * p2 * p1).factor_squarefree() + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) + """ + cdef: + nmod_mpoly_factor_t fac + fmpz c + nmod_mpoly u + + nmod_mpoly_factor_init(fac, self.ctx.val) + if not nmod_mpoly_factor_squarefree(fac, self.val, self.ctx.val): + raise RuntimeError("factorisation failed") + res = [0] * fac.num + + for i in range(fac.num): + u = create_nmod_mpoly(self.ctx) + nmod_mpoly_init(u.val, u.ctx.val) + nmod_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + constant = fac.constant + nmod_mpoly_factor_clear(fac, self.ctx.val) + return constant, res + + # TODO: Rethink context conversions, particularly the proposed methods in #132 + # def coerce_to_context(self, ctx): + # cdef: + # nmod_mpoly res + # slong *C + # slong i + + # if not typecheck(ctx, nmod_mpoly_ctx): + # raise ValueError("provided context is not a nmod_mpoly_ctx") + + # if self.ctx is ctx: + # return self + + # C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + # if C is NULL: + # raise MemoryError("malloc returned a null pointer") + # res = create_nmod_mpoly(self.ctx) + + # vars = {x: i for i, x in enumerate(ctx.py_names)} + # for i, var in enumerate(self.ctx.py_names): + # C[i] = vars[var] + + # nmod_mpoly_compose_nmod_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) + + # libc.stdlib.free(C) + # return res + + def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument can either be the variable as a string, or the index of the + variable in the context. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ + cdef: + nmod_mpoly res + slong i = self.ctx.variable_to_index(var) + + res = create_nmod_mpoly(self.ctx) + + nmod_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + cdef class nmod_mpoly_vec: - pass + """ + A class representing a vector of nmod_mpolys. + """ + + def __cinit__(self, iterable_or_len, nmod_mpoly_ctx ctx, bint double_indirect = False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.ctx = ctx + + self.val = libc.stdlib.malloc(self.length * sizeof(nmod_mpoly_struct)) + for i in range(self.length): + nmod_mpoly_init(&self.val[i], self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(nmod_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + for i in range(self.length): + nmod_mpoly_clear(&self.val[i], self.ctx.val) + libc.stdlib.free(self.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef nmod_mpoly z = create_nmod_mpoly(self.ctx) + nmod_mpoly_set(z.val, &self.val[x], self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + elif not typecheck(y, nmod_mpoly): + raise TypeError("argument is not nmod_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + nmod_mpoly_set(&self.val[x], (y).val, self.ctx.val) + + def __len__(self): + return self.val.length + + def __str__(self): + s = [None] * self.length + for i in range(self.length): + x = create_nmod_mpoly(self.ctx) + nmod_mpoly_set(x.val, &self.val[x], self.ctx.val) + s[i] = str(x) + return f"[{', '.join(s)}]" + + def __repr__(self): + return f"nmod_mpoly_vec({self}, ctx={self.ctx})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.val.length)) From e0512bc2ffdb49b9870e00bf243e5e04a4b6297b Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 28 Jul 2024 23:16:05 +1000 Subject: [PATCH 10/29] Second pass at nmod_mpoly, better int handling, update tests --- src/flint/test/test_all.py | 39 +++++--- src/flint/types/nmod_mpoly.pyx | 176 +++++++++++++++++++++------------ 2 files changed, 138 insertions(+), 77 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 515f3249..ad435f19 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2854,14 +2854,18 @@ def quick_poly(): assert +quick_poly() \ == quick_poly() - # assert -quick_poly() \ - # == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) + + assert -quick_poly() \ + == mpoly( + {(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4} if P is not flint.nmod_mpoly + else {k: ctx.modulus() + v for k, v in {(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}.items()} + ) assert quick_poly() \ + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + for T in [int, S, lambda x: P(x, ctx=ctx)]: p = quick_poly() p += T(1) q = quick_poly() @@ -2875,24 +2879,31 @@ def quick_poly(): assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().iadd(P(ctx=ctx1)), IncompatibleContextError) - # assert raises(lambda: quick_poly().iadd(None), NotImplementedError) + assert raises(lambda: quick_poly().iadd(None), NotImplementedError) - # assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ - # == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) + assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + == mpoly( + {(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4} if P is not flint.nmod_mpoly + else {k: ctx.modulus() + v for k, v in {(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}.items()} + ) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + for T in [int, S, lambda x: P(x, ctx=ctx)]: p = quick_poly() p -= T(1) q = quick_poly() assert q.isub(T(1)) is None assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) - # assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) + assert T(1) - quick_poly() == \ + mpoly( + {(0, 1): -2, (1, 0): -3, (2, 2): -4} if P is not flint.nmod_mpoly + else {k: ctx.modulus() + v for k, v in {(0, 1): -2, (1, 0): -3, (2, 2): -4}.items()} + ) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().isub(P(ctx=ctx1)), IncompatibleContextError) - # assert raises(lambda: quick_poly().isub(None), NotImplementedError) + assert raises(lambda: quick_poly().isub(None), NotImplementedError) assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2905,7 +2916,7 @@ def quick_poly(): (0, 1): 6 }) - for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + for T in [int, S, lambda x: P(x, ctx=ctx)]: p = quick_poly() p *= T(2) q = quick_poly() @@ -2917,7 +2928,7 @@ def quick_poly(): assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) - # assert raises(lambda: quick_poly().imul(None), NotImplementedError) + assert raises(lambda: quick_poly().imul(None), NotImplementedError) if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): assert raises(lambda: quick_poly() // mpoly({(1, 1): 1}), DomainError) @@ -3023,7 +3034,11 @@ def quick_poly(): else: assert raises(lambda: (f * g).gcd(f), DomainError) - # assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) + if P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly: + if is_field: + assert (f * g).factor() == (S(8), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f / 4, 1)]) + else: + assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly) or f.context().is_prime(): assert (f * f).sqrt() == f diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 2312e0a4..77f13e18 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -260,12 +260,13 @@ cdef class nmod_mpoly(flint_mpoly): if nmod_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: raise ValueError("unable to parse nmod_mpoly from string") nmod_mpoly_sort_terms(self.val, self.ctx.val) - else: - val = int(val) # Explicitly coerce to int here to be consistent with exception ordering + elif isinstance(val, int): if ctx is None: - raise ValueError("need context to convert fmpz to nmod_mpoly") + raise ValueError("need context to convert int to nmod_mpoly") init_nmod_mpoly(self, ctx) nmod_mpoly_set_ui(self.val, val, self.ctx.val) + else: + raise TypeError(f"cannot construct a nmod_mpoly from a {type(val)}") def __bool__(self): return not nmod_mpoly_is_zero(self.val, self.ctx.val) @@ -350,16 +351,22 @@ cdef class nmod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_nmod_mpoly(self.ctx) nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - else: + return res + elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - return res + return res + else: + return NotImplemented def __radd__(self, other): cdef nmod_mpoly res - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - return res + if isinstance(other, int): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) + return res + else: + return NotImplemented def __sub__(self, other): cdef nmod_mpoly res @@ -368,16 +375,22 @@ cdef class nmod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - else: + return res + elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub_ui(res.val, (self).val, other, self.ctx.val) - return res + return res + else: + return NotImplemented def __rsub__(self, other): cdef nmod_mpoly res - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) - return -res + if isinstance(other, int): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) + return -res + else: + return NotImplemented def __mul__(self, other): cdef nmod_mpoly res @@ -386,33 +399,41 @@ cdef class nmod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_nmod_mpoly(self.ctx) nmod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - else: + return res + elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) - return res + return res + else: + return NotImplemented def __rmul__(self, other): cdef nmod_mpoly res - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) - return res + if isinstance(other, int): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) + return res + else: + return NotImplemented def __pow__(self, other, modulus): cdef nmod_mpoly res if modulus is not None: - raise NotImplementedError + raise NotImplementedError("cannot specify modulus outside of the nmod_mpoly_ctx") + other = any_as_fmpz(other) if other is NotImplemented: - return other - if other < 0: - raise ValueError("cannot raise nmod_mpoly to negative power") + return NotImplemented + elif other < 0: + raise ValueError("cannot raise nmod_mpoly to a negative power") + res = create_nmod_mpoly(self.ctx) if nmod_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover return res def __divmod__(self, other): - cdef nmod_mpoly res, res2 + cdef nmod_mpoly res, res2, o if not self.ctx.is_prime(): raise DomainError("division with non-prime modulus is not supported") @@ -425,35 +446,38 @@ cdef class nmod_mpoly(flint_mpoly): res2 = create_nmod_mpoly(self.ctx) nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) return (res, res2) + elif isinstance(other, int): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, other, o.ctx.val) + + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + return (res, res2) else: - other = any_as_fmpz(other) - if other is not NotImplemented: - other = nmod_mpoly(other, self.ctx) - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - return NotImplemented + return NotImplemented def __rdivmod__(self, other): - cdef nmod_mpoly res, res2 + cdef nmod_mpoly res, res2, o if not self.ctx.is_prime(): raise DomainError("division with non-prime modulus is not supported") elif not self: raise ZeroDivisionError("nmod_mpoly division by zero") - other = any_as_fmpz(other) - if other is not NotImplemented: - other = nmod_mpoly(other, self.ctx) + elif isinstance(other, int): + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) return (res, res2) - return NotImplemented + else: + return NotImplemented def __floordiv__(self, other): - cdef nmod_mpoly res + cdef nmod_mpoly res, o if not self.ctx.is_prime(): raise DomainError("division with non-prime modulus is not supported") elif typecheck(other, nmod_mpoly): @@ -464,30 +488,33 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res + elif isinstance(other, int): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, other, o.ctx.val) + + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) + return res else: - other = any_as_fmpz(other) - if other is not NotImplemented: - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - other = nmod_mpoly(other, self.ctx) - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented + return NotImplemented def __rfloordiv__(self, other): - cdef nmod_mpoly res + cdef nmod_mpoly res, o if not self.ctx.is_prime(): raise DomainError("division with non-prime modulus is not supported") elif not self: raise ZeroDivisionError("nmod_mpoly division by zero") - other = any_as_fmpz(other) - if other is not NotImplemented: - other = nmod_mpoly(other, self.ctx) + elif isinstance(other, int): + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) return res - return NotImplemented + else: + return NotImplemented def __truediv__(self, other): cdef: @@ -506,7 +533,7 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") - elif isinstance(other, int): # TODO FIXME fix the other truthy checks to actually be on ints + elif isinstance(other, int): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") res = create_nmod_mpoly(self.ctx) @@ -516,7 +543,8 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") - return NotImplemented + else: + return NotImplemented def __rtruediv__(self, other): cdef nmod_mpoly res @@ -524,9 +552,16 @@ cdef class nmod_mpoly(flint_mpoly): raise DomainError("division with non-prime modulus is not supported") elif not self: raise ZeroDivisionError("nmod_mpoly division by zero") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(res.val, other, self.ctx.val) - return res / self + elif isinstance(other, int): + res = create_nmod_mpoly(self.ctx) + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, other, self.ctx.val) + if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") + else: + return NotImplemented def __mod__(self, other): return divmod(self, other)[1] @@ -534,9 +569,8 @@ cdef class nmod_mpoly(flint_mpoly): def __rmod__(self, other): return divmod(other, self)[1] - def __call__(self, *args) -> fmpz: + def __call__(self, *args) -> ulong: cdef: - fmpz vres slong nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: @@ -565,8 +599,10 @@ cdef class nmod_mpoly(flint_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") nmod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - else: + elif isinstance(other, int): nmod_mpoly_add_ui((self).val, (self).val, other, self.ctx.val) + else: + raise NotImplementedError(f"cannot add {type(other)} to nmod_mpoly") def isub(self, other): """ @@ -586,8 +622,10 @@ cdef class nmod_mpoly(flint_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") nmod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - else: + elif isinstance(other, int): nmod_mpoly_sub_ui((self).val, (self).val, other, self.ctx.val) + else: + raise NotImplementedError(f"cannot subtract {type(other)} from nmod_mpoly") def imul(self, other): """ @@ -607,8 +645,10 @@ cdef class nmod_mpoly(flint_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") nmod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - else: + elif isinstance(other, int): nmod_mpoly_scalar_mul_ui(self.val, (self).val, other, self.ctx.val) + else: + raise NotImplementedError(f"cannot multiple nmod_mpoly by {type(other)}") def monoms(self): """ @@ -902,6 +942,9 @@ cdef class nmod_mpoly(flint_mpoly): fmpz c nmod_mpoly u + if not self.ctx.is_prime(): + raise DomainError("factorisation with non-prime modulus is not supported") + nmod_mpoly_factor_init(fac, self.ctx.val) if not nmod_mpoly_factor(fac, self.val, self.ctx.val): raise RuntimeError("factorisation failed") @@ -941,6 +984,9 @@ cdef class nmod_mpoly(flint_mpoly): fmpz c nmod_mpoly u + if not self.ctx.is_prime(): + raise DomainError("factorisation with non-prime modulus is not supported") + nmod_mpoly_factor_init(fac, self.ctx.val) if not nmod_mpoly_factor_squarefree(fac, self.val, self.ctx.val): raise RuntimeError("factorisation failed") From 805fd3409b8385d41b1895539833bb2196dc8da0 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 29 Jul 2024 00:02:34 +1000 Subject: [PATCH 11/29] Replace numpy + memoryview with malloc --- src/flint/types/nmod_mpoly.pyx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 77f13e18..b2517cc5 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -64,7 +64,6 @@ from flint.flintlib.ulong_extras cimport n_is_prime from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib -import numpy as np cdef dict _nmod_mpoly_ctx_cache = {} @@ -578,8 +577,20 @@ cdef class nmod_mpoly(flint_mpoly): elif nargs > nvars: raise ValueError("more arguments provided than variables") - cdef ulong[::1] V = np.asarray(args, dtype=np.uint) - return nmod_mpoly_evaluate_all_ui(self.val, &(V[0]), self.ctx.val) + cdef: + ulong res + ulong *V = libc.stdlib.malloc(len(args) * sizeof(ulong)) + if V is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + try: + for i in range(len(args)): + V[i] = args[i] + res = nmod_mpoly_evaluate_all_ui(self.val, V, self.ctx.val) + finally: + libc.stdlib.free(V) + + return res def iadd(self, other): """ From 51a572e6920794e861a816f2087ff2384c024ad7 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 29 Jul 2024 00:38:57 +1000 Subject: [PATCH 12/29] Mirror f24f9aa --- src/flint/types/fmpz_mod_mpoly.pyx | 9 ++++++--- src/flint/types/nmod_mpoly.pyx | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 932f618b..b7e69916 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -290,6 +290,12 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __bool__(self): return not fmpz_mod_mpoly_is_zero(self.val, self.ctx.val) + def is_zero(self): + return fmpz_mod_mpoly_is_zero(self.val, self.ctx.val) + + def is_one(self): + return fmpz_mod_mpoly_is_one(self.val, self.ctx.val) + def __richcmp__(self, other, int op): if not (op == Py_EQ or op == Py_NE): return NotImplemented @@ -844,9 +850,6 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ return self.ctx - def is_one(self): - return fmpz_mod_mpoly_is_one(self.val, self.ctx.val) - def coefficient(self, slong i): """ Return the coefficient at index `i`. diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index b2517cc5..8c3f9ad6 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -270,6 +270,12 @@ cdef class nmod_mpoly(flint_mpoly): def __bool__(self): return not nmod_mpoly_is_zero(self.val, self.ctx.val) + def is_zero(self): + return nmod_mpoly_is_zero(self.val, self.ctx.val) + + def is_one(self): + return nmod_mpoly_is_one(self.val, self.ctx.val) + def __richcmp__(self, other, int op): if not (op == Py_EQ or op == Py_NE): return NotImplemented @@ -811,9 +817,6 @@ cdef class nmod_mpoly(flint_mpoly): """ return self.ctx - def is_one(self): - return nmod_mpoly_is_one(self.val, self.ctx.val) - def coefficient(self, slong i): """ Return the coefficient at index `i`. From accffa96869fa35ec90f5ed176d9728b6e7f492e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 8 Aug 2024 02:22:14 +1000 Subject: [PATCH 13/29] Support comparisons with more literals --- src/flint/test/test_all.py | 21 +++++++++++++-------- src/flint/types/fmpq_mpoly.pyx | 15 ++++++++++++--- src/flint/types/fmpz_mod_mpoly.pyx | 8 ++++++++ src/flint/types/fmpz_mpoly.pyx | 15 ++++++++++----- src/flint/types/nmod_mpoly.pyx | 13 ++++++++++--- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index c02f86e2..0ee017f6 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2685,13 +2685,13 @@ def _all_mpolys(): ( flint.nmod_mpoly, lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), - int, + lambda x: flint.nmod(x, 101), True, ), ( flint.nmod_mpoly, lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), - int, + lambda x: flint.nmod(x, 100), False, ), ] @@ -2767,10 +2767,15 @@ def quick_poly(): assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True - assert (P(1, ctx=ctx) == 1) is False - assert (P(1, ctx=ctx) != 1) is True - assert (1 == P(1, ctx=ctx)) is False - assert (1 != P(1, ctx=ctx)) is True + assert (P(1, ctx=ctx) == 1) is True + assert (P(1, ctx=ctx) != 1) is False + assert (1 == P(1, ctx=ctx)) is True + assert (1 != P(1, ctx=ctx)) is False + + assert (P(1, ctx=ctx) == S(1)) is True + assert (P(1, ctx=ctx) != S(1)) is False + assert (S(1) == P(1, ctx=ctx)) is True + assert (S(1) != P(1, ctx=ctx)) is False assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True @@ -2926,7 +2931,7 @@ def quick_poly(): else {k: ctx.modulus() + v for k, v in {(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}.items()} ) - for T in [int, S, lambda x: P(x, ctx=ctx)]: + for T in [int, S, int, lambda x: P(x, ctx=ctx)]: p = quick_poly() p -= T(1) q = quick_poly() @@ -2955,7 +2960,7 @@ def quick_poly(): (0, 1): 6 }) - for T in [int, S, lambda x: P(x, ctx=ctx)]: + for T in [int, S, int, lambda x: P(x, ctx=ctx)]: p = quick_poly() p *= T(2) q = quick_poly() diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index b3197247..44234b93 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -28,6 +28,8 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_divides, fmpq_mpoly_divrem, fmpq_mpoly_equal, + fmpq_mpoly_equal_fmpq, + fmpq_mpoly_equal_fmpz, fmpq_mpoly_evaluate_all_fmpq, fmpq_mpoly_evaluate_one_fmpq, fmpq_mpoly_gcd, @@ -256,11 +258,18 @@ cdef class fmpq_mpoly(flint_mpoly): return op == Py_NE elif typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): if (self).ctx is (other).ctx: - return (op == Py_NE) ^ bool( - fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val) - ) + return (op == Py_NE) ^ fmpq_mpoly_equal(self.val, (other).val, self.ctx.val) else: return op == Py_NE + elif typecheck(other, fmpq): + return (op == Py_NE) ^ fmpq_mpoly_equal_fmpq(self.val, (other).val, self.ctx.val) + elif typecheck(other, fmpz): + return (op == Py_NE) ^ fmpq_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) + elif isinstance(other, int): + other = any_as_fmpz(other) + if other is NotImplemented: + return NotImplemented + return (op == Py_NE) ^ fmpq_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) else: return NotImplemented diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index b7e69916..3ed7547b 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -26,6 +26,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_divides, fmpz_mod_mpoly_divrem, fmpz_mod_mpoly_equal, + fmpz_mod_mpoly_equal_fmpz, fmpz_mod_mpoly_evaluate_all_fmpz, fmpz_mod_mpoly_evaluate_one_fmpz, fmpz_mod_mpoly_gcd, @@ -308,6 +309,13 @@ cdef class fmpz_mod_mpoly(flint_mpoly): ) else: return op == Py_NE + elif typecheck(other, fmpz): + return (op == Py_NE) ^ fmpz_mod_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) + elif isinstance(other, int): + other = any_as_fmpz(other) + if other is NotImplemented: + return NotImplemented + return (op == Py_NE) ^ fmpz_mod_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) else: return NotImplemented diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 0e2ab9b9..243f43b4 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -24,13 +24,13 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_divides, fmpz_mpoly_divrem, fmpz_mpoly_equal, + fmpz_mpoly_equal_fmpz, fmpz_mpoly_evaluate_all_fmpz, fmpz_mpoly_evaluate_one_fmpz, fmpz_mpoly_gcd, fmpz_mpoly_gen, fmpz_mpoly_get_coeff_fmpz_fmpz, fmpz_mpoly_get_str_pretty, - fmpz_mpoly_get_term, fmpz_mpoly_get_term_coeff_fmpz, fmpz_mpoly_get_term_exp_fmpz, fmpz_mpoly_integral, @@ -234,13 +234,18 @@ cdef class fmpz_mpoly(flint_mpoly): return NotImplemented elif other is None: return op == Py_NE - elif typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): + elif typecheck(other, fmpz_mpoly): if (self).ctx is (other).ctx: - return (op == Py_NE) ^ bool( - fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val) - ) + return (op == Py_NE) ^ fmpz_mpoly_equal(self.val, (other).val, self.ctx.val) else: return op == Py_NE + elif typecheck(other, fmpz): + return (op == Py_NE) ^ fmpz_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) + elif isinstance(other, int): + other = any_as_fmpz(other) + if other is NotImplemented: + return NotImplemented + return (op == Py_NE) ^ fmpz_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) else: return NotImplemented diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 8c3f9ad6..637fb9cd 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -12,6 +12,8 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec +from flint.types.nmod cimport nmod + from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_add, @@ -26,6 +28,7 @@ from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_divides, nmod_mpoly_divrem, nmod_mpoly_equal, + nmod_mpoly_equal_ui, nmod_mpoly_evaluate_all_ui, nmod_mpoly_evaluate_one_ui, nmod_mpoly_gcd, @@ -283,11 +286,15 @@ cdef class nmod_mpoly(flint_mpoly): return op == Py_NE elif typecheck(self, nmod_mpoly) and typecheck(other, nmod_mpoly): if (self).ctx is (other).ctx: - return (op == Py_NE) ^ bool( - nmod_mpoly_equal((self).val, (other).val, (self).ctx.val) - ) + return (op == Py_NE) ^ nmod_mpoly_equal(self.val, (other).val, self.ctx.val) else: return op == Py_NE + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot compare different modulus {other.modulus()} vs {self.ctx.modulus()}") + return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, int(other), self.ctx.val) + elif isinstance(other, int): + return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, other, self.ctx.val) else: return NotImplemented From 48136d12dab5fb8d82a9dfd82818279ae806c4ad Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 8 Aug 2024 02:43:57 +1000 Subject: [PATCH 14/29] More nmod interop --- src/flint/test/test_all.py | 10 +++ src/flint/types/nmod_mpoly.pyx | 118 ++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 0ee017f6..7d0848e9 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2994,6 +2994,12 @@ def quick_poly(): assert quick_poly() % 1 == P(ctx=ctx) assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) + assert S(1) / P(1, ctx=ctx) == P(1, ctx=ctx) + assert quick_poly() / S(1) == quick_poly() + assert quick_poly() // S(1) == quick_poly() + assert quick_poly() % S(1) == P(ctx=ctx) + assert divmod(quick_poly(), S(1)) == (quick_poly(), P(ctx=ctx)) + if is_field: if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly): assert quick_poly() / 3 == mpoly({(0, 0): S(34), (0, 1): S(68), (1, 0): S(1), (2, 2): S(35)}) @@ -3011,6 +3017,10 @@ def quick_poly(): assert 1 % quick_poly() == P(1, ctx=ctx) assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + assert S(1) // quick_poly() == P(ctx=ctx) + assert S(1) % quick_poly() == P(1, ctx=ctx) + assert divmod(S(1), quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + assert raises(lambda: quick_poly() / None, TypeError) assert raises(lambda: quick_poly() // None, TypeError) assert raises(lambda: quick_poly() % None, TypeError) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 637fb9cd..6ead67bb 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -291,7 +291,7 @@ cdef class nmod_mpoly(flint_mpoly): return op == Py_NE elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot compare different modulus {other.modulus()} vs {self.ctx.modulus()}") + raise ValueError(f"cannot compare with different modulus {other.modulus()} vs {self.ctx.modulus()}") return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, int(other), self.ctx.val) elif isinstance(other, int): return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, other, self.ctx.val) @@ -364,6 +364,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) + return res elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) @@ -377,6 +383,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) + return res else: return NotImplemented @@ -388,6 +400,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, (self).val, int(other), self.ctx.val) + return res elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub_ui(res.val, (self).val, other, self.ctx.val) @@ -401,6 +419,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) return -res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, (self).val, int(other), self.ctx.val) + return -res else: return NotImplemented @@ -412,6 +436,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, (self).val, int(other), self.ctx.val) + return res elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) @@ -425,6 +455,12 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, (self).val, int(other), self.ctx.val) + return res else: return NotImplemented @@ -464,6 +500,18 @@ cdef class nmod_mpoly(flint_mpoly): o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + return (res, res2) + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + elif not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + res = create_nmod_mpoly(self.ctx) res2 = create_nmod_mpoly(self.ctx) nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) @@ -481,6 +529,16 @@ cdef class nmod_mpoly(flint_mpoly): o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) + return (res, res2) + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + res = create_nmod_mpoly(self.ctx) res2 = create_nmod_mpoly(self.ctx) nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) @@ -506,6 +564,17 @@ cdef class nmod_mpoly(flint_mpoly): o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) + return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + elif not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + res = create_nmod_mpoly(self.ctx) nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) return res @@ -525,6 +594,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) return res + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) + return res else: return NotImplemented @@ -555,6 +633,18 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + elif not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + res = create_nmod_mpoly(self.ctx) + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, int(other), self.ctx.val) + if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") else: return NotImplemented @@ -572,6 +662,16 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + res = create_nmod_mpoly(self.ctx) + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, int(other), self.ctx.val) + if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") else: return NotImplemented @@ -625,6 +725,10 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_add_ui((self).val, (self).val, other, self.ctx.val) + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") + nmod_mpoly_add_ui(self.val, (self).val, int(other), self.ctx.val) else: raise NotImplementedError(f"cannot add {type(other)} to nmod_mpoly") @@ -648,6 +752,10 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_sub_ui((self).val, (self).val, other, self.ctx.val) + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") + nmod_mpoly_sub_ui(self.val, (self).val, int(other), self.ctx.val) else: raise NotImplementedError(f"cannot subtract {type(other)} from nmod_mpoly") @@ -671,6 +779,10 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_scalar_mul_ui(self.val, (self).val, other, self.ctx.val) + elif typecheck(other, nmod): + if other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") + nmod_mpoly_scalar_mul_ui(self.val, (self).val, int(other), self.ctx.val) else: raise NotImplementedError(f"cannot multiple nmod_mpoly by {type(other)}") @@ -748,9 +860,9 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), v) for k, v in dict_args.items()) + args = tuple((self.ctx.variable_to_index(k), int(v)) for k, v in dict_args.items()) for _, v in args: - if not (isinstance(v, int) and v >= 0): + if not v >= 0: raise TypeError("constants must be non-negative integers") # Partial application with args in Z. We evaluate the polynomial one variable at a time From 84fbf9144eb4faff453f5fcefa1ca1afbfe2e4f8 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Tue, 13 Aug 2024 23:39:29 +1000 Subject: [PATCH 15/29] Fix Windows specific memory error (32 vs 64 bit issue) --- src/flint/types/nmod_mpoly.pyx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 6ead67bb..33f5e177 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -14,6 +14,7 @@ from flint.types.fmpz_vec cimport fmpz_vec from flint.types.nmod cimport nmod +from flint.flintlib.flint cimport FLINT_BITS from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_add, @@ -690,18 +691,22 @@ cdef class nmod_mpoly(flint_mpoly): elif nargs > nvars: raise ValueError("more arguments provided than variables") + args = [int(x) for x in args] + cdef: + # Using sizeof(ulong) here breaks on 64 windows machines because of the `ctypedef unsigned long ulong` in + # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. + ulong *vals = libc.stdlib.malloc(nargs * (FLINT_BITS // 4)) ulong res - ulong *V = libc.stdlib.malloc(len(args) * sizeof(ulong)) - if V is NULL: + if vals is NULL: raise MemoryError("malloc returned a null pointer") # pragma: no cover try: - for i in range(len(args)): - V[i] = args[i] - res = nmod_mpoly_evaluate_all_ui(self.val, V, self.ctx.val) + for i in range(nargs): + vals[i] = args[i] + res = nmod_mpoly_evaluate_all_ui(self.val, vals, self.ctx.val) finally: - libc.stdlib.free(V) + libc.stdlib.free(vals) return res @@ -895,7 +900,7 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly res nmod_mpoly_ctx res_ctx nmod_mpoly_vec C - slong i, nvars = self.ctx.nvars(), nargs = len(args) + slong nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: raise ValueError("not enough arguments provided") From f15b55a29f68d9493a13ca8990a157df4262294e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 14 Aug 2024 00:01:47 +1000 Subject: [PATCH 16/29] Add missing argument to `flint_scalar._any_as_self` --- src/flint/flint_base/flint_base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 97f82451..fc92bfc0 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -38,7 +38,7 @@ cdef class flint_scalar(flint_elem): def is_zero(self): return False - def _any_as_self(self): + def _any_as_self(self, other): return NotImplemented def _neg_(self): From e3120cf627dff1c1c4914bc7d1d8d78e48326d99 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 14 Aug 2024 00:09:57 +1000 Subject: [PATCH 17/29] Add modules to setup.py --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index d129b1a5..0146323d 100644 --- a/setup.py +++ b/setup.py @@ -97,9 +97,11 @@ ("flint.types.nmod_poly", ["src/flint/types/nmod_poly.pyx"]), ("flint.types.nmod_mat", ["src/flint/types/nmod_mat.pyx"]), ("flint.types.nmod_series", ["src/flint/types/nmod_series.pyx"]), + ("flint.types.nmod_mpoly", ["src/flint/types/nmod_mpoly.pyx"]), ("flint.types.fmpz_mod", ["src/flint/types/fmpz_mod.pyx"]), ("flint.types.fmpz_mod_poly", ["src/flint/types/fmpz_mod_poly.pyx"]), + ("flint.types.fmpz_mod_mpoly", ["src/flint/types/fmpz_mod_mpoly.pyx"]), ("flint.types.fmpz_mod_mat", ["src/flint/types/fmpz_mod_mat.pyx"]), ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), From abe80c19ac96699fcd59b166631c40ffc3efce41 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 14 Aug 2024 12:45:44 +1000 Subject: [PATCH 18/29] Remove duplicated ctypedef for nmod_t --- src/flint/flintlib/nmod.pxd | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/flint/flintlib/nmod.pxd b/src/flint/flintlib/nmod.pxd index b748f90f..c88b876b 100644 --- a/src/flint/flintlib/nmod.pxd +++ b/src/flint/flintlib/nmod.pxd @@ -1,11 +1,7 @@ -from flint.flintlib.flint cimport mp_limb_t, mp_bitcnt_t, ulong +from flint.flintlib.flint cimport mp_limb_t, mp_bitcnt_t, ulong, nmod_t from flint.flintlib.fmpz cimport fmpz_t cdef extern from "flint/nmod.h": - ctypedef struct nmod_t: - mp_limb_t n - mp_limb_t ninv - mp_bitcnt_t norm # TODO add macros # from here on is parsed From c740fec7897f84ef8463c31613b6048b57aed148 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Fri, 16 Aug 2024 20:57:33 +1000 Subject: [PATCH 19/29] Doc changes --- src/flint/types/fmpq_mpoly.pyx | 2 +- src/flint/types/fmpz_mod_mpoly.pyx | 4 ++-- src/flint/types/fmpz_mpoly.pyx | 2 +- src/flint/types/nmod_mpoly.pyx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 44234b93..4e392bc6 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -925,7 +925,7 @@ cdef class fmpq_mpoly(flint_mpoly): >>> Zm = fmpq_mpoly >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) - >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 3ed7547b..300e3940 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -246,7 +246,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): cdef class fmpz_mod_mpoly(flint_mpoly): """ The *fmpz_mod_mpoly* type represents sparse multivariate polynomials over - the integers. + the integers modulo `n`, for large `n`. """ def __cinit__(self): @@ -989,7 +989,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> Zm = fmpz_mod_mpoly >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) - >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 243f43b4..3cf85394 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -903,7 +903,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> Zm = fmpz_mpoly >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) - >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 33f5e177..2da06c05 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1069,7 +1069,7 @@ cdef class nmod_mpoly(flint_mpoly): >>> Zm = nmod_mpoly >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) - >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() From 72e220ee3e250873231596eb94758425b20247e7 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Fri, 16 Aug 2024 20:58:12 +1000 Subject: [PATCH 20/29] sizeof(ulong) fix --- src/flint/flintlib/flint.pxd | 4 ++++ src/flint/types/nmod_mpoly.pyx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/flint/flintlib/flint.pxd b/src/flint/flintlib/flint.pxd index 993a61e3..60be1da3 100644 --- a/src/flint/flintlib/flint.pxd +++ b/src/flint/flintlib/flint.pxd @@ -50,6 +50,10 @@ cdef extern from *: """ cdef extern from "flint/flint.h": + """ + #define SIZEOF_ULONG sizeof(ulong) + """ + int SIZEOF_ULONG const char * FLINT_VERSION const int __FLINT_RELEASE const int FLINT_BITS diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 2da06c05..6da7ec81 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -14,7 +14,7 @@ from flint.types.fmpz_vec cimport fmpz_vec from flint.types.nmod cimport nmod -from flint.flintlib.flint cimport FLINT_BITS +from flint.flintlib.flint cimport SIZEOF_ULONG from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_add, @@ -696,7 +696,7 @@ cdef class nmod_mpoly(flint_mpoly): cdef: # Using sizeof(ulong) here breaks on 64 windows machines because of the `ctypedef unsigned long ulong` in # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. - ulong *vals = libc.stdlib.malloc(nargs * (FLINT_BITS // 4)) + ulong *vals = libc.stdlib.malloc(nargs * SIZEOF_ULONG) ulong res if vals is NULL: raise MemoryError("malloc returned a null pointer") # pragma: no cover From 4526d2b7ee2d87df8e6cc20567ea0968f09a2eac Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Fri, 16 Aug 2024 21:01:04 +1000 Subject: [PATCH 21/29] Simplify call argument checks --- src/flint/types/fmpq_mpoly.pyx | 13 +++---------- src/flint/types/fmpz_mod_mpoly.pyx | 13 +++---------- src/flint/types/fmpz_mpoly.pyx | 13 +++---------- src/flint/types/nmod_mpoly.pyx | 7 ++----- 4 files changed, 11 insertions(+), 35 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 4e392bc6..88219b54 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -542,17 +542,10 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq vres slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") - - args_fmpq = tuple(any_as_fmpq(v) for v in args) - for arg in args_fmpq: - if arg is NotImplemented: - raise TypeError(f"cannot coerce argument ('{arg}') to fmpq") + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") - V = fmpq_vec(args_fmpq, double_indirect=True) + V = fmpq_vec(args, double_indirect=True) vres = fmpq.__new__(fmpq) if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 300e3940..080c78ce 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -605,17 +605,10 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz vres slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") - - args_fmpz = tuple(any_as_fmpz(v) for v in args) - for arg in args_fmpz: - if arg is NotImplemented: - raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") # TODO: fix for all of them - V = fmpz_vec(args_fmpz, double_indirect=True) + V = fmpz_vec(args, double_indirect=True) vres = fmpz.__new__(fmpz) fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) return vres diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 3cf85394..b964050d 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -520,17 +520,10 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz vres slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") - - args_fmpz = tuple(any_as_fmpz(v) for v in args) - for arg in args_fmpz: - if arg is NotImplemented: - raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") - V = fmpz_vec(args_fmpz, double_indirect=True) + V = fmpz_vec(args, double_indirect=True) vres = fmpz.__new__(fmpz) if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 6da7ec81..105ff1e7 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -686,13 +686,10 @@ cdef class nmod_mpoly(flint_mpoly): cdef: slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") args = [int(x) for x in args] - cdef: # Using sizeof(ulong) here breaks on 64 windows machines because of the `ctypedef unsigned long ulong` in # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. From 043ca9dd9d047c78af7fe4ca8a01ee0ffa9341de Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 00:56:47 +1000 Subject: [PATCH 22/29] Add support for fmpz_mod and nmod interop --- src/flint/test/test_all.py | 32 +-- src/flint/types/fmpz_mod_mpoly.pyx | 312 +++++++++++++++++++++-------- src/flint/types/fmpz_vec.pyx | 14 +- src/flint/types/nmod_mpoly.pyx | 252 +++++++++++++++++++---- 4 files changed, 458 insertions(+), 152 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 74e060e7..7e88a891 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2755,13 +2755,13 @@ def _all_mpolys(): ( flint.fmpz_mod_mpoly, lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), - flint.fmpz, + lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(101)), True, ), ( flint.fmpz_mod_mpoly, lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), - flint.fmpz, + lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(100)), False, ), ( @@ -2981,17 +2981,13 @@ def quick_poly(): assert +quick_poly() \ == quick_poly() - assert -quick_poly() \ - == mpoly( - {(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4} if P is not flint.nmod_mpoly - else {k: ctx.modulus() + v for k, v in {(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}.items()} - ) + assert -quick_poly() == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) assert quick_poly() \ + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - for T in [int, S, lambda x: P(x, ctx=ctx)]: + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p += T(1) q = quick_poly() @@ -3008,22 +3004,15 @@ def quick_poly(): assert raises(lambda: quick_poly().iadd(None), NotImplementedError) assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ - == mpoly( - {(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4} if P is not flint.nmod_mpoly - else {k: ctx.modulus() + v for k, v in {(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}.items()} - ) + == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) - for T in [int, S, int, lambda x: P(x, ctx=ctx)]: + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p -= T(1) q = quick_poly() assert q.isub(T(1)) is None assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) - assert T(1) - quick_poly() == \ - mpoly( - {(0, 1): -2, (1, 0): -3, (2, 2): -4} if P is not flint.nmod_mpoly - else {k: ctx.modulus() + v for k, v in {(0, 1): -2, (1, 0): -3, (2, 2): -4}.items()} - ) + assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) @@ -3042,7 +3031,7 @@ def quick_poly(): (0, 1): 6 }) - for T in [int, S, int, lambda x: P(x, ctx=ctx)]: + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p *= T(2) q = quick_poly() @@ -3083,10 +3072,7 @@ def quick_poly(): assert divmod(quick_poly(), S(1)) == (quick_poly(), P(ctx=ctx)) if is_field: - if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly): - assert quick_poly() / 3 == mpoly({(0, 0): S(34), (0, 1): S(68), (1, 0): S(1), (2, 2): S(35)}) - else: - assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) + assert quick_poly() / 3 == mpoly({(0, 0): S(1) / 3, (0, 1): S(2) / 3, (1, 0): S(1), (2, 2): S(4) / 3}) else: assert raises(lambda: quick_poly() / 3, DomainError) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 080c78ce..b12c506e 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -11,6 +11,7 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec +from flint.types.fmpz_mod cimport fmpz_mod from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.fmpz_mod_mpoly cimport ( @@ -227,17 +228,34 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): res = create_fmpz_mod_mpoly(self) - for i, (k, v) in enumerate(d.items()): - o = any_as_fmpz(v) - if o is NotImplemented: - raise TypeError(f"cannot coerce coefficient '{v}' to fmpz") - elif len(k) != nvars: - raise ValueError(f"expected {nvars} exponents, got {len(k)}") + for i, (exps, coeff) in enumerate(d.items()): + if len(exps) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(exps)}") + elif not coeff: + continue - exp_vec = fmpz_vec(k) + exp_vec = fmpz_vec(exps) - if o: - fmpz_mod_mpoly_push_term_fmpz_ffmpz(res.val, (o).val, exp_vec.val, self.val) + if isinstance(coeff, int) or typecheck(coeff, fmpz): + coeff_fmpz = any_as_fmpz(coeff) + if coeff_fmpz is NotImplemented: + raise TypeError(f"cannot coerce '{repr(coeff)}' to fmpz") + + fmpz_mod_mpoly_push_term_fmpz_ffmpz( + res.val, + (coeff_fmpz).val, + exp_vec.val, + self.val + ) + elif typecheck(coeff, fmpz_mod): + fmpz_mod_mpoly_push_term_fmpz_ffmpz( + res.val, + (coeff).val, + exp_vec.val, + self.val + ) + else: + raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") fmpz_mod_mpoly_sort_terms(res.val, self.val) return res @@ -279,12 +297,17 @@ cdef class fmpz_mod_mpoly(flint_mpoly): if fmpz_mod_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: raise ValueError("unable to parse fmpz_mod_mpoly from string") fmpz_mod_mpoly_sort_terms(self.val, self.ctx.val) + elif typecheck(val, fmpz_mod): + if ctx is None: + raise ValueError("need context to convert fmpz_mod to fmpz_mod_mpoly") + init_fmpz_mod_mpoly(self, ctx) + fmpz_mod_mpoly_set_fmpz(self.val, (val).val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: raise TypeError("cannot create fmpz_mod_mpoly from type %s" % type(val)) - if ctx is None: - raise ValueError("need context to convert fmpz to fmpz_mod_mpoly") + elif ctx is None: + raise ValueError("need context to convert fmpz to fmpz_mod_mpoly") init_fmpz_mod_mpoly(self, ctx) fmpz_mod_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) @@ -311,6 +334,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): return op == Py_NE elif typecheck(other, fmpz): return (op == Py_NE) ^ fmpz_mod_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) + elif typecheck(other, fmpz_mod): + return (op == Py_NE) ^ fmpz_mod_mpoly_equal_fmpz(self.val, (other).val, self.ctx.val) elif isinstance(other, int): other = any_as_fmpz(other) if other is NotImplemented: @@ -365,16 +390,23 @@ cdef class fmpz_mod_mpoly(flint_mpoly): cdef: slong nvars = self.ctx.nvars() - coeff = any_as_fmpz(y) - if coeff is NotImplemented: - raise TypeError("provided coefficient not coercible to fmpz") - elif not isinstance(x, tuple): + if not isinstance(x, tuple): raise TypeError("exponent vector index is not a tuple") elif len(x) != nvars: raise ValueError("exponent vector provided does not match number of variables") - exp_vec = fmpz_vec(x, double_indirect=True) - fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) + + if typecheck(y, fmpz_mod): + if self.ctx.modulus() != (y).ctx.modulus(): + raise ValueError( + f"cannot set a coefficient with different modulus {(y).ctx.modulus()} vs {self.ctx.modulus()}" + ) + fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (y).val, exp_vec.double_indirect, self.ctx.val) + else: + coeff = any_as_fmpz(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpz") + fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) def __neg__(self): cdef fmpz_mod_mpoly res @@ -390,6 +422,14 @@ cdef class fmpz_mod_mpoly(flint_mpoly): res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res else: other = any_as_fmpz(other) if other is not NotImplemented: @@ -400,11 +440,20 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __radd__(self, other): cdef fmpz_mod_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + if typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res return NotImplemented def __sub__(self, other): @@ -415,6 +464,14 @@ cdef class fmpz_mod_mpoly(flint_mpoly): res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res else: other = any_as_fmpz(other) if other is not NotImplemented: @@ -425,11 +482,20 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __rsub__(self, other): cdef fmpz_mod_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + if typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) return -res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return -res return NotImplemented def __mul__(self, other): @@ -440,6 +506,14 @@ cdef class fmpz_mod_mpoly(flint_mpoly): res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res else: other = any_as_fmpz(other) if other is not NotImplemented: @@ -450,11 +524,20 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __rmul__(self, other): cdef fmpz_mod_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + if typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return res return NotImplemented def __pow__(self, other, modulus): @@ -474,37 +557,46 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __divmod__(self, other): cdef fmpz_mod_mpoly res, res2 - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod_mpoly): + if typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mod_mpoly(self.ctx) res2 = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) return (res, res2) - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - other = fmpz_mod_mpoly(other, self.ctx) - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - res = create_fmpz_mod_mpoly(self.ctx) - res2 = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) + elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + res2 = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) return NotImplemented def __rdivmod__(self, other): cdef fmpz_mod_mpoly res, res2 - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - other = any_as_fmpz(other) - if other is not NotImplemented: + if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + other = fmpz_mod_mpoly(other, self.ctx) res = create_fmpz_mod_mpoly(self.ctx) res2 = create_fmpz_mod_mpoly(self.ctx) @@ -514,35 +606,44 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __floordiv__(self, other): cdef fmpz_mod_mpoly res - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod_mpoly): + if typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res + elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not other: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res return NotImplemented def __rfloordiv__(self, other): cdef fmpz_mod_mpoly res - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - other = any_as_fmpz(other) - if other is not NotImplemented: + if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + other = fmpz_mod_mpoly(other, self.ctx) res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_div(res.val, (other).val, self.val, res.ctx.val) @@ -553,11 +654,11 @@ cdef class fmpz_mod_mpoly(flint_mpoly): cdef: fmpz_mod_mpoly res, div - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod_mpoly): + if typecheck(other, fmpz_mod_mpoly): if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif self.ctx is not (other).ctx: raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") @@ -566,32 +667,42 @@ cdef class fmpz_mod_mpoly(flint_mpoly): return res else: raise DomainError("fmpz_mod_mpoly division is not exact") - else: - o = any_as_fmpz(other) - if o is NotImplemented: - return NotImplemented - elif not o: + elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not other: raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + + div = fmpz_mod_mpoly(other, self.ctx) res = create_fmpz_mod_mpoly(self.ctx) - div = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_set_fmpz(div.val, (o).val, self.ctx.val) if fmpz_mod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): return res else: raise DomainError("fmpz_mod_mpoly division is not exact") + return NotImplemented def __rtruediv__(self, other): cdef fmpz_mod_mpoly res - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - o = any_as_fmpz(other) - if o is NotImplemented: - return NotImplemented - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_set_fmpz(res.val, (o).val, self.ctx.val) - return res / self + if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): + if not self: + raise ZeroDivisionError("fmpz_mod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + other = fmpz_mod_mpoly(other, self.ctx) + res = create_fmpz_mod_mpoly(self.ctx) + if fmpz_mod_mpoly_divides(res.val, (other).val, self.val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mod_mpoly division is not exact") + return NotImplemented def __mod__(self, other): return divmod(self, other)[1] @@ -632,6 +743,13 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) return + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + fmpz_mod_mpoly_add_fmpz(self.val, self.val, (other).val, self.ctx.val) + return else: zval = any_as_fmpz(other) if zval is not NotImplemented: @@ -658,6 +776,13 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) return + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + fmpz_mod_mpoly_sub_fmpz(self.val, self.val, (other).val, self.ctx.val) + return else: other = any_as_fmpz(other) if other is not NotImplemented: @@ -684,6 +809,13 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) return + elif typecheck(other, fmpz_mod): + if self.ctx.modulus() != (other).ctx.modulus(): + raise ValueError( + f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" + ) + fmpz_mod_mpoly_scalar_mul_fmpz(self.val, self.val, (other).val, self.ctx.val) + return else: other = any_as_fmpz(other) if other is not NotImplemented: @@ -775,17 +907,21 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), any_as_fmpz(v)) for k, v in dict_args.items()) - for _, v in args: - if v is NotImplemented: - raise TypeError(f"cannot coerce argument ('{v}') to fmpz") + args = tuple((self.ctx.variable_to_index(k), v) for k, v in dict_args.items()) # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_set(res.val, self.val, self.ctx.val) for i, arg in args: - fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) + if typecheck(arg, fmpz_mod): + fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) + else: + arg_fmpz = any_as_fmpz(arg) + if arg_fmpz is NotImplemented: + raise TypeError(f"cannot coerce {type(arg)} to fmpz") + fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) + return res def compose(self, *args, ctx=None) -> fmpz_mod_mpoly: diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index d5d7e27e..0a1c31b5 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -3,6 +3,9 @@ from flint.flintlib.flint cimport slong from flint.flintlib.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear from flint.types.fmpz cimport fmpz, any_as_fmpz +from flint.types.fmpz_mod cimport fmpz_mod + +from flint.utils.typecheck cimport typecheck cimport libc.stdlib @@ -52,11 +55,14 @@ cdef class fmpz_vec: elif not 0 <= x < self.length: raise IndexError("index out of range") - y = any_as_fmpz(y) - if y is NotImplemented: - raise TypeError("argument is not coercible to fmpz") + if typecheck(y, fmpz_mod): + fmpz_set(&self.val[x], (y).val) + else: + y = any_as_fmpz(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpz") - fmpz_set(&self.val[x], (y).val) + fmpz_set(&self.val[x], (y).val) def __len__(self): return self.length diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 105ff1e7..e61eb9b8 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -16,6 +16,7 @@ from flint.types.nmod cimport nmod from flint.flintlib.flint cimport SIZEOF_ULONG from flint.flintlib.fmpz cimport fmpz_set +from flint.flintlib.nmod cimport fmpz_get_nmod from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_add, nmod_mpoly_add_ui, @@ -216,12 +217,29 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): res = create_nmod_mpoly(self) - for i, (k, v) in enumerate(d.items()): - if len(k) != nvars: - raise ValueError(f"expected {nvars} exponents, got {len(k)}") - elif v: - exp_vec = fmpz_vec(k) - nmod_mpoly_push_term_ui_ffmpz(res.val, v, exp_vec.val, self.val) + for i, (exps, coeff) in enumerate(d.items()): + if len(exps) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(exps)}") + elif not coeff: + continue + + exp_vec = fmpz_vec(exps) + + if isinstance(coeff, int) or typecheck(coeff, fmpz): + coeff_fmpz = any_as_fmpz(coeff) + if coeff_fmpz is NotImplemented: + raise TypeError(f"cannot coerce '{repr(coeff)}' to fmpz") + + nmod_mpoly_push_term_ui_ffmpz( + res.val, + fmpz_get_nmod((coeff_fmpz).val, self.val.mod), + exp_vec.val, + self.val + ) + elif typecheck(coeff, nmod): + nmod_mpoly_push_term_ui_ffmpz(res.val, int(coeff), exp_vec.val, self.val) + else: + raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") nmod_mpoly_sort_terms(res.val, self.val) return res @@ -283,8 +301,6 @@ cdef class nmod_mpoly(flint_mpoly): def __richcmp__(self, other, int op): if not (op == Py_EQ or op == Py_NE): return NotImplemented - elif other is None: - return op == Py_NE elif typecheck(self, nmod_mpoly) and typecheck(other, nmod_mpoly): if (self).ctx is (other).ctx: return (op == Py_NE) ^ nmod_mpoly_equal(self.val, (other).val, self.ctx.val) @@ -292,8 +308,14 @@ cdef class nmod_mpoly(flint_mpoly): return op == Py_NE elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot compare with different modulus {other.modulus()} vs {self.ctx.modulus()}") + return op == Py_NE return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, int(other), self.ctx.val) + elif typecheck(other, fmpz): + return (op == Py_NE) ^ nmod_mpoly_equal_ui( + self.val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) elif isinstance(other, int): return (op == Py_NE) ^ nmod_mpoly_equal_ui(self.val, other, self.ctx.val) else: @@ -349,7 +371,26 @@ cdef class nmod_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") exp_vec = fmpz_vec(x, double_indirect=True) - nmod_mpoly_set_coeff_ui_fmpz(self.val, y, exp_vec.double_indirect, self.ctx.val) + if isinstance(y, int): + nmod_mpoly_set_coeff_ui_fmpz(self.val, y, exp_vec.double_indirect, self.ctx.val) + elif typecheck(y, fmpz): + nmod_mpoly_set_coeff_ui_fmpz( + self.val, + fmpz_get_nmod((y).val, self.ctx.val.mod), + exp_vec.double_indirect, + self.ctx.val + ) + elif typecheck(y, nmod): + if y.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot add with different modulus {y.modulus()} vs {self.ctx.modulus()}") + nmod_mpoly_set_coeff_ui_fmpz( + self.val, + int(y), + exp_vec.double_indirect, + self.ctx.val + ) + else: + raise TypeError(f"cannot set coefficient to type {type(y)}") def __neg__(self): cdef nmod_mpoly res @@ -365,6 +406,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -384,6 +434,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) return res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -401,6 +460,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -420,6 +488,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) return -res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return -res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -437,6 +514,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -456,6 +542,15 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) return res + elif typecheck(other, fmpz): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui( + res.val, + (self).val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -484,11 +579,11 @@ cdef class nmod_mpoly(flint_mpoly): def __divmod__(self, other): cdef nmod_mpoly res, res2, o - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, nmod_mpoly): + if typecheck(other, nmod_mpoly): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_nmod_mpoly(self.ctx) @@ -498,18 +593,34 @@ cdef class nmod_mpoly(flint_mpoly): elif isinstance(other, int): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + return (res, res2) + elif typecheck(other, fmpz): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + res = create_nmod_mpoly(self.ctx) res2 = create_nmod_mpoly(self.ctx) nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) return (res, res2) elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - elif not other: + if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) @@ -522,14 +633,22 @@ cdef class nmod_mpoly(flint_mpoly): def __rdivmod__(self, other): cdef nmod_mpoly res, res2, o - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: + if not self: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif isinstance(other, int): o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) + return (res, res2) + elif typecheck(other, fmpz): + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + res = create_nmod_mpoly(self.ctx) res2 = create_nmod_mpoly(self.ctx) nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) @@ -549,11 +668,11 @@ cdef class nmod_mpoly(flint_mpoly): def __floordiv__(self, other): cdef nmod_mpoly res, o - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, nmod_mpoly): + if typecheck(other, nmod_mpoly): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_nmod_mpoly(self.ctx) @@ -562,17 +681,32 @@ cdef class nmod_mpoly(flint_mpoly): elif isinstance(other, int): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) + return res + elif typecheck(other, fmpz): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + res = create_nmod_mpoly(self.ctx) nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) return res elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - elif not other: + if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) @@ -584,10 +718,10 @@ cdef class nmod_mpoly(flint_mpoly): def __rfloordiv__(self, other): cdef nmod_mpoly res, o - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: + if not self: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif isinstance(other, int): o = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(o.val, other, o.ctx.val) @@ -595,6 +729,13 @@ cdef class nmod_mpoly(flint_mpoly): res = create_nmod_mpoly(self.ctx) nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) return res + elif typecheck(other, fmpz): + o = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) + return res elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -611,11 +752,11 @@ cdef class nmod_mpoly(flint_mpoly): cdef: nmod_mpoly res, div - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, nmod_mpoly): + if typecheck(other, nmod_mpoly): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif self.ctx is not (other).ctx: raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") @@ -627,6 +768,8 @@ cdef class nmod_mpoly(flint_mpoly): elif isinstance(other, int): if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") res = create_nmod_mpoly(self.ctx) div = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(div.val, other, self.ctx.val) @@ -634,11 +777,26 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") + elif typecheck(other, fmpz): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, fmpz_get_nmod((other).val, self.ctx.val.mod), div.ctx.val) + + res = create_nmod_mpoly(self.ctx) + if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - elif not other: + if not other: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + elif other.modulus() != self.ctx.modulus(): + raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") res = create_nmod_mpoly(self.ctx) div = create_nmod_mpoly(self.ctx) nmod_mpoly_set_ui(div.val, int(other), self.ctx.val) @@ -651,10 +809,10 @@ cdef class nmod_mpoly(flint_mpoly): def __rtruediv__(self, other): cdef nmod_mpoly res - if not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif not self: + if not self: raise ZeroDivisionError("nmod_mpoly division by zero") + elif not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") elif isinstance(other, int): res = create_nmod_mpoly(self.ctx) div = create_nmod_mpoly(self.ctx) @@ -663,6 +821,15 @@ cdef class nmod_mpoly(flint_mpoly): return res else: raise DomainError("nmod_mpoly division is not exact") + elif typecheck(other, fmpz): + div = create_nmod_mpoly(self.ctx) + nmod_mpoly_set_ui(div.val, fmpz_get_nmod((other).val, self.ctx.val.mod), div.ctx.val) + + res = create_nmod_mpoly(self.ctx) + if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): + return res + else: + raise DomainError("nmod_mpoly division is not exact") elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -727,6 +894,8 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_add_ui((self).val, (self).val, other, self.ctx.val) + elif typecheck(other, fmpz): + nmod_mpoly_add_ui(self.val, self.val, fmpz_get_nmod((other).val, self.ctx.val.mod), self.ctx.val) elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -754,6 +923,8 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_sub_ui((self).val, (self).val, other, self.ctx.val) + elif typecheck(other, fmpz): + nmod_mpoly_sub_ui(self.val, self.val, fmpz_get_nmod((other).val, self.ctx.val.mod), self.ctx.val) elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") @@ -781,6 +952,13 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) elif isinstance(other, int): nmod_mpoly_scalar_mul_ui(self.val, (self).val, other, self.ctx.val) + elif typecheck(other, fmpz): + nmod_mpoly_scalar_mul_ui( + self.val, + self.val, + fmpz_get_nmod((other).val, self.ctx.val.mod), + self.ctx.val + ) elif typecheck(other, nmod): if other.modulus() != self.ctx.modulus(): raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") From f2fa712dae84e2caf36b045a4f3c5e2dc08dbbce Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 13:45:13 +1000 Subject: [PATCH 23/29] Wrong variable --- src/flint/types/fmpz_mod_mpoly.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index b12c506e..d5ee4303 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -920,7 +920,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): arg_fmpz = any_as_fmpz(arg) if arg_fmpz is NotImplemented: raise TypeError(f"cannot coerce {type(arg)} to fmpz") - fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) + fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg_fmpz).val, self.ctx.val) return res From 9abc8d21a8de41788083ece5896e7deb115ffb21 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 13:45:47 +1000 Subject: [PATCH 24/29] Generic add and divmod POC methods --- src/flint/flint_base/flint_base.pyx | 60 +++++ src/flint/types/nmod_mpoly.pyx | 332 ++++++++++++++++------------ 2 files changed, 251 insertions(+), 141 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index fc92bfc0..b2a78e43 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -11,6 +11,7 @@ from flint.utils.typecheck cimport typecheck cimport libc.stdlib from typing import Optional +from flint.utils.flint_exceptions import IncompatibleContextError FLINT_BITS = _FLINT_BITS @@ -369,6 +370,18 @@ cdef class flint_mpoly_context(flint_elem): nametup=ctx.names() ) + def any_as_scalar(self, other): + raise NotImplementedError("abstract method") + + def scalar_as_mpoly(self, other): + raise NotImplementedError("abstract method") + + def compatible_context_check(self, other): + if not typecheck(other, type(self)): + raise TypeError(f"type {type(other)} is not {type(self)}") + elif other is not self: + raise IncompatibleContextError(f"{other} is not {self}") + cdef class flint_mpoly(flint_elem): """ @@ -381,6 +394,53 @@ cdef class flint_mpoly(flint_elem): def to_dict(self): return {self.monomial(i): self.coefficient(i) for i in range(len(self))} + def _division_check(self, other): + if not other: + raise ZeroDivisionError("nmod_mpoly division by zero") + + def _add_scalar_(self, other): + return NotImplemented + + def _add_mpoly_(self, other): + return NotImplemented + + def __add__(self, other): + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + return self._add_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + return self._add_scalar_(other) + + def __radd__(self, other): + return self.__add__(other) + + def __divmod__(self, other): + if typecheck(other, type(self)): + self._division_check(other) + self.context().compatible_context_check(other.context()) + return self._divmod_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(other) + other = self.context().scalar_as_mpoly(other) + return self._divmod_mpoly_(other) + + def __rdivmod__(self, other): + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(self) + other = self.context().scalar_as_mpoly(other) + return other._divmod_mpoly_(self) + def __contains__(self, x): """ Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index e61eb9b8..7f0fa525 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -11,6 +11,7 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec +from flint.types.fmpz_mod cimport fmpz_mod from flint.types.nmod cimport nmod @@ -120,6 +121,34 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): raise ValueError("must provide either `names` or `nametup`") return key + def any_as_scalar(self, other): + if isinstance(other, int): + try: + return other + except OverflowError: + return (other % self.val.mod) + elif typecheck(other, nmod): + val = other + if val.modulus() != self.modulus(): + raise DomainError( + f"modulus does not match, got {val.modulus()}, expected {self.modulus()}" + ) + return val.val + elif typecheck(other, fmpz): + return fmpz_get_nmod((other).val, self.val.mod) + elif typecheck(other, fmpz_mod): + if other.ctx.modulus() != self.modulus(): + raise DomainError( + f"modulus does not match, got {other.ctx.modulus()}, expected {self.modulus()}" + ) + return fmpz_get_nmod((other).val, self.val.mod) + else: + return NotImplemented + + def scalar_as_mpoly(self, other: ulong): + # non-ulong scalars should first be converted via cls.any_as_scalar + return self.constant(other) + def nvars(self): """ Return the number of variables in the context @@ -289,6 +318,11 @@ cdef class nmod_mpoly(flint_mpoly): else: raise TypeError(f"cannot construct a nmod_mpoly from a {type(val)}") + def _division_check(self, other): + super()._division_check(other) + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + def __bool__(self): return not nmod_mpoly_is_zero(self.val, self.ctx.val) @@ -398,59 +432,69 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_neg(res.val, (self).val, res.ctx.val) return res - def __add__(self, other): - cdef nmod_mpoly res - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) - return res - elif isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - return res - else: - return NotImplemented + def _add_scalar_(self, other: ulong): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add_ui(res.val, self.val, other, self.ctx.val) + return res - def __radd__(self, other): - cdef nmod_mpoly res - if isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - return res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) - return res - else: - return NotImplemented + def _add_mpoly_(self, other: nmod_mpoly): + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_add(res.val, self.val, other.val, res.ctx.val) + return res + + # def __add__(self, other): + # cdef nmod_mpoly res + # if typecheck(other, nmod_mpoly): + # if (self).ctx is not (other).ctx: + # raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + # return res + # elif typecheck(other, fmpz): + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui( + # res.val, + # (self).val, + # fmpz_get_nmod((other).val, self.ctx.val.mod), + # self.ctx.val + # ) + # return res + # elif typecheck(other, nmod): + # if other.modulus() != self.ctx.modulus(): + # raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) + # return res + # elif isinstance(other, int): + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) + # return res + # else: + # return NotImplemented + + # def __radd__(self, other): + # cdef nmod_mpoly res + # if isinstance(other, int): + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) + # return res + # elif typecheck(other, fmpz): + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui( + # res.val, + # (self).val, + # fmpz_get_nmod((other).val, self.ctx.val.mod), + # self.ctx.val + # ) + # return res + # elif typecheck(other, nmod): + # if other.modulus() != self.ctx.modulus(): + # raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") + # res = create_nmod_mpoly(self.ctx) + # nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) + # return res + # else: + # return NotImplemented def __sub__(self, other): cdef nmod_mpoly res @@ -576,95 +620,101 @@ cdef class nmod_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return res - def __divmod__(self, other): - cdef nmod_mpoly res, res2, o - - if typecheck(other, nmod_mpoly): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - elif isinstance(other, int): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - return (res, res2) - elif typecheck(other, fmpz): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - return (res, res2) - elif typecheck(other, nmod): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - return (res, res2) - else: - return NotImplemented - - def __rdivmod__(self, other): - cdef nmod_mpoly res, res2, o - if not self: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif isinstance(other, int): - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - return (res, res2) - elif typecheck(other, fmpz): - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - return (res, res2) - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - return (res, res2) - else: - return NotImplemented + def _divmod_mpoly_(self, other: nmod_mpoly): + res = create_nmod_mpoly(self.ctx) + res2 = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(res.val, res2.val, self.val, other.val, res.ctx.val) + return (res, res2) + + # def __divmod__(self, other): + # cdef nmod_mpoly res, res2, o + + # if typecheck(other, nmod_mpoly): + # if not other: + # raise ZeroDivisionError("nmod_mpoly division by zero") + # elif not self.ctx.is_prime(): + # raise DomainError("division with non-prime modulus is not supported") + # elif (self).ctx is not (other).ctx: + # raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + # return (res, res2) + # elif isinstance(other, int): + # if not other: + # raise ZeroDivisionError("nmod_mpoly division by zero") + # elif not self.ctx.is_prime(): + # raise DomainError("division with non-prime modulus is not supported") + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, other, o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + # return (res, res2) + # elif typecheck(other, fmpz): + # if not other: + # raise ZeroDivisionError("nmod_mpoly division by zero") + # elif not self.ctx.is_prime(): + # raise DomainError("division with non-prime modulus is not supported") + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + # return (res, res2) + # elif typecheck(other, nmod): + # if not other: + # raise ZeroDivisionError("nmod_mpoly division by zero") + # elif not self.ctx.is_prime(): + # raise DomainError("division with non-prime modulus is not supported") + # elif other.modulus() != self.ctx.modulus(): + # raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) + # return (res, res2) + # else: + # return NotImplemented + + # def __rdivmod__(self, other): + # cdef nmod_mpoly res, res2, o + # if not self: + # raise ZeroDivisionError("nmod_mpoly division by zero") + # elif not self.ctx.is_prime(): + # raise DomainError("division with non-prime modulus is not supported") + # elif isinstance(other, int): + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, other, o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) + # return (res, res2) + # elif typecheck(other, fmpz): + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) + # return (res, res2) + # elif typecheck(other, nmod): + # if other.modulus() != self.ctx.modulus(): + # raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") + # o = create_nmod_mpoly(self.ctx) + # nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) + + # res = create_nmod_mpoly(self.ctx) + # res2 = create_nmod_mpoly(self.ctx) + # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) + # return (res, res2) + # else: + # return NotImplemented def __floordiv__(self, other): cdef nmod_mpoly res, o From e2b6369a6dcdec2ed423d5d65be7f65afe0c725e Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 15:18:31 +1000 Subject: [PATCH 25/29] Significantly clean up nmod_mpoly type conversion Other types will come later --- src/flint/flint_base/flint_base.pyx | 230 +++++++++++ src/flint/types/nmod_mpoly.pyx | 614 +++------------------------- 2 files changed, 293 insertions(+), 551 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index b2a78e43..5269baba 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -13,6 +13,8 @@ cimport libc.stdlib from typing import Optional from flint.utils.flint_exceptions import IncompatibleContextError +from flint.types.fmpz cimport fmpz, any_as_fmpz + FLINT_BITS = _FLINT_BITS FLINT_VERSION = _FLINT_VERSION.decode("ascii") @@ -404,6 +406,48 @@ cdef class flint_mpoly(flint_elem): def _add_mpoly_(self, other): return NotImplemented + def _iadd_scalar_(self, other): + return NotImplemented + + def _iadd_mpoly_(self, other): + return NotImplemented + + def _sub_scalar_(self, other): + return NotImplemented + + def _sub_mpoly_(self, other): + return NotImplemented + + def _isub_scalar_(self, other): + return NotImplemented + + def _isub_mpoly_(self, other): + return NotImplemented + + def _mul_scalar_(self, other): + return NotImplemented + + def _imul_mpoly_(self, other): + return NotImplemented + + def _imul_scalar_(self, other): + return NotImplemented + + def _mul_mpoly_(self, other): + return NotImplemented + + def _pow_(self, other): + return NotImplemented + + def _divmod_mpoly_(self, other): + return NotImplemented + + def _floordiv_mpoly_(self, other): + return NotImplemented + + def _truediv_mpoly_(self, other): + return NotImplemented + def __add__(self, other): if typecheck(other, type(self)): self.context().compatible_context_check(other.context()) @@ -418,6 +462,123 @@ cdef class flint_mpoly(flint_elem): def __radd__(self, other): return self.__add__(other) + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering, fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + self._iadd_mpoly_(other) + return + + other_scalar = self.context().any_as_scalar(other) + if other_scalar is NotImplemented: + raise NotImplementedError(f"cannot add {type(self)} and {type(other)}") + + self._iadd_scalar_(other_scalar) + + def __sub__(self, other): + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + return self._sub_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + return self._sub_scalar_(other) + + def __rsub__(self, other): + return -self.__sub__(other) + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering, fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + self._isub_mpoly_(other) + return + + other_scalar = self.context().any_as_scalar(other) + if other_scalar is NotImplemented: + raise NotImplementedError(f"cannot subtract {type(self)} and {type(other)}") + + self._isub_scalar_(other_scalar) + + def __mul__(self, other): + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + return self._mul_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + return self._mul_scalar_(other) + + def __rmul__(self, other): + return self.__mul__(other) + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering, fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, type(self)): + self.context().compatible_context_check(other.context()) + self._imul_mpoly_(other) + return + + other_scalar = self.context().any_as_scalar(other) + if other_scalar is NotImplemented: + raise NotImplementedError(f"cannot multiply {type(self)} and {type(other)}") + + self._imul_scalar_(other_scalar) + + def __pow__(self, other, modulus): + if modulus is not None: + raise NotImplementedError("cannot specify modulus outside of the context") + elif typecheck(other, fmpz): + return self._pow_(other) + + other = any_as_fmpz(other) + if other is NotImplemented: + return NotImplemented + elif other < 0: + raise ValueError("cannot raise to a negative power") + + return self._pow_(other) + def __divmod__(self, other): if typecheck(other, type(self)): self._division_check(other) @@ -441,6 +602,75 @@ cdef class flint_mpoly(flint_elem): other = self.context().scalar_as_mpoly(other) return other._divmod_mpoly_(self) + def __truediv__(self, other): + if typecheck(other, type(self)): + self._division_check(other) + self.context().compatible_context_check(other.context()) + return self._truediv_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(other) + other = self.context().scalar_as_mpoly(other) + return self._truediv_mpoly_(other) + + def __rtruediv__(self, other): + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(self) + other = self.context().scalar_as_mpoly(other) + return other._truediv_mpoly_(self) + + def __floordiv__(self, other): + if typecheck(other, type(self)): + self._division_check(other) + self.context().compatible_context_check(other.context()) + return self._floordiv_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(other) + other = self.context().scalar_as_mpoly(other) + return self._floordiv_mpoly_(other) + + def __rfloordiv__(self, other): + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(self) + other = self.context().scalar_as_mpoly(other) + return other._floordiv_mpoly_(self) + + def __mod__(self, other): + if typecheck(other, type(self)): + self._division_check(other) + self.context().compatible_context_check(other.context()) + return self._mod_mpoly_(other) + + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(other) + other = self.context().scalar_as_mpoly(other) + return self._mod_mpoly_(other) + + def __rmod__(self, other): + other = self.context().any_as_scalar(other) + if other is NotImplemented: + return NotImplemented + + self._division_check(self) + other = self.context().scalar_as_mpoly(other) + return other._mod_mpoly_(self) + def __contains__(self, x): """ Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 7f0fa525..44f0c606 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -398,6 +398,7 @@ cdef class nmod_mpoly(flint_mpoly): """ cdef: slong nvars = self.ctx.nvars() + ulong coeff if not isinstance(x, tuple): raise TypeError("exponent vector index is not a tuple") @@ -405,26 +406,9 @@ cdef class nmod_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") exp_vec = fmpz_vec(x, double_indirect=True) - if isinstance(y, int): - nmod_mpoly_set_coeff_ui_fmpz(self.val, y, exp_vec.double_indirect, self.ctx.val) - elif typecheck(y, fmpz): - nmod_mpoly_set_coeff_ui_fmpz( - self.val, - fmpz_get_nmod((y).val, self.ctx.val.mod), - exp_vec.double_indirect, - self.ctx.val - ) - elif typecheck(y, nmod): - if y.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot add with different modulus {y.modulus()} vs {self.ctx.modulus()}") - nmod_mpoly_set_coeff_ui_fmpz( - self.val, - int(y), - exp_vec.double_indirect, - self.ctx.val - ) - else: - raise TypeError(f"cannot set coefficient to type {type(y)}") + + coeff = self.ctx.any_as_scalar(y) + nmod_mpoly_set_coeff_ui_fmpz(self.val, coeff, exp_vec.double_indirect, self.ctx.val) def __neg__(self): cdef nmod_mpoly res @@ -433,6 +417,7 @@ cdef class nmod_mpoly(flint_mpoly): return res def _add_scalar_(self, other: ulong): + cdef nmod_mpoly res res = create_nmod_mpoly(self.ctx) nmod_mpoly_add_ui(res.val, self.val, other, self.ctx.val) return res @@ -442,462 +427,64 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_add(res.val, self.val, other.val, res.ctx.val) return res - # def __add__(self, other): - # cdef nmod_mpoly res - # if typecheck(other, nmod_mpoly): - # if (self).ctx is not (other).ctx: - # raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - # return res - # elif typecheck(other, fmpz): - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui( - # res.val, - # (self).val, - # fmpz_get_nmod((other).val, self.ctx.val.mod), - # self.ctx.val - # ) - # return res - # elif typecheck(other, nmod): - # if other.modulus() != self.ctx.modulus(): - # raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) - # return res - # elif isinstance(other, int): - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - # return res - # else: - # return NotImplemented - - # def __radd__(self, other): - # cdef nmod_mpoly res - # if isinstance(other, int): - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui(res.val, (self).val, other, self.ctx.val) - # return res - # elif typecheck(other, fmpz): - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui( - # res.val, - # (self).val, - # fmpz_get_nmod((other).val, self.ctx.val.mod), - # self.ctx.val - # ) - # return res - # elif typecheck(other, nmod): - # if other.modulus() != self.ctx.modulus(): - # raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") - # res = create_nmod_mpoly(self.ctx) - # nmod_mpoly_add_ui(res.val, (self).val, int(other), self.ctx.val) - # return res - # else: - # return NotImplemented - - def __sub__(self, other): + def _sub_scalar_(self, other: ulong): cdef nmod_mpoly res - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui(res.val, (self).val, int(other), self.ctx.val) - return res - elif isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui(res.val, (self).val, other, self.ctx.val) - return res - else: - return NotImplemented + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub_ui(res.val, self.val, other, self.ctx.val) + return res - def __rsub__(self, other): + def _sub_mpoly_(self, other: nmod_mpoly): cdef nmod_mpoly res - if isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui(res.val, (self).val, other, res.ctx.val) - return -res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return -res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_sub_ui(res.val, (self).val, int(other), self.ctx.val) - return -res - else: - return NotImplemented + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_sub(res.val, self.val, other.val, res.ctx.val) + return res - def __mul__(self, other): + def _mul_scalar_(self, other: ulong): cdef nmod_mpoly res - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui(res.val, (self).val, int(other), self.ctx.val) - return res - elif isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) - return res - else: - return NotImplemented + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_scalar_mul_ui(res.val, self.val, other, self.ctx.val) + return res - def __rmul__(self, other): + def _mul_mpoly_(self, other: nmod_mpoly): cdef nmod_mpoly res - if isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui(res.val, (self).val, other, res.ctx.val) - return res - elif typecheck(other, fmpz): - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui( - res.val, - (self).val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_scalar_mul_ui(res.val, (self).val, int(other), self.ctx.val) - return res - else: - return NotImplemented + res = create_nmod_mpoly(self.ctx) + nmod_mpoly_mul(res.val, self.val, other.val, res.ctx.val) + return res - def __pow__(self, other, modulus): + def _pow_(self, other: fmpz): cdef nmod_mpoly res - if modulus is not None: - raise NotImplementedError("cannot specify modulus outside of the nmod_mpoly_ctx") - - other = any_as_fmpz(other) - if other is NotImplemented: - return NotImplemented - elif other < 0: - raise ValueError("cannot raise nmod_mpoly to a negative power") - res = create_nmod_mpoly(self.ctx) - if nmod_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + if nmod_mpoly_pow_fmpz(res.val, self.val, (other).val, res.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover return res def _divmod_mpoly_(self, other: nmod_mpoly): - res = create_nmod_mpoly(self.ctx) - res2 = create_nmod_mpoly(self.ctx) - nmod_mpoly_divrem(res.val, res2.val, self.val, other.val, res.ctx.val) - return (res, res2) - - # def __divmod__(self, other): - # cdef nmod_mpoly res, res2, o - - # if typecheck(other, nmod_mpoly): - # if not other: - # raise ZeroDivisionError("nmod_mpoly division by zero") - # elif not self.ctx.is_prime(): - # raise DomainError("division with non-prime modulus is not supported") - # elif (self).ctx is not (other).ctx: - # raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - # return (res, res2) - # elif isinstance(other, int): - # if not other: - # raise ZeroDivisionError("nmod_mpoly division by zero") - # elif not self.ctx.is_prime(): - # raise DomainError("division with non-prime modulus is not supported") - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - # return (res, res2) - # elif typecheck(other, fmpz): - # if not other: - # raise ZeroDivisionError("nmod_mpoly division by zero") - # elif not self.ctx.is_prime(): - # raise DomainError("division with non-prime modulus is not supported") - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - # return (res, res2) - # elif typecheck(other, nmod): - # if not other: - # raise ZeroDivisionError("nmod_mpoly division by zero") - # elif not self.ctx.is_prime(): - # raise DomainError("division with non-prime modulus is not supported") - # elif other.modulus() != self.ctx.modulus(): - # raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, (self).val, o.val, res.ctx.val) - # return (res, res2) - # else: - # return NotImplemented - - # def __rdivmod__(self, other): - # cdef nmod_mpoly res, res2, o - # if not self: - # raise ZeroDivisionError("nmod_mpoly division by zero") - # elif not self.ctx.is_prime(): - # raise DomainError("division with non-prime modulus is not supported") - # elif isinstance(other, int): - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - # return (res, res2) - # elif typecheck(other, fmpz): - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - # return (res, res2) - # elif typecheck(other, nmod): - # if other.modulus() != self.ctx.modulus(): - # raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - # o = create_nmod_mpoly(self.ctx) - # nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - # res = create_nmod_mpoly(self.ctx) - # res2 = create_nmod_mpoly(self.ctx) - # nmod_mpoly_divrem(res.val, res2.val, o.val, (self).val, res.ctx.val) - # return (res, res2) - # else: - # return NotImplemented - - def __floordiv__(self, other): - cdef nmod_mpoly res, o - if typecheck(other, nmod_mpoly): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res - elif isinstance(other, int): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) - return res - elif typecheck(other, fmpz): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) - return res - elif typecheck(other, nmod): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, (self).val, o.val, res.ctx.val) - return res + cdef nmod_mpoly quotient, remainder + quotient = create_nmod_mpoly(self.ctx) + remainder = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) + return (quotient, remainder) + + def _floordiv_mpoly_(self, other: nmod_mpoly): + cdef nmod_mpoly quotient + quotient = create_nmod_mpoly(self.ctx) + nmod_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) + return quotient + + def _truediv_mpoly_(self, other: nmod_mpoly): + cdef nmod_mpoly quotient + quotient = create_nmod_mpoly(self.ctx) + if nmod_mpoly_divides(quotient.val, self.val, other.val, self.ctx.val): + return quotient else: - return NotImplemented + raise DomainError("nmod_mpoly division is not exact") - def __rfloordiv__(self, other): - cdef nmod_mpoly res, o - if not self: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif isinstance(other, int): - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, other, o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) - return res - elif typecheck(other, fmpz): - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, fmpz_get_nmod((other).val, self.ctx.val.mod), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) - return res - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - o = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(o.val, int(other), o.ctx.val) - - res = create_nmod_mpoly(self.ctx) - nmod_mpoly_div(res.val, o.val, self.val, res.ctx.val) - return res - else: - return NotImplemented - - def __truediv__(self, other): - cdef: - nmod_mpoly res, div - - if typecheck(other, nmod_mpoly): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif self.ctx is not (other).ctx: - raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") - - res = create_nmod_mpoly(self.ctx) - if nmod_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - elif isinstance(other, int): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - res = create_nmod_mpoly(self.ctx) - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, other, self.ctx.val) - if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - elif typecheck(other, fmpz): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, fmpz_get_nmod((other).val, self.ctx.val.mod), div.ctx.val) - - res = create_nmod_mpoly(self.ctx) - if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - elif typecheck(other, nmod): - if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, int(other), self.ctx.val) - if nmod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - else: - return NotImplemented - - def __rtruediv__(self, other): - cdef nmod_mpoly res - if not self: - raise ZeroDivisionError("nmod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif isinstance(other, int): - res = create_nmod_mpoly(self.ctx) - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, other, self.ctx.val) - if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - elif typecheck(other, fmpz): - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, fmpz_get_nmod((other).val, self.ctx.val.mod), div.ctx.val) - - res = create_nmod_mpoly(self.ctx) - if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot divide with different modulus {other.modulus()} vs {self.ctx.modulus()}") - res = create_nmod_mpoly(self.ctx) - div = create_nmod_mpoly(self.ctx) - nmod_mpoly_set_ui(div.val, int(other), self.ctx.val) - if nmod_mpoly_divides(res.val, div.val, self.val, self.ctx.val): - return res - else: - raise DomainError("nmod_mpoly division is not exact") - else: - return NotImplemented - - def __mod__(self, other): - return divmod(self, other)[1] - - def __rmod__(self, other): - return divmod(other, self)[1] + def _mod_mpoly_(self, other: nmod_mpoly): + cdef nmod_mpoly quotient, remainder + quotient = create_nmod_mpoly(self.ctx) + remainder = create_nmod_mpoly(self.ctx) + nmod_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) + return remainder def __call__(self, *args) -> ulong: cdef: @@ -906,7 +493,7 @@ cdef class nmod_mpoly(flint_mpoly): if nargs != nvars: raise ValueError("number of generators does not match number of arguments") - args = [int(x) for x in args] + args = [self.ctx.any_as_scalar(x) for x in args] cdef: # Using sizeof(ulong) here breaks on 64 windows machines because of the `ctypedef unsigned long ulong` in # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. @@ -924,97 +511,23 @@ cdef class nmod_mpoly(flint_mpoly): return res - def iadd(self, other): - """ - In-place addition, mutates self. + def _iadd_scalar_(self, other: ulong): + nmod_mpoly_add_ui(self.val, self.val, other, self.ctx.val) - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.iadd(5) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 + 5 + def _iadd_mpoly_(self, other: nmod_mpoly): + nmod_mpoly_add(self.val, self.val, other.val, self.ctx.val) - """ - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - nmod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - elif isinstance(other, int): - nmod_mpoly_add_ui((self).val, (self).val, other, self.ctx.val) - elif typecheck(other, fmpz): - nmod_mpoly_add_ui(self.val, self.val, fmpz_get_nmod((other).val, self.ctx.val.mod), self.ctx.val) - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot add with different modulus {other.modulus()} vs {self.ctx.modulus()}") - nmod_mpoly_add_ui(self.val, (self).val, int(other), self.ctx.val) - else: - raise NotImplementedError(f"cannot add {type(other)} to nmod_mpoly") - - def isub(self, other): - """ - In-place subtraction, mutates self. + def _isub_scalar_(self, other: ulong): + nmod_mpoly_sub_ui(self.val, self.val, other, self.ctx.val) - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.isub(5) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - 5 + def _isub_mpoly_(self, other: nmod_mpoly): + nmod_mpoly_sub(self.val, self.val, other.val, self.ctx.val) - """ - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - nmod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - elif isinstance(other, int): - nmod_mpoly_sub_ui((self).val, (self).val, other, self.ctx.val) - elif typecheck(other, fmpz): - nmod_mpoly_sub_ui(self.val, self.val, fmpz_get_nmod((other).val, self.ctx.val.mod), self.ctx.val) - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot subtract with different modulus {other.modulus()} vs {self.ctx.modulus()}") - nmod_mpoly_sub_ui(self.val, (self).val, int(other), self.ctx.val) - else: - raise NotImplementedError(f"cannot subtract {type(other)} from nmod_mpoly") + def _imul_scalar_(self, other: ulong): + nmod_mpoly_scalar_mul_ui(self.val, self.val, other, self.ctx.val) - def imul(self, other): - """ - In-place multiplication, mutates self. - - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.imul(2) - >>> f - 8*x0*x1 + 4*x0 + 6*x1 - - """ - if typecheck(other, nmod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - nmod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - elif isinstance(other, int): - nmod_mpoly_scalar_mul_ui(self.val, (self).val, other, self.ctx.val) - elif typecheck(other, fmpz): - nmod_mpoly_scalar_mul_ui( - self.val, - self.val, - fmpz_get_nmod((other).val, self.ctx.val.mod), - self.ctx.val - ) - elif typecheck(other, nmod): - if other.modulus() != self.ctx.modulus(): - raise ValueError(f"cannot multiply with different modulus {other.modulus()} vs {self.ctx.modulus()}") - nmod_mpoly_scalar_mul_ui(self.val, (self).val, int(other), self.ctx.val) - else: - raise NotImplementedError(f"cannot multiple nmod_mpoly by {type(other)}") + def _imul_mpoly_(self, other: nmod_mpoly): + nmod_mpoly_mul(self.val, self.val, other.val, self.ctx.val) def monoms(self): """ @@ -1127,10 +640,9 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_vec C slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") + + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") elif self.ctx.nvars() == 0 and ctx is None: raise ValueError("a context must be provided when composing a polynomial with no generators") elif not all(typecheck(arg, nmod_mpoly) for arg in args): From c08fdb07eee04849675363e24b78fb74999c036a Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 17:06:06 +1000 Subject: [PATCH 26/29] Fix nmod type errors, no clue how this worked at all --- src/flint/types/nmod_mpoly.pyx | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 44f0c606..a943ed56 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -126,22 +126,21 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): try: return other except OverflowError: - return (other % self.val.mod) + return (other % self.modulus()) elif typecheck(other, nmod): - val = other - if val.modulus() != self.modulus(): + if (other).modulus() != self.modulus(): raise DomainError( - f"modulus does not match, got {val.modulus()}, expected {self.modulus()}" + f"modulus does not match, got {(other).modulus()}, expected {self.modulus()}" ) - return val.val + return (other).val elif typecheck(other, fmpz): return fmpz_get_nmod((other).val, self.val.mod) elif typecheck(other, fmpz_mod): - if other.ctx.modulus() != self.modulus(): + if (other).ctx.modulus() != self.modulus(): raise DomainError( - f"modulus does not match, got {other.ctx.modulus()}, expected {self.modulus()}" + f"modulus does not match, got {(other).ctx.modulus()}, expected {self.modulus()}" ) - return fmpz_get_nmod((other).val, self.val.mod) + return fmpz_get_nmod((other).val, self.val.mod) else: return NotImplemented @@ -253,23 +252,12 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): continue exp_vec = fmpz_vec(exps) - - if isinstance(coeff, int) or typecheck(coeff, fmpz): - coeff_fmpz = any_as_fmpz(coeff) - if coeff_fmpz is NotImplemented: - raise TypeError(f"cannot coerce '{repr(coeff)}' to fmpz") - - nmod_mpoly_push_term_ui_ffmpz( - res.val, - fmpz_get_nmod((coeff_fmpz).val, self.val.mod), - exp_vec.val, - self.val - ) - elif typecheck(coeff, nmod): - nmod_mpoly_push_term_ui_ffmpz(res.val, int(coeff), exp_vec.val, self.val) - else: + coeff_scalar = self.any_as_scalar(coeff) + if coeff_scalar is NotImplemented: raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") + nmod_mpoly_push_term_ui_ffmpz(res.val, coeff_scalar, exp_vec.val, self.val) + nmod_mpoly_sort_terms(res.val, self.val) return res From 789ad002eda44468e751d442bbe92dcad796a045 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 17:28:36 +1000 Subject: [PATCH 27/29] Reverse changes to fmpz_vec --- src/flint/types/fmpz_vec.pyx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index 0a1c31b5..365a1d38 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -55,14 +55,11 @@ cdef class fmpz_vec: elif not 0 <= x < self.length: raise IndexError("index out of range") - if typecheck(y, fmpz_mod): - fmpz_set(&self.val[x], (y).val) - else: - y = any_as_fmpz(y) - if y is NotImplemented: - raise TypeError("argument is not coercible to fmpz") + y = any_as_fmpz(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpz") - fmpz_set(&self.val[x], (y).val) + fmpz_set(&self.val[x], (y).val) def __len__(self): return self.length From a2573c1fcf28326eabea865cd43bcb39e2cd6529 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sat, 17 Aug 2024 17:28:56 +1000 Subject: [PATCH 28/29] Refactor fmpz_mod_mpoly --- src/flint/types/fmpz_mod_mpoly.pyx | 534 +++++++---------------------- src/flint/types/nmod_mpoly.pyx | 12 +- 2 files changed, 125 insertions(+), 421 deletions(-) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index d5ee4303..d8112255 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -12,6 +12,7 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec from flint.types.fmpz_mod cimport fmpz_mod +from flint.types.nmod cimport nmod from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.fmpz_mod_mpoly cimport ( @@ -126,6 +127,32 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): raise ValueError("must provide either `names` or `nametup`") return key + def any_as_scalar(self, other): + if isinstance(other, int): + return any_as_fmpz(other) + elif typecheck(other, nmod): + if (other).modulus() != self.modulus(): + raise DomainError( + f"modulus does not match, got {(other).modulus()}, expected {self.modulus()}" + ) + return any_as_fmpz((other).val) + elif typecheck(other, fmpz): + return fmpz(other) + elif typecheck(other, fmpz_mod): + if (other).ctx.modulus() != self.modulus(): + raise DomainError( + f"modulus does not match, got {(other).ctx.modulus()}, expected {self.modulus()}" + ) + res = fmpz.__new__(fmpz) + fmpz_set((res).val, (other).val) + return res + else: + return NotImplemented + + def scalar_as_mpoly(self, other: fmpz): + # non-fmpz scalars should first be converted via cls.any_as_scalar + return self.constant(other) + def nvars(self): """ Return the number of variables in the context @@ -235,28 +262,17 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): continue exp_vec = fmpz_vec(exps) - - if isinstance(coeff, int) or typecheck(coeff, fmpz): - coeff_fmpz = any_as_fmpz(coeff) - if coeff_fmpz is NotImplemented: - raise TypeError(f"cannot coerce '{repr(coeff)}' to fmpz") - - fmpz_mod_mpoly_push_term_fmpz_ffmpz( - res.val, - (coeff_fmpz).val, - exp_vec.val, - self.val - ) - elif typecheck(coeff, fmpz_mod): - fmpz_mod_mpoly_push_term_fmpz_ffmpz( - res.val, - (coeff).val, - exp_vec.val, - self.val - ) - else: + coeff_scalar = self.any_as_scalar(coeff) + if coeff_scalar is NotImplemented: raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") + fmpz_mod_mpoly_push_term_fmpz_ffmpz( + res.val, + (coeff_scalar).val, + exp_vec.val, + self.val + ) + fmpz_mod_mpoly_sort_terms(res.val, self.val) return res @@ -311,6 +327,11 @@ cdef class fmpz_mod_mpoly(flint_mpoly): init_fmpz_mod_mpoly(self, ctx) fmpz_mod_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) + def _division_check(self, other): + super()._division_check(other) + if not self.ctx.is_prime(): + raise DomainError("division with non-prime modulus is not supported") + def __bool__(self): return not fmpz_mod_mpoly_is_zero(self.val, self.ctx.val) @@ -396,17 +417,10 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") exp_vec = fmpz_vec(x, double_indirect=True) - if typecheck(y, fmpz_mod): - if self.ctx.modulus() != (y).ctx.modulus(): - raise ValueError( - f"cannot set a coefficient with different modulus {(y).ctx.modulus()} vs {self.ctx.modulus()}" - ) - fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (y).val, exp_vec.double_indirect, self.ctx.val) - else: - coeff = any_as_fmpz(y) - if coeff is NotImplemented: - raise TypeError("provided coefficient not coercible to fmpz") - fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) + coeff = self.ctx.any_as_scalar(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpz") + fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) def __neg__(self): cdef fmpz_mod_mpoly res @@ -414,301 +428,76 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_neg(res.val, (self).val, res.ctx.val) return res - def __add__(self, other): + def _add_scalar_(self, other: fmpz): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add_fmpz(res.val, self.val, other.val, self.ctx.val) + return res - def __radd__(self, other): + def _add_mpoly_(self, other: fmpz_mod_mpoly): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_add(res.val, self.val, other.val, res.ctx.val) + return res - def __sub__(self, other): + def _sub_scalar_(self, other: fmpz): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub_fmpz(res.val, self.val, other.val, self.ctx.val) + return res - def __rsub__(self, other): + def _sub_mpoly_(self, other: fmpz_mod_mpoly): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return -res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return -res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_sub(res.val, self.val, other.val, res.ctx.val) + return res - def __mul__(self, other): + def _mul_scalar_(self, other: fmpz): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_scalar_mul_fmpz(res.val, self.val, other.val, self.ctx.val) + return res - def __rmul__(self, other): + def _mul_mpoly_(self, other: fmpz_mod_mpoly): cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, self.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented + res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_mul(res.val, self.val, other.val, res.ctx.val) + return res - def __pow__(self, other, modulus): + def _pow_(self, other: fmpz): cdef fmpz_mod_mpoly res - if modulus is not None: - raise NotImplementedError - other = any_as_fmpz(other) - if other is NotImplemented: - return other - if other < 0: - raise ValueError("cannot raise fmpz_mod_mpoly to negative power") res = create_fmpz_mod_mpoly(self.ctx) - if fmpz_mod_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + if fmpz_mod_mpoly_pow_fmpz(res.val, self.val, other.val, res.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover return res - def __divmod__(self, other): - cdef fmpz_mod_mpoly res, res2 - - if typecheck(other, fmpz_mod_mpoly): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mod_mpoly(self.ctx) - res2 = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - res2 = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - return NotImplemented - - def __rdivmod__(self, other): - cdef fmpz_mod_mpoly res, res2 - if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - res2 = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) - return (res, res2) - return NotImplemented - - def __floordiv__(self, other): - cdef fmpz_mod_mpoly res - if typecheck(other, fmpz_mod_mpoly): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res - elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __rfloordiv__(self, other): - cdef fmpz_mod_mpoly res - if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_div(res.val, (other).val, self.val, res.ctx.val) - return res - return NotImplemented - - def __truediv__(self, other): - cdef: - fmpz_mod_mpoly res, div - - if typecheck(other, fmpz_mod_mpoly): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif self.ctx is not (other).ctx: - raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") - - res = create_fmpz_mod_mpoly(self.ctx) - if fmpz_mod_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): - return res - else: - raise DomainError("fmpz_mod_mpoly division is not exact") - elif typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not other: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - - div = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - if fmpz_mod_mpoly_divides(res.val, self.val, div.val, self.ctx.val): - return res - else: - raise DomainError("fmpz_mod_mpoly division is not exact") - return NotImplemented - - def __rtruediv__(self, other): - cdef fmpz_mod_mpoly res - if typecheck(other, fmpz) or typecheck(other, fmpz_mod) or isinstance(other, int): - if not self: - raise ZeroDivisionError("fmpz_mod_mpoly division by zero") - elif not self.ctx.is_prime(): - raise DomainError("division with non-prime modulus is not supported") - elif typecheck(other, fmpz_mod) and self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot divide with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - other = fmpz_mod_mpoly(other, self.ctx) - res = create_fmpz_mod_mpoly(self.ctx) - if fmpz_mod_mpoly_divides(res.val, (other).val, self.val, self.ctx.val): - return res - else: - raise DomainError("fmpz_mod_mpoly division is not exact") - return NotImplemented - - def __mod__(self, other): - return divmod(self, other)[1] + def _divmod_mpoly_(self, other: fmpz_mod_mpoly): + cdef fmpz_mod_mpoly quotient, remainder + quotient = create_fmpz_mod_mpoly(self.ctx) + remainder = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) + return (quotient, remainder) + + def _floordiv_mpoly_(self, other: fmpz_mod_mpoly): + cdef fmpz_mod_mpoly quotient + quotient = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) + return quotient + + def _truediv_mpoly_(self, other: fmpz_mod_mpoly): + cdef fmpz_mod_mpoly quotient + quotient = create_fmpz_mod_mpoly(self.ctx) + if fmpz_mod_mpoly_divides(quotient.val, self.val, other.val, self.ctx.val): + return quotient + else: + raise DomainError("fmpz_mod_mpoly division is not exact") - def __rmod__(self, other): - return divmod(other, self)[1] + def _mod_mpoly_(self, other: fmpz_mod_mpoly): + cdef fmpz_mod_mpoly quotient, remainder + quotient = create_fmpz_mod_mpoly(self.ctx) + remainder = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) + return remainder def __call__(self, *args) -> fmpz: cdef: @@ -717,111 +506,30 @@ cdef class fmpz_mod_mpoly(flint_mpoly): slong nvars = self.ctx.nvars(), nargs = len(args) if nargs != nvars: - raise ValueError("number of generators does not match number of arguments") # TODO: fix for all of them - + raise ValueError("number of generators does not match number of arguments") + args = [self.ctx.any_as_scalar(x) for x in args] V = fmpz_vec(args, double_indirect=True) vres = fmpz.__new__(fmpz) fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) return vres - def iadd(self, other): - """ - In-place addition, mutates self. - - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.iadd(5) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 + 5 - - """ - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mod_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot add with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - fmpz_mod_mpoly_add_fmpz(self.val, self.val, (other).val, self.ctx.val) - return - else: - zval = any_as_fmpz(other) - if zval is not NotImplemented: - fmpz_mod_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) - return - raise NotImplementedError() + def _iadd_scalar_(self, other: fmpz): + fmpz_mod_mpoly_add_fmpz(self.val, self.val, other.val, self.ctx.val) - def isub(self, other): - """ - In-place subtraction, mutates self. + def _iadd_mpoly_(self, other: fmpz_mod_mpoly): + fmpz_mod_mpoly_add(self.val, self.val, other.val, self.ctx.val) - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.isub(5) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - 5 + def _isub_scalar_(self, other: fmpz): + fmpz_mod_mpoly_sub_fmpz(self.val, self.val, other.val, self.ctx.val) - """ - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mod_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot subtract with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - fmpz_mod_mpoly_sub_fmpz(self.val, self.val, (other).val, self.ctx.val) - return - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mod_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) - return - raise NotImplementedError() + def _isub_mpoly_(self, other: fmpz_mod_mpoly): + fmpz_mod_mpoly_sub(self.val, self.val, other.val, self.ctx.val) - def imul(self, other): - """ - In-place multiplication, mutates self. + def _imul_scalar_(self, other: fmpz): + fmpz_mod_mpoly_scalar_mul_fmpz(self.val, self.val, other.val, self.ctx.val) - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') - >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f - 4*x0*x1 + 2*x0 + 3*x1 - >>> f.imul(2) - >>> f - 8*x0*x1 + 4*x0 + 6*x1 - - """ - if typecheck(other, fmpz_mod_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mod_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return - elif typecheck(other, fmpz_mod): - if self.ctx.modulus() != (other).ctx.modulus(): - raise ValueError( - f"cannot multiply with different modulus {(other).ctx.modulus()} vs {self.ctx.modulus()}" - ) - fmpz_mod_mpoly_scalar_mul_fmpz(self.val, self.val, (other).val, self.ctx.val) - return - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mod_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) - return - raise NotImplementedError() + def _imul_mpoly_(self, other: fmpz_mod_mpoly): + fmpz_mod_mpoly_mul(self.val, self.val, other.val, self.ctx.val) def monoms(self): """ @@ -907,21 +615,17 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), v) for k, v in dict_args.items()) + args = tuple((self.ctx.variable_to_index(k), self.ctx.any_as_scalar(v)) for k, v in dict_args.items()) + for (_, v), old in zip(args, dict_args.values()): + if v is NotImplemented: + raise TypeError(f"cannot coerce {type(old)} to fmpz") # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_set(res.val, self.val, self.ctx.val) for i, arg in args: - if typecheck(arg, fmpz_mod): - fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) - else: - arg_fmpz = any_as_fmpz(arg) - if arg_fmpz is NotImplemented: - raise TypeError(f"cannot coerce {type(arg)} to fmpz") - fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg_fmpz).val, self.ctx.val) - + fmpz_mod_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) return res def compose(self, *args, ctx=None) -> fmpz_mod_mpoly: @@ -948,10 +652,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_vec C slong i, nvars = self.ctx.nvars(), nargs = len(args) - if nargs < nvars: - raise ValueError("not enough arguments provided") - elif nargs > nvars: - raise ValueError("more arguments provided than variables") + if nargs != nvars: + raise ValueError("number of generators does not match number of arguments") elif self.ctx.nvars() == 0 and ctx is None: raise ValueError("a context must be provided when composing a polynomial with no generators") elif not all(typecheck(arg, fmpz_mod_mpoly) for arg in args): diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index a943ed56..22fcacc4 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -396,6 +396,8 @@ cdef class nmod_mpoly(flint_mpoly): exp_vec = fmpz_vec(x, double_indirect=True) coeff = self.ctx.any_as_scalar(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to ulong") nmod_mpoly_set_coeff_ui_fmpz(self.val, coeff, exp_vec.double_indirect, self.ctx.val) def __neg__(self): @@ -411,6 +413,7 @@ cdef class nmod_mpoly(flint_mpoly): return res def _add_mpoly_(self, other: nmod_mpoly): + cdef nmod_mpoly res res = create_nmod_mpoly(self.ctx) nmod_mpoly_add(res.val, self.val, other.val, res.ctx.val) return res @@ -591,10 +594,10 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), int(v)) for k, v in dict_args.items()) - for _, v in args: - if not v >= 0: - raise TypeError("constants must be non-negative integers") + args = tuple((self.ctx.variable_to_index(k), self.ctx.any_as_scalar(v)) for k, v in dict_args.items()) + for (_, v), old in zip(args, dict_args.values()): + if v is NotImplemented: + raise TypeError(f"cannot coerce {type(old)} to ulong") # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_nmod_mpoly(self.ctx) @@ -628,7 +631,6 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_vec C slong nvars = self.ctx.nvars(), nargs = len(args) - if nargs != nvars: raise ValueError("number of generators does not match number of arguments") elif self.ctx.nvars() == 0 and ctx is None: From 1358a6738d6cd61735d42de003ba2e94272ac164 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Sun, 18 Aug 2024 11:38:16 +1000 Subject: [PATCH 29/29] Update tests, make "division not support" exception lowest priority --- src/flint/flint_base/flint_base.pyx | 24 +++---- src/flint/test/test_all.py | 103 +++++++++++++++------------- src/flint/types/fmpz_mod_mpoly.pyx | 6 ++ 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 5269baba..e0b02fcf 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -581,16 +581,16 @@ cdef class flint_mpoly(flint_elem): def __divmod__(self, other): if typecheck(other, type(self)): - self._division_check(other) self.context().compatible_context_check(other.context()) + self._division_check(other) return self._divmod_mpoly_(other) other = self.context().any_as_scalar(other) if other is NotImplemented: return NotImplemented - self._division_check(other) other = self.context().scalar_as_mpoly(other) + self._division_check(other) return self._divmod_mpoly_(other) def __rdivmod__(self, other): @@ -598,22 +598,22 @@ cdef class flint_mpoly(flint_elem): if other is NotImplemented: return NotImplemented - self._division_check(self) other = self.context().scalar_as_mpoly(other) + other._division_check(self) return other._divmod_mpoly_(self) def __truediv__(self, other): if typecheck(other, type(self)): - self._division_check(other) self.context().compatible_context_check(other.context()) + self._division_check(other) return self._truediv_mpoly_(other) other = self.context().any_as_scalar(other) if other is NotImplemented: return NotImplemented - self._division_check(other) other = self.context().scalar_as_mpoly(other) + self._division_check(other) return self._truediv_mpoly_(other) def __rtruediv__(self, other): @@ -621,22 +621,22 @@ cdef class flint_mpoly(flint_elem): if other is NotImplemented: return NotImplemented - self._division_check(self) other = self.context().scalar_as_mpoly(other) + other._division_check(self) return other._truediv_mpoly_(self) def __floordiv__(self, other): if typecheck(other, type(self)): - self._division_check(other) self.context().compatible_context_check(other.context()) + self._division_check(other) return self._floordiv_mpoly_(other) other = self.context().any_as_scalar(other) if other is NotImplemented: return NotImplemented - self._division_check(other) other = self.context().scalar_as_mpoly(other) + self._division_check(other) return self._floordiv_mpoly_(other) def __rfloordiv__(self, other): @@ -644,22 +644,22 @@ cdef class flint_mpoly(flint_elem): if other is NotImplemented: return NotImplemented - self._division_check(self) other = self.context().scalar_as_mpoly(other) + other._division_check(self) return other._floordiv_mpoly_(self) def __mod__(self, other): if typecheck(other, type(self)): - self._division_check(other) self.context().compatible_context_check(other.context()) + self._division_check(other) return self._mod_mpoly_(other) other = self.context().any_as_scalar(other) if other is NotImplemented: return NotImplemented - self._division_check(other) other = self.context().scalar_as_mpoly(other) + self._division_check(other) return self._mod_mpoly_(other) def __rmod__(self, other): @@ -667,8 +667,8 @@ cdef class flint_mpoly(flint_elem): if other is NotImplemented: return NotImplemented - self._division_check(self) other = self.context().scalar_as_mpoly(other) + other._division_check(self) return other._mod_mpoly_(self) def __contains__(self, x): diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 7e88a891..783a4084 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2782,6 +2782,12 @@ def _all_mpolys(): def test_mpolys(): for P, get_context, S, is_field in _all_mpolys(): + # Division under modulo will raise a flint exception if something is not invertible, crashing the program. We + # can't tell before what is invertible and what is not before hand so we always raise an exception, except for + # fmpz_mpoly, that returns an bool noting if the division is exact or not. + division_not_supported = P is not flint.fmpz_mpoly and not is_field + characteristic_zero = not (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) + ctx = get_context(nvars=2) assert raises(lambda: get_context(nvars=2, ordering="bad"), TypeError) @@ -3045,7 +3051,7 @@ def quick_poly(): assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) assert raises(lambda: quick_poly().imul(None), NotImplementedError) - if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): + if division_not_supported: assert raises(lambda: quick_poly() // mpoly({(1, 1): 1}), DomainError) assert raises(lambda: quick_poly() % mpoly({(1, 1): 1}), DomainError) assert raises(lambda: divmod(quick_poly(), mpoly({(1, 1): 1})), DomainError) @@ -3056,9 +3062,6 @@ def quick_poly(): assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) - if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): - pass - else: assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) assert quick_poly() / 1 == quick_poly() assert quick_poly() // 1 == quick_poly() @@ -3078,9 +3081,7 @@ def quick_poly(): f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) - if (P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly) and not ctx.is_prime(): - pass - else: + if not division_not_supported: assert 1 // quick_poly() == P(ctx=ctx) assert 1 % quick_poly() == P(1, ctx=ctx) assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) @@ -3089,36 +3090,6 @@ def quick_poly(): assert S(1) % quick_poly() == P(1, ctx=ctx) assert divmod(S(1), quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) - assert raises(lambda: quick_poly() / None, TypeError) - assert raises(lambda: quick_poly() // None, TypeError) - assert raises(lambda: quick_poly() % None, TypeError) - assert raises(lambda: divmod(quick_poly(), None), TypeError) - - assert raises(lambda: None / quick_poly(), TypeError) - assert raises(lambda: None // quick_poly(), TypeError) - assert raises(lambda: None % quick_poly(), TypeError) - assert raises(lambda: divmod(None, quick_poly()), TypeError) - - assert raises(lambda: quick_poly() / 0, ZeroDivisionError) - assert raises(lambda: quick_poly() // 0, ZeroDivisionError) - assert raises(lambda: quick_poly() % 0, ZeroDivisionError) - assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) - - assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) - - assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) - assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) - - assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) - assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - assert f * g / mpoly({(0, 1): 1, (1, 0): 1}) \ == mpoly({(1, 1): 8, (0, 0): 2}) @@ -3126,6 +3097,37 @@ def quick_poly(): assert raises(lambda: 1 / quick_poly(), DomainError) assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) + # We prefer various other errors to the "division not supported" domain error so these are safe. + assert raises(lambda: quick_poly() / None, TypeError) + assert raises(lambda: quick_poly() // None, TypeError) + assert raises(lambda: quick_poly() % None, TypeError) + assert raises(lambda: divmod(quick_poly(), None), TypeError) + + assert raises(lambda: None / quick_poly(), TypeError) + assert raises(lambda: None // quick_poly(), TypeError) + assert raises(lambda: None % quick_poly(), TypeError) + assert raises(lambda: divmod(None, quick_poly()), TypeError) + + assert raises(lambda: quick_poly() / 0, ZeroDivisionError) + assert raises(lambda: quick_poly() // 0, ZeroDivisionError) + assert raises(lambda: quick_poly() % 0, ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) + + assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) + assert quick_poly() ** 0 == P(1, ctx=ctx) assert quick_poly() ** 1 == quick_poly() assert quick_poly() ** 2 == mpoly({ @@ -3146,29 +3148,34 @@ def quick_poly(): # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly) or f.context().is_prime(): + if division_not_supported: + assert raises(lambda: (f * g).gcd(f), DomainError) + else: if is_field: assert (f * g).gcd(f) == f / 4 else: assert (f * g).gcd(f) == f assert raises(lambda: quick_poly().gcd(None), TypeError) assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) - else: - assert raises(lambda: (f * g).gcd(f), DomainError) - if P is flint.fmpz_mod_mpoly or P is flint.nmod_mpoly: - if is_field: - assert (f * g).factor() == (S(8), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f / 4, 1)]) + if division_not_supported: + # Factorisation not allowed over Z/nZ for n not prime. + # Flint would abort so we raise an exception instead: + assert raises(lambda: (f * g).factor(), DomainError) + elif characteristic_zero: + # Primitive factors over Z for fmpz_mpoly and fmpq_mpoly + assert (f * g).factor() == (S(2), [(g / 2, 1), (f, 1)]) + elif is_field: + # Monic polynomials over Z/pZ for nmod_mpoly and fmpz_mod_mpoly + assert (f * g).factor() == (S(8), [(g / 2, 1), (f / 4, 1)]) + + if division_not_supported: + assert raises(lambda: (f * g).sqrt(), DomainError) else: - assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) - - if (P is not flint.fmpz_mod_mpoly and P is not flint.nmod_mpoly) or f.context().is_prime(): assert (f * f).sqrt() == f if P is flint.fmpz_mpoly: assert (f * f).sqrt(assume_perfect_square=True) == f assert raises(lambda: quick_poly().sqrt(), ValueError) - else: - assert raises(lambda: (f * g).sqrt(), DomainError) p = quick_poly() assert p.derivative(0) == p.derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index d8112255..43f5b328 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -831,6 +831,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz c fmpz_mod_mpoly u + if not self.ctx.is_prime(): + raise DomainError("factorisation with non-prime modulus is not supported") + fmpz_mod_mpoly_factor_init(fac, self.ctx.val) if not fmpz_mod_mpoly_factor(fac, self.val, self.ctx.val): raise RuntimeError("factorisation failed") @@ -871,6 +874,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz c fmpz_mod_mpoly u + if not self.ctx.is_prime(): + raise DomainError("factorisation with non-prime modulus is not supported") + fmpz_mod_mpoly_factor_init(fac, self.ctx.val) if not fmpz_mod_mpoly_factor_squarefree(fac, self.val, self.ctx.val): raise RuntimeError("factorisation failed")