From fea7defe8a2d12070a3a51ba8eae0da7fe18fd82 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 20 Sep 2023 16:03:19 +0100 Subject: [PATCH 01/35] Begin adding fmpz_mod_poly --- setup.py | 1 + src/flint/__init__.py | 1 + src/flint/flintlib/fmpz_mod_poly.pxd | 301 ++++++++++++++++++++ src/flint/flintlib/fmpz_mod_poly_factor.pxd | 46 +++ src/flint/test/test.py | 13 +- src/flint/types/fmpz_mod_poly.pxd | 14 + src/flint/types/fmpz_mod_poly.pyx | 91 ++++++ 7 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 src/flint/flintlib/fmpz_mod_poly.pxd create mode 100644 src/flint/flintlib/fmpz_mod_poly_factor.pxd create mode 100644 src/flint/types/fmpz_mod_poly.pxd create mode 100644 src/flint/types/fmpz_mod_poly.pyx diff --git a/setup.py b/setup.py index 1972be10..13ce2c28 100644 --- a/setup.py +++ b/setup.py @@ -95,6 +95,7 @@ ("flint.types.acb_series", ["src/flint/types/acb_series.pyx"]), ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_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.dirichlet", ["src/flint/types/dirichlet.pyx"]), ("flint.flint_base.flint_base", ["src/flint/flint_base/flint_base.pyx"]), ("flint.flint_base.flint_context", ["src/flint/flint_base/flint_context.pyx"]), diff --git a/src/flint/__init__.py b/src/flint/__init__.py index ef3f78e9..e545556c 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -22,6 +22,7 @@ from .types.acb_series import * from .types.fmpz_mpoly import * from .types.fmpz_mod import * +from .types.fmpz_mod_poly import * from .types.dirichlet import * from .functions.showgood import showgood diff --git a/src/flint/flintlib/fmpz_mod_poly.pxd b/src/flint/flintlib/fmpz_mod_poly.pxd new file mode 100644 index 00000000..2df3f851 --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_poly.pxd @@ -0,0 +1,301 @@ +from flint.flintlib.flint cimport ulong, slong, flint_rand_t +from flint.flintlib.fmpz cimport fmpz_t, fmpz_struct +from flint.flintlib.fmpz_mat cimport fmpz_mat_t, fmpz_mat_struct +from flint.flintlib.fmpz_poly cimport fmpz_poly_t, fmpz_poly_struct +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t, fmpz_mod_ctx_struct +from flint.flintlib.nmod_poly cimport nmod_poly_t + +cdef extern from "flint/fmpz_mod_poly.h": + # Type definitions *********************************************************/ + ctypedef struct fmpz_mod_poly_struct: + fmpz_struct * coeffs + slong alloc + slong length + ctypedef fmpz_mod_poly_struct fmpz_mod_poly_t[1] + + ctypedef struct fmpz_mod_poly_res_struct: + fmpz_t res + fmpz_t lc + slong len0 + slong len1 + slong off + ctypedef fmpz_mod_poly_res_struct fmpz_mod_poly_res_t[1] + + ctypedef struct fmpz_mod_poly_frobenius_powers_2exp_struct: + fmpz_mod_poly_struct * pow + slong len + ctypedef fmpz_mod_poly_frobenius_powers_2exp_struct fmpz_mod_poly_frobenius_powers_2exp_t[1] + + ctypedef struct fmpz_mod_poly_frobenius_powers_struct: + fmpz_mod_poly_struct * pow + slong len + ctypedef fmpz_mod_poly_frobenius_powers_struct fmpz_mod_poly_frobenius_powers_t[1] + + ctypedef struct fmpz_mod_poly_matrix_precompute_arg_t: + fmpz_mat_struct * A + fmpz_mod_poly_struct * poly1 + fmpz_mod_poly_struct * poly2 + fmpz_mod_poly_struct * poly2inv + const fmpz_mod_ctx_struct * ctx + + ctypedef struct fmpz_mod_poly_compose_mod_precomp_preinv_arg_t: + fmpz_mat_struct * A + fmpz_mod_poly_struct * res + fmpz_mod_poly_struct * poly1 + fmpz_mod_poly_struct * poly3 + fmpz_mod_poly_struct * poly3inv + const fmpz_mod_ctx_struct * ctx + + # Radix conversion *********************************************************/ + ctypedef struct fmpz_mod_poly_radix_struct: + fmpz_struct *V + fmpz_struct *W + fmpz_struct **Rpow + fmpz_struct **Rinv + slong degR + slong k + fmpz_struct invL + ctypedef fmpz_mod_poly_radix_struct fmpz_mod_poly_radix_t[1] + + # Berlekamp-Massey Algorithm - see fmpz_mod_poly/berlekamp_massey.c for more info ********/ + ctypedef struct fmpz_mod_berlekamp_massey_struct: + slong npoints + fmpz_mod_poly_t R0, R1 + fmpz_mod_poly_t V0, V1 + fmpz_mod_poly_t qt, rt + fmpz_mod_poly_t points + ctypedef fmpz_mod_berlekamp_massey_struct fmpz_mod_berlekamp_massey_t[1] + + # Parsed from here + void fmpz_mod_poly_init(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_init2(fmpz_mod_poly_t poly, slong alloc, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_clear(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_realloc(fmpz_mod_poly_t poly, slong alloc, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_fit_length(fmpz_mod_poly_t poly, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_normalise(fmpz_mod_poly_t poly) + void _fmpz_mod_poly_set_length(fmpz_mod_poly_t poly, slong len) + void fmpz_mod_poly_truncate(fmpz_mod_poly_t poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_trunc(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_irreducible(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_not_zero(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_monic(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_monic_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_monic_primitive(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_trinomial(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_randtest_trinomial_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, slong max_attempts, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_pentomial(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_randtest_pentomial_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, slong max_attempts, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_randtest_sparse_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + slong fmpz_mod_poly_degree(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + slong fmpz_mod_poly_length(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + fmpz_struct * fmpz_mod_poly_lead(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set(fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_swap(fmpz_mod_poly_t poly1, fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_zero(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_one(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_zero_coeffs(fmpz_mod_poly_t poly, slong i, slong j, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_reverse(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_ui(fmpz_mod_poly_t f, ulong c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_fmpz(fmpz_mod_poly_t f, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_fmpz_poly(fmpz_mod_poly_t f, const fmpz_poly_t g, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_get_fmpz_poly(fmpz_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_get_nmod_poly(nmod_poly_t f, const fmpz_mod_poly_t g) + void fmpz_mod_poly_set_nmod_poly(fmpz_mod_poly_t f, const nmod_poly_t g) + int fmpz_mod_poly_equal(const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_equal_trunc(const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_zero(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_one(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_gen(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_coeff_fmpz(fmpz_mod_poly_t poly, slong n, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_coeff_ui(fmpz_mod_poly_t poly, slong n, ulong x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_get_coeff_fmpz(fmpz_t x, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + # void fmpz_mod_poly_set_coeff_mpz(fmpz_mod_poly_t poly, slong n, const mpz_t x, const fmpz_mod_ctx_t ctx) + # void fmpz_mod_poly_get_coeff_mpz(mpz_t x, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_shift_left(fmpz_struct * res, const fmpz_struct * poly, slong len, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_shift_left(fmpz_mod_poly_t f, const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_shift_right(fmpz_struct * res, const fmpz_struct * poly, slong len, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_shift_right(fmpz_mod_poly_t f, const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_add(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_add(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_add_series(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_sub(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_sub(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_sub_series(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_neg(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_neg(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_scalar_mul_fmpz(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_scalar_mul_fmpz(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_scalar_addmul_fmpz(fmpz_mod_poly_t rop, const fmpz_mod_poly_t op, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_scalar_div_fmpz(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_scalar_div_fmpz(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_mul(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_mul(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_mullow(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_t p, slong n) + void fmpz_mod_poly_mullow(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_sqr(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_sqr(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_mulhigh(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong start, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_mulmod(fmpz_struct * res, const fmpz_struct * poly1, slong len1, const fmpz_struct * poly2, slong len2, const fmpz_struct * f, slong lenf, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_mulmod(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_mulmod_preinv(fmpz_struct * res, const fmpz_struct * poly1, slong len1, const fmpz_struct * poly2, slong len2, const fmpz_struct * f, slong lenf, const fmpz_struct* finv, slong lenfinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_mulmod_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_product_roots_fmpz_vec(fmpz_struct * poly, const fmpz_struct * xs, slong n, fmpz_t f) + void fmpz_mod_poly_product_roots_fmpz_vec(fmpz_mod_poly_t poly, const fmpz_struct * xs, slong n, fmpz_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_find_distinct_nonzero_roots(fmpz_struct * roots, const fmpz_mod_poly_t A, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_pow(fmpz_struct *rop, const fmpz_struct *op, slong len, ulong e, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_pow(fmpz_mod_poly_t rop, const fmpz_mod_poly_t op, ulong e, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_pow_trunc(fmpz_struct * res, const fmpz_struct * poly, ulong e, slong trunc, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_pow_trunc(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, ulong e, slong trunc, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_pow_trunc_binexp(fmpz_struct * res, const fmpz_struct * poly, ulong e, slong trunc, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_pow_trunc_binexp(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, ulong e, slong trunc, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powmod_ui_binexp(fmpz_struct * res, const fmpz_struct * poly, ulong e, const fmpz_struct * f, slong lenf, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powmod_ui_binexp(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, ulong e, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powmod_ui_binexp_preinv(fmpz_struct * res, const fmpz_struct * poly, ulong e, const fmpz_struct * f, slong lenf, const fmpz_struct * finv, slong lenfinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powmod_ui_binexp_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, ulong e, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powmod_fmpz_binexp(fmpz_struct * res, const fmpz_struct * poly, const fmpz_t e, const fmpz_struct * f, slong lenf, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powmod_fmpz_binexp(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t e, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powmod_fmpz_binexp_preinv(fmpz_struct * res, const fmpz_struct * poly, const fmpz_t e, const fmpz_struct * f, slong lenf, const fmpz_struct* finv, slong lenfinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powmod_fmpz_binexp_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t e, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powmod_x_fmpz_preinv(fmpz_struct * res, const fmpz_t e, const fmpz_struct * f, slong lenf, const fmpz_struct* finv, slong lenfinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powmod_x_fmpz_preinv(fmpz_mod_poly_t res, const fmpz_t e, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_powers_mod_preinv_naive(fmpz_struct ** res, const fmpz_struct * f, slong flen, slong n, const fmpz_struct * g, slong glen, const fmpz_struct * ginv, slong ginvlen, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_powers_mod_naive(fmpz_mod_poly_struct * res, const fmpz_mod_poly_t f, slong n, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + # void _fmpz_mod_poly_powers_mod_preinv_threaded_pool(fmpz_struct ** res, const fmpz_struct * f, slong flen, slong n, const fmpz_struct * g, slong glen, const fmpz_struct * ginv, slong ginvlen, const fmpz_t p, thread_pool_handle * threads, slong num_threads) + void fmpz_mod_poly_powers_mod_bsgs(fmpz_mod_poly_struct * res, const fmpz_mod_poly_t f, slong n, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_frobenius_powers_2exp_precomp( fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, ulong m, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_frobenius_powers_2exp_clear(fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_frobenius_power(fmpz_mod_poly_t res, fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_poly_t f, ulong m, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_frobenius_powers_precomp(fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, ulong m, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_frobenius_powers_clear(fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_divrem_basecase(fmpz_struct * Q, fmpz_struct * R, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_divrem_basecase(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_divrem_newton_n_preinv (fmpz_struct* Q, fmpz_struct* R, const fmpz_struct* A, slong lenA, const fmpz_struct* B, slong lenB, const fmpz_struct* Binv, slong lenBinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_divrem_newton_n_preinv(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t Binv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_div_newton_n_preinv (fmpz_struct* Q, const fmpz_struct* A, slong lenA, const fmpz_struct* B, slong lenB, const fmpz_struct* Binv, slong lenBinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_div_newton_n_preinv(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t Binv, const fmpz_mod_ctx_t ctx) + ulong fmpz_mod_poly_remove(fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_rem_basecase(fmpz_struct * R, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_rem_basecase(fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_div(fmpz_struct * Q, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_div(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_divrem(fmpz_struct * Q, fmpz_struct * R, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_divrem(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_divrem_f(fmpz_t f, fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_rem(fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_rem_f(fmpz_t f, fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_rem(fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_divides_classical(fmpz_struct * Q, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_divides_classical(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_divides(fmpz_struct * Q, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_divides(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_inv_series(fmpz_struct * Qinv, const fmpz_struct * Q, slong n, const fmpz_t cinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_inv_series(fmpz_mod_poly_t Qinv, const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_inv_series_f(fmpz_t f, fmpz_mod_poly_t Qinv, const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_div_series(fmpz_struct * Q, const fmpz_struct * A, slong Alen, const fmpz_struct * B, slong Blen, const fmpz_t p, slong n) + void fmpz_mod_poly_div_series(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_make_monic(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_make_monic_f(fmpz_t f, fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcd(fmpz_struct *G, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcd(fmpz_mod_poly_t G, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcd_euclidean_f(fmpz_t f, fmpz_struct *G, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcd_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcd_f(fmpz_t f, fmpz_struct *G, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcd_f(fmpz_t f, fmpz_mod_poly_t G, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_hgcd(fmpz_struct **M, slong *lenM, fmpz_struct *A, slong *lenA, fmpz_struct *B, slong *lenB, const fmpz_struct *a, slong lena, const fmpz_struct *b, slong lenb, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_xgcd_euclidean_f(fmpz_t f, fmpz_struct *G, fmpz_struct *S, fmpz_struct *T, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_xgcd_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, fmpz_mod_poly_t S, fmpz_mod_poly_t T, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_xgcd(fmpz_struct *G, fmpz_struct *S, fmpz_struct *T, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_xgcd(fmpz_mod_poly_t G, fmpz_mod_poly_t S, fmpz_mod_poly_t T, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_xgcd_f(fmpz_t f, fmpz_mod_poly_t G, fmpz_mod_poly_t S, fmpz_mod_poly_t T, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcdinv_euclidean(fmpz_struct *G, fmpz_struct *S, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcdinv_euclidean(fmpz_mod_poly_t G, fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcdinv_euclidean_f(fmpz_t f, fmpz_struct *G, fmpz_struct *S, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcdinv_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcdinv(fmpz_struct *G, fmpz_struct *S, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_gcdinv_f(fmpz_t f, fmpz_struct *G, fmpz_struct *S, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcdinv(fmpz_mod_poly_t G, fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_gcdinv_f(fmpz_t f, fmpz_mod_poly_t G, fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_invmod(fmpz_struct *A, const fmpz_struct *B, slong lenB, const fmpz_struct *P, slong lenP, const fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_invmod_f(fmpz_t f, fmpz_struct *A, const fmpz_struct *B, slong lenB, const fmpz_struct *P, slong lenP, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_invmod(fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_invmod_f(fmpz_t f, fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_minpoly_bm(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_minpoly_bm(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_minpoly_hgcd(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_minpoly_hgcd(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + slong _fmpz_mod_poly_minpoly(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_minpoly(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_resultant_euclidean(fmpz_t res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_resultant_euclidean(fmpz_t r, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_resultant_hgcd(fmpz_t res, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_resultant_hgcd(fmpz_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_resultant(fmpz_t res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_resultant(fmpz_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_discriminant(fmpz_t d, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_discriminant(fmpz_t d, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_derivative(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_derivative(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_evaluate_fmpz(fmpz_t res, const fmpz_struct *poly, slong len, const fmpz_t a, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_evaluate_fmpz(fmpz_t res, const fmpz_mod_poly_t poly, const fmpz_t a, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_evaluate_fmpz_vec_iter(fmpz_struct * ys, const fmpz_struct * coeffs, slong len, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_evaluate_fmpz_vec_iter(fmpz_struct * ys, const fmpz_mod_poly_t poly, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_evaluate_fmpz_vec_fast_precomp(fmpz_struct * vs, const fmpz_struct * poly, slong plen, fmpz_poly_struct * const * tree, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_evaluate_fmpz_vec_fast(fmpz_struct * ys, const fmpz_struct * poly, slong plen, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_evaluate_fmpz_vec_fast(fmpz_struct * ys, const fmpz_mod_poly_t poly, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_evaluate_fmpz_vec(fmpz_struct * ys, const fmpz_struct * coeffs, slong len, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_evaluate_fmpz_vec(fmpz_struct * ys, const fmpz_mod_poly_t poly, const fmpz_struct * xs, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_invsqrt_series(fmpz_struct * g, const fmpz_struct * h, slong hlen, slong n, fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_invsqrt_series(fmpz_mod_poly_t g, const fmpz_mod_poly_t h, slong n, fmpz_mod_ctx_t ctx) # TODO: Typo: fmpz_ctx_t should be fmpz_mod_ctx_t + void _fmpz_mod_poly_sqrt_series(fmpz_struct * g, const fmpz_struct * h, slong hlen, slong n, fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_sqrt_series(fmpz_mod_poly_t g, const fmpz_mod_poly_t h, slong n, fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_sqrt(fmpz_struct * s, const fmpz_struct * p, slong n, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_sqrt(fmpz_mod_poly_t s, const fmpz_mod_poly_t p, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod(fmpz_struct * res, const fmpz_struct * f, slong lenf, const fmpz_struct * g, const fmpz_struct * h, slong lenh, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod(fmpz_mod_poly_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_poly_t h, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod_horner(fmpz_struct * res, const fmpz_struct * f, slong lenf, const fmpz_struct * g, const fmpz_struct * h, slong lenh, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod_horner(fmpz_mod_poly_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_poly_t h, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod_brent_kung(fmpz_struct * res, const fmpz_struct * f, slong len1, const fmpz_struct * g, const fmpz_struct * h, slong len3, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod_brent_kung(fmpz_mod_poly_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_poly_t h, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_reduce_matrix_mod_poly (fmpz_mat_t A, const fmpz_mat_t B, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_precompute_matrix_worker(void * arg_ptr) + void _fmpz_mod_poly_precompute_matrix (fmpz_mat_t A, const fmpz_struct * f, const fmpz_struct * g, slong leng, const fmpz_struct * ginv, slong lenginv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_precompute_matrix(fmpz_mat_t A, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_poly_t ginv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod_brent_kung_precomp_preinv_worker(void * arg_ptr) + void _fmpz_mod_poly_compose_mod_brent_kung_precomp_preinv(fmpz_struct * res, const fmpz_struct * f, slong lenf, const fmpz_mat_t A, const fmpz_struct * h, slong lenh, const fmpz_struct * hinv, slong lenhinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod_brent_kung_precomp_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t f, const fmpz_mat_t A, const fmpz_mod_poly_t h, const fmpz_mod_poly_t hinv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod_brent_kung_preinv(fmpz_struct * res, const fmpz_struct * f, slong lenf, const fmpz_struct * g, const fmpz_struct * h, slong lenh, const fmpz_struct * hinv, slong lenhinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod_brent_kung_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_poly_t h, const fmpz_mod_poly_t hinv, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_compose_mod_brent_kung_vec_preinv(fmpz_mod_poly_struct * res, const fmpz_mod_poly_struct * polys, slong len1, slong l, const fmpz_struct * g, slong glen, const fmpz_struct * h, slong lenh, const fmpz_struct * hinv, slong lenhinv, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv(fmpz_mod_poly_struct * res, const fmpz_mod_poly_struct * polys, slong len1, slong n, const fmpz_mod_poly_t g, const fmpz_mod_poly_t h, const fmpz_mod_poly_t hinv, const fmpz_mod_ctx_t ctx) + # void _fmpz_mod_poly_compose_mod_brent_kung_vec_preinv_threaded_pool(fmpz_mod_poly_struct * res, const fmpz_mod_poly_struct * polys, slong lenpolys, slong l, const fmpz_struct * g, slong glen, const fmpz_struct * poly, slong len, const fmpz_struct * polyinv, slong leninv, const fmpz_t p, thread_pool_handle * threads, slong num_threads) + # void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv_threaded_pool(fmpz_mod_poly_struct * res,const fmpz_mod_poly_struct * polys, slong len1, slong n, const fmpz_mod_poly_t g, const fmpz_mod_poly_t poly, const fmpz_mod_poly_t polyinv, const fmpz_mod_ctx_t ctx, thread_pool_handle * threads, slong num_threads) + void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv_threaded(fmpz_mod_poly_struct * res, const fmpz_mod_poly_struct * polys, slong len1, slong n, const fmpz_mod_poly_t g, const fmpz_mod_poly_t poly, const fmpz_mod_poly_t polyinv, const fmpz_mod_ctx_t ctx) + fmpz_poly_struct ** _fmpz_mod_poly_tree_alloc(slong len) + void _fmpz_mod_poly_tree_free(fmpz_poly_struct ** tree, slong len) + void _fmpz_mod_poly_tree_build(fmpz_poly_struct ** tree, const fmpz_struct * roots, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_radix_init(fmpz_struct **Rpow, fmpz_struct **Rinv, const fmpz_struct *R, slong lenR, slong k, const fmpz_t invL, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_radix_init(fmpz_mod_poly_radix_t D, const fmpz_mod_poly_t R, slong degF, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_radix(fmpz_struct **B, const fmpz_struct *F, fmpz_struct **Rpow, fmpz_struct **Rinv, slong degR, slong k, slong i, fmpz_struct *W, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_radix(fmpz_mod_poly_struct **B, const fmpz_mod_poly_t F, const fmpz_mod_poly_radix_t D, const fmpz_mod_ctx_t ctx) + # int _fmpz_mod_poly_fprint(FILE * file, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) + # int fmpz_mod_poly_fprint(FILE * file, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + # int fmpz_mod_poly_fprint_pretty(FILE * file, const fmpz_mod_poly_t poly, const char * x, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_print(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_print_pretty(const fmpz_mod_poly_t poly, const char * x, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_inflate(fmpz_mod_poly_t result, const fmpz_mod_poly_t input, ulong inflation, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_deflate(fmpz_mod_poly_t result, const fmpz_mod_poly_t input, ulong deflation, const fmpz_mod_ctx_t ctx) + ulong fmpz_mod_poly_deflation(const fmpz_mod_poly_t input, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_init(fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_clear(fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_start_over(fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_add_points(fmpz_mod_berlekamp_massey_t B, const fmpz_struct * a, slong count, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_add_zeros(fmpz_mod_berlekamp_massey_t B, slong count, const fmpz_mod_ctx_t ctx) + void fmpz_mod_berlekamp_massey_add_point(fmpz_mod_berlekamp_massey_t B, const fmpz_t a, const fmpz_mod_ctx_t ctx) + int fmpz_mod_berlekamp_massey_reduce(fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + slong fmpz_mod_berlekamp_massey_point_count(const fmpz_mod_berlekamp_massey_t B) + const fmpz_struct * fmpz_mod_berlekamp_massey_points(const fmpz_mod_berlekamp_massey_t B) + const fmpz_mod_poly_struct * fmpz_mod_berlekamp_massey_V_poly(const fmpz_mod_berlekamp_massey_t B) + const fmpz_mod_poly_struct * fmpz_mod_berlekamp_massey_R_poly(const fmpz_mod_berlekamp_massey_t B) diff --git a/src/flint/flintlib/fmpz_mod_poly_factor.pxd b/src/flint/flintlib/fmpz_mod_poly_factor.pxd new file mode 100644 index 00000000..40889ee8 --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_poly_factor.pxd @@ -0,0 +1,46 @@ +from flint.flintlib.flint cimport fmpz_struct, slong, flint_rand_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t +from flint.flintlib.fmpz_factor cimport fmpz_factor_t +from flint.flintlib.fmpz_mod_poly cimport fmpz_mod_poly_struct, fmpz_mod_poly_t + +# unimported types {'fmpz_mod_poly_factor_t', 'void', 'fmpz_mod_poly_t'} + +cdef extern from "flint/fmpz_mod_poly_factor.h": + ctypedef struct fmpz_mod_poly_factor_struct: + fmpz_mod_poly_struct * poly + slong *exp + slong num + slong alloc + ctypedef fmpz_mod_poly_factor_struct fmpz_mod_poly_factor_t[1] + + # Parsed from here + void fmpz_mod_poly_factor_init(fmpz_mod_poly_factor_t fac, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_clear(fmpz_mod_poly_factor_t fac, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_realloc(fmpz_mod_poly_factor_t fac, slong alloc, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_fit_length(fmpz_mod_poly_factor_t fac, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_set(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_factor_t fac, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_print(const fmpz_mod_poly_factor_t fac, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_insert(fmpz_mod_poly_factor_t fac, const fmpz_mod_poly_t poly, slong exp, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_concat(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_factor_t fac, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_pow(fmpz_mod_poly_factor_t fac, slong exp, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_irreducible(const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_irreducible_ddf(const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_irreducible_rabin(const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_irreducible_rabin_f(fmpz_t r, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_is_squarefree(const fmpz_struct * f, slong len, const fmpz_mod_ctx_t ctx) + int _fmpz_mod_poly_is_squarefree_f(fmpz_t fac, const fmpz_struct * f, slong len, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_squarefree(const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_squarefree_f(fmpz_t fac, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_factor_equal_deg_prob(fmpz_mod_poly_t factor, flint_rand_t state, const fmpz_mod_poly_t pol, slong d, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_equal_deg(fmpz_mod_poly_factor_t factors, const fmpz_mod_poly_t pol, slong d, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_distinct_deg(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t poly, slong * const *degs, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_distinct_deg_threaded(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t poly, slong * const *degs, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_squarefree(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_cantor_zassenhaus(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_kaltofen_shoup(fmpz_mod_poly_factor_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_factor_berlekamp(fmpz_mod_poly_factor_t factors, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_poly_interval_poly_worker(void* arg_ptr) + void fmpz_mod_poly_roots(fmpz_mod_poly_factor_t r, const fmpz_mod_poly_t f, int with_multiplicity, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_roots_factored(fmpz_mod_poly_factor_t r, const fmpz_mod_poly_t f, int with_multiplicity, const fmpz_factor_t n, const fmpz_mod_ctx_t ctx) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 3a5b7622..7979e789 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1813,7 +1813,17 @@ def test_fmpz_mod_dlog(): x = g.discrete_log(a) assert g**x == a +def test_fmpz_mod_poly(): + from flint import fmpz_poly, fmpz_mod_poly_ctx + + R = fmpz_mod_poly_ctx(11) + f = fmpz_poly([1,2,3]) + g = R(f) + assert str(g) == "3*x^2 + 2*x + 1" + f = fmpz_poly([12,13,14]) + g = R(f) + assert str(g) == "3*x^2 + 2*x + 1" all_tests = [ test_pyflint, @@ -1834,5 +1844,6 @@ def test_fmpz_mod_dlog(): test_nmod_mat, test_arb, test_fmpz_mod, - test_fmpz_mod_dlog + test_fmpz_mod_dlog, + test_fmpz_mod_poly ] diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd new file mode 100644 index 00000000..95877dc6 --- /dev/null +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -0,0 +1,14 @@ +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t +from flint.flintlib.fmpz_mod_poly cimport * + +from flint.flint_base.flint_base cimport flint_poly +from flint.types.fmpz_mod cimport fmpz_mod_ctx + +cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): + pass + +cdef class fmpz_mod_poly(flint_poly): + cdef fmpz_mod_poly_t val + cdef fmpz_mod_poly_ctx ctx + cpdef long length(self) + cpdef long degree(self) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx new file mode 100644 index 00000000..a32f3c49 --- /dev/null +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -0,0 +1,91 @@ +from flint.flintlib.fmpz_mod_poly cimport * +from flint.flintlib.fmpz cimport( + fmpz_equal, +) +from flint.types.fmpz cimport fmpz +from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod +from flint.types.fmpz_poly cimport any_as_fmpz_poly, fmpz_poly + +from flint.flint_base.flint_base cimport flint_poly +from flint.utils.typecheck cimport typecheck + +cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): + """ + NOTE: + + Technically this could just be the same as `fmpz_mod_ctx`, + however, the usage of fmpz_mod_ctx allows the creation of + `fmpz_mod` types by calling the context class. For symmetry + we allow this to be the case here. + """ + def __eq__(self, other): + # Most often, we expect both `fmpz_mod_poly` to be pointing + # to the same ctx, so this seems the fastest way to check + if self is other: + return True + + # If they're not the same object in memory, they may have the + # same modulus, which is good enough + if typecheck(other, fmpz_mod_poly_ctx): + return fmpz_equal(self.val.n, (other).val.n) + return False + + def __str__(self): + return f"Context for fmpz_mod_poly with modulus: {self.modulus()}" + + def __repr__(self): + return f"fmpz_mod_poly_ctx({self.modulus()})" + + def __call__(self, val): + return fmpz_mod_poly(val, self) + + +cdef class fmpz_mod_poly(flint_poly): + """ + """ + def __cinit__(self): + fmpz_mod_poly_init(self.val, self.ctx.val) + + def __dealloc__(self): + fmpz_mod_poly_clear(self.val, self.ctx.val) + + def __init__(self, val, ctx): + if not typecheck(ctx, fmpz_mod_ctx): + raise TypeError + self.ctx = ctx + + val = any_as_fmpz_poly(val) + if val is NotImplemented: + raise TypeError + + fmpz_mod_poly_set_fmpz_poly( + self.val, (val).val, self.ctx.val + ) + + def __getitem__(self, long i): + cdef fmpz x + x = fmpz() + if i < 0: + return x + fmpz_mod_poly_get_coeff_fmpz(x.val, self.val, i, self.ctx.val) + return x + + def __setitem__(self, long i, x): + if i < 0: + raise ValueError("cannot assign to index < 0 of polynomial") + v = self.ctx.any_as_fmpz_mod(x) + if v is NotImplemented: + raise TypeError + fmpz_mod_poly_set_coeff_fmpz(self.val, i, (v).val, self.ctx.val) + + def repr(self): + return "fmpz_mod_poly([%s])" % (", ".join(map(str, self.coeffs()))) + + def __len__(self): + return fmpz_mod_poly_length(self.val, self.ctx.val) + + cpdef long length(self): + return fmpz_mod_poly_length(self.val, self.ctx.val) + + cpdef long degree(self): + return fmpz_mod_poly_degree(self.val, self.ctx.val) From 6b1611ab7f9e9a130c179a184c844c5aced29ef2 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 20 Sep 2023 17:46:41 +0100 Subject: [PATCH 02/35] Modify methods for contexts --- src/flint/test/test.py | 44 +++++++++++++++++++++++++++---- src/flint/types/fmpz_mod.pyx | 6 ++--- src/flint/types/fmpz_mod_poly.pxd | 7 +++-- src/flint/types/fmpz_mod_poly.pyx | 40 ++++++++++++++++++++++++---- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 7979e789..4bd5ed23 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1814,17 +1814,51 @@ def test_fmpz_mod_dlog(): assert g**x == a def test_fmpz_mod_poly(): - from flint import fmpz_poly, fmpz_mod_poly_ctx - - R = fmpz_mod_poly_ctx(11) + from flint import fmpz_poly, fmpz_mod_poly_ctx, fmpz_mod_ctx + + # fmpz_mod_poly_ctx tests + F = fmpz_mod_ctx(11) + R1 = fmpz_mod_poly_ctx(F) + R2 = fmpz_mod_poly_ctx(11) + R3 = fmpz_mod_poly_ctx(13) + + assert raises(lambda: fmpz_mod_ctx("AAA"), TypeError) + assert raises(lambda: fmpz_mod_ctx(-1), ValueError) + assert (R1 == R1) is True + assert (R1 == R2) is True + assert (R1 != R3) is True + assert (R1 != "AAA") is True + + assert (hash(R1) == hash(R1)) is True + assert (hash(R1) == hash(R2)) is True + assert (hash(R1) != hash(R3)) is True + + assert str(R1) == "Context for fmpz_mod_poly with modulus: 11" + assert str(R1) == str(R2) + assert repr(R3) == "fmpz_mod_poly_ctx(13)" + + assert R1.modulus() == 11 + + # fmpz_mod_poly tests + # TODO + R = fmpz_mod_poly_ctx(F) f = fmpz_poly([1,2,3]) - g = R(f) + g = R1(f) assert str(g) == "3*x^2 + 2*x + 1" f = fmpz_poly([12,13,14]) - g = R(f) + g = R1(f) assert str(g) == "3*x^2 + 2*x + 1" + f = fmpz_poly([5, 6, 7]) + g = R1(f) + assert g[0] == 5 + assert repr(g[0]) == "fmpz_mod(5, 11)" + + g[0] = 7 + assert repr(g[0]) == "fmpz_mod(7, 11)" + assert str(g) == "7*x^2 + 6*x + 7" + all_tests = [ test_pyflint, test_fmpz, diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 686d21e4..340e9ec1 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -52,7 +52,7 @@ cdef class fmpz_mod_ctx: mod = any_as_fmpz(mod) if mod is NotImplemented: raise TypeError( - "Context modulus must be able to be case to an `fmpz` type" + "Context modulus must be able to be cast to an `fmpz` type" ) # Ensure modulus is positive @@ -126,11 +126,11 @@ cdef class fmpz_mod_ctx: # If they're not the same object in memory, they may have the # same modulus, which is good enough if typecheck(other, fmpz_mod_ctx): - return fmpz_equal(self.val.n, (other).val.n) + return fmpz_equal(self.val.n, (other).val.n) == 1 return False def __hash__(self): - return hash(self.modulus()) + return hash(repr(self)) def __str__(self): return f"Context for fmpz_mod with modulus: {self.modulus()}" diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 95877dc6..0a185de7 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -1,14 +1,13 @@ -from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t from flint.flintlib.fmpz_mod_poly cimport * from flint.flint_base.flint_base cimport flint_poly from flint.types.fmpz_mod cimport fmpz_mod_ctx -cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): - pass +cdef class fmpz_mod_poly_ctx: + cdef fmpz_mod_ctx mod cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_t val - cdef fmpz_mod_poly_ctx ctx + cdef fmpz_mod_ctx ctx cpdef long length(self) cpdef long degree(self) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index a32f3c49..d70e4bad 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,6 +1,7 @@ from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz cimport( fmpz_equal, + fmpz_set ) from flint.types.fmpz cimport fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod @@ -9,7 +10,7 @@ from flint.types.fmpz_poly cimport any_as_fmpz_poly, fmpz_poly from flint.flint_base.flint_base cimport flint_poly from flint.utils.typecheck cimport typecheck -cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): +cdef class fmpz_mod_poly_ctx: """ NOTE: @@ -18,6 +19,31 @@ cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): `fmpz_mod` types by calling the context class. For symmetry we allow this to be the case here. """ + def __cinit__(self): + pass + + def __dealloc__(self): + pass + + def __init__(self, mod): + # Allow context to be made from fmpz_mod_ctx + if typecheck(mod, fmpz_mod_ctx): + self.mod = mod + else: # Otherwise attempt to create context from moduli + self.mod = fmpz_mod_ctx(mod) + + def modulus(self): + """ + Return the modulus from the context as an fmpz + type + + >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) + >>> mod_ctx.modulus() + 170141183460469231731687303715884105727 + + """ + return self.mod.modulus() + def __eq__(self, other): # Most often, we expect both `fmpz_mod_poly` to be pointing # to the same ctx, so this seems the fastest way to check @@ -27,8 +53,11 @@ cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): # If they're not the same object in memory, they may have the # same modulus, which is good enough if typecheck(other, fmpz_mod_poly_ctx): - return fmpz_equal(self.val.n, (other).val.n) + return self.mod == (other).mod return False + + def __hash__(self): + return hash(repr(self)) def __str__(self): return f"Context for fmpz_mod_poly with modulus: {self.modulus()}" @@ -37,7 +66,7 @@ cdef class fmpz_mod_poly_ctx(fmpz_mod_ctx): return f"fmpz_mod_poly_ctx({self.modulus()})" def __call__(self, val): - return fmpz_mod_poly(val, self) + return fmpz_mod_poly(val, self.mod) cdef class fmpz_mod_poly(flint_poly): @@ -63,8 +92,9 @@ cdef class fmpz_mod_poly(flint_poly): ) def __getitem__(self, long i): - cdef fmpz x - x = fmpz() + cdef fmpz_mod x + x = fmpz_mod.__new__(fmpz_mod) + x.ctx = self.ctx if i < 0: return x fmpz_mod_poly_get_coeff_fmpz(x.val, self.val, i, self.ctx.val) From 6797150344718243fff7b0a026479ff8b46ab8ee Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 21 Sep 2023 15:16:06 +0100 Subject: [PATCH 03/35] Add conversion and comparison functions --- src/flint/flintlib/fmpz_mod_poly.pxd | 37 ++++++ src/flint/test/test.py | 73 ++++++++---- src/flint/types/fmpz_mod.pyx | 4 +- src/flint/types/fmpz_mod_poly.pxd | 6 +- src/flint/types/fmpz_mod_poly.pyx | 161 +++++++++++++++++++++++---- 5 files changed, 236 insertions(+), 45 deletions(-) diff --git a/src/flint/flintlib/fmpz_mod_poly.pxd b/src/flint/flintlib/fmpz_mod_poly.pxd index 2df3f851..227beb00 100644 --- a/src/flint/flintlib/fmpz_mod_poly.pxd +++ b/src/flint/flintlib/fmpz_mod_poly.pxd @@ -67,6 +67,7 @@ cdef extern from "flint/fmpz_mod_poly.h": ctypedef fmpz_mod_berlekamp_massey_struct fmpz_mod_berlekamp_massey_t[1] # Parsed from here + # Memory Management void fmpz_mod_poly_init(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_init2(fmpz_mod_poly_t poly, slong alloc, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_clear(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) @@ -76,6 +77,8 @@ cdef extern from "flint/fmpz_mod_poly.h": void _fmpz_mod_poly_set_length(fmpz_mod_poly_t poly, slong len) void fmpz_mod_poly_truncate(fmpz_mod_poly_t poly, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_set_trunc(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + + # Randomisation void fmpz_mod_poly_randtest(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_randtest_irreducible(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_randtest_not_zero(fmpz_mod_poly_t f, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) @@ -87,35 +90,49 @@ cdef extern from "flint/fmpz_mod_poly.h": void fmpz_mod_poly_randtest_pentomial(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_randtest_pentomial_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, slong max_attempts, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_randtest_sparse_irreducible(fmpz_mod_poly_t poly, flint_rand_t state, slong len, const fmpz_mod_ctx_t ctx) + + # Attributes slong fmpz_mod_poly_degree(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) slong fmpz_mod_poly_length(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) fmpz_struct * fmpz_mod_poly_lead(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + # Assignment and Basic Manipulation void fmpz_mod_poly_set(fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_swap(fmpz_mod_poly_t poly1, fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_zero(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_one(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_zero_coeffs(fmpz_mod_poly_t poly, slong i, slong j, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_reverse(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + + # Conversion void fmpz_mod_poly_set_ui(fmpz_mod_poly_t f, ulong c, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_set_fmpz(fmpz_mod_poly_t f, const fmpz_t c, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_set_fmpz_poly(fmpz_mod_poly_t f, const fmpz_poly_t g, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_get_fmpz_poly(fmpz_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_get_nmod_poly(nmod_poly_t f, const fmpz_mod_poly_t g) void fmpz_mod_poly_set_nmod_poly(fmpz_mod_poly_t f, const nmod_poly_t g) + + # Comparison int fmpz_mod_poly_equal(const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_equal_trunc(const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_is_zero(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_is_one(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_is_gen(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + # Getting and Setting coefficients void fmpz_mod_poly_set_coeff_fmpz(fmpz_mod_poly_t poly, slong n, const fmpz_t x, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_set_coeff_ui(fmpz_mod_poly_t poly, slong n, ulong x, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_get_coeff_fmpz(fmpz_t x, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) # void fmpz_mod_poly_set_coeff_mpz(fmpz_mod_poly_t poly, slong n, const mpz_t x, const fmpz_mod_ctx_t ctx) # void fmpz_mod_poly_get_coeff_mpz(mpz_t x, const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + + # Shifiting void _fmpz_mod_poly_shift_left(fmpz_struct * res, const fmpz_struct * poly, slong len, slong n, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_shift_left(fmpz_mod_poly_t f, const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_shift_right(fmpz_struct * res, const fmpz_struct * poly, slong len, slong n, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_shift_right(fmpz_mod_poly_t f, const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) + + # Addition and Subtraction void _fmpz_mod_poly_add(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_add(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_add_series(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) @@ -124,11 +141,15 @@ cdef extern from "flint/fmpz_mod_poly.h": void fmpz_mod_poly_sub_series(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_neg(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_neg(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + # Scalar Multiplication and division void _fmpz_mod_poly_scalar_mul_fmpz(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_t x, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_scalar_mul_fmpz(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_scalar_addmul_fmpz(fmpz_mod_poly_t rop, const fmpz_mod_poly_t op, const fmpz_t x, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_scalar_div_fmpz(fmpz_struct *res, const fmpz_struct *poly, slong len, const fmpz_t x, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_scalar_div_fmpz(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) + + # Multiplication void _fmpz_mod_poly_mul(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_mul(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_mullow(fmpz_struct *res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_t p, slong n) @@ -140,9 +161,13 @@ cdef extern from "flint/fmpz_mod_poly.h": void fmpz_mod_poly_mulmod(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_mulmod_preinv(fmpz_struct * res, const fmpz_struct * poly1, slong len1, const fmpz_struct * poly2, slong len2, const fmpz_struct * f, slong lenf, const fmpz_struct* finv, slong lenfinv, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_mulmod_preinv(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, const fmpz_mod_ctx_t ctx) + + # Products void _fmpz_mod_poly_product_roots_fmpz_vec(fmpz_struct * poly, const fmpz_struct * xs, slong n, fmpz_t f) void fmpz_mod_poly_product_roots_fmpz_vec(fmpz_mod_poly_t poly, const fmpz_struct * xs, slong n, fmpz_t f, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_find_distinct_nonzero_roots(fmpz_struct * roots, const fmpz_mod_poly_t A, const fmpz_mod_ctx_t ctx) + + # Powering void _fmpz_mod_poly_pow(fmpz_struct *rop, const fmpz_struct *op, slong len, ulong e, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_pow(fmpz_mod_poly_t rop, const fmpz_mod_poly_t op, ulong e, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_pow_trunc(fmpz_struct * res, const fmpz_struct * poly, ulong e, slong trunc, const fmpz_mod_ctx_t ctx) @@ -168,6 +193,8 @@ cdef extern from "flint/fmpz_mod_poly.h": void fmpz_mod_poly_frobenius_power(fmpz_mod_poly_t res, fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_poly_t f, ulong m, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_frobenius_powers_precomp(fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, ulong m, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_frobenius_powers_clear(fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_ctx_t ctx) + + # Division void _fmpz_mod_poly_divrem_basecase(fmpz_struct * Q, fmpz_struct * R, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_divrem_basecase(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_divrem_newton_n_preinv (fmpz_struct* Q, fmpz_struct* R, const fmpz_struct* A, slong lenA, const fmpz_struct* B, slong lenB, const fmpz_struct* Binv, slong lenBinv, const fmpz_mod_ctx_t ctx) @@ -185,15 +212,21 @@ cdef extern from "flint/fmpz_mod_poly.h": void _fmpz_mod_poly_rem(fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_rem_f(fmpz_t f, fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_rem(fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + # Divisibility testing int _fmpz_mod_poly_divides_classical(fmpz_struct * Q, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_divides_classical(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, fmpz_mod_ctx_t ctx) int _fmpz_mod_poly_divides(fmpz_struct * Q, const fmpz_struct * A, slong lenA, const fmpz_struct * B, slong lenB, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_divides(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, fmpz_mod_ctx_t ctx) + + # Power series division void _fmpz_mod_poly_inv_series(fmpz_struct * Qinv, const fmpz_struct * Q, slong n, const fmpz_t cinv, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_inv_series(fmpz_mod_poly_t Qinv, const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_inv_series_f(fmpz_t f, fmpz_mod_poly_t Qinv, const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_div_series(fmpz_struct * Q, const fmpz_struct * A, slong Alen, const fmpz_struct * B, slong Blen, const fmpz_t p, slong n) void fmpz_mod_poly_div_series(fmpz_mod_poly_t Q, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, slong n, const fmpz_mod_ctx_t ctx) + + # Greatest common divisor void fmpz_mod_poly_make_monic(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_make_monic_f(fmpz_t f, fmpz_mod_poly_t res, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) slong _fmpz_mod_poly_gcd(fmpz_struct *G, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) @@ -220,12 +253,16 @@ cdef extern from "flint/fmpz_mod_poly.h": int _fmpz_mod_poly_invmod_f(fmpz_t f, fmpz_struct *A, const fmpz_struct *B, slong lenB, const fmpz_struct *P, slong lenP, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_invmod(fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) int fmpz_mod_poly_invmod_f(fmpz_t f, fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) + + # Minpoly slong _fmpz_mod_poly_minpoly_bm(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_minpoly_bm(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) slong _fmpz_mod_poly_minpoly_hgcd(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_minpoly_hgcd(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) slong _fmpz_mod_poly_minpoly(fmpz_struct* poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_minpoly(fmpz_mod_poly_t poly, const fmpz_struct* seq, slong len, const fmpz_mod_ctx_t ctx) + + # Resultant void _fmpz_mod_poly_resultant_euclidean(fmpz_t res, const fmpz_struct *poly1, slong len1, const fmpz_struct *poly2, slong len2, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_resultant_euclidean(fmpz_t r, const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_resultant_hgcd(fmpz_t res, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_mod_ctx_t ctx) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 4bd5ed23..2239eb09 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1814,7 +1814,7 @@ def test_fmpz_mod_dlog(): assert g**x == a def test_fmpz_mod_poly(): - from flint import fmpz_poly, fmpz_mod_poly_ctx, fmpz_mod_ctx + from flint import fmpz_poly, fmpz_mod_poly_ctx, fmpz_mod_ctx, fmpz # fmpz_mod_poly_ctx tests F = fmpz_mod_ctx(11) @@ -1838,26 +1838,61 @@ def test_fmpz_mod_poly(): assert repr(R3) == "fmpz_mod_poly_ctx(13)" assert R1.modulus() == 11 + assert R1([0,1]) == R1.gen() - # fmpz_mod_poly tests - # TODO + # Conversion tests + F = fmpz_mod_ctx(11) R = fmpz_mod_poly_ctx(F) - f = fmpz_poly([1,2,3]) - g = R1(f) - assert str(g) == "3*x^2 + 2*x + 1" - - f = fmpz_poly([12,13,14]) - g = R1(f) - assert str(g) == "3*x^2 + 2*x + 1" - - f = fmpz_poly([5, 6, 7]) - g = R1(f) - assert g[0] == 5 - assert repr(g[0]) == "fmpz_mod(5, 11)" - - g[0] = 7 - assert repr(g[0]) == "fmpz_mod(7, 11)" - assert str(g) == "7*x^2 + 6*x + 7" + + f1 = R([int(-1),int(-2),int(-3)]) + f2 = R([fmpz(-1),fmpz(-2),fmpz(-3)]) + f3 = R([F(-1),F(-2),F(-3)]) + f4 = R(fmpz_poly([-1, -2, -3])) + + assert str(f1) == "8*x^2 + 9*x + 10" + assert str(f2) == "8*x^2 + 9*x + 10" + assert str(f3) == "8*x^2 + 9*x + 10" + assert str(f4) == "8*x^2 + 9*x + 10" + + f1 = R(5) + f2 = R(fmpz(6)) + f3 = R(F(7)) + assert str(f1) == "5" + assert str(f2) == "6" + assert str(f3) == "7" + + # Printing + f = R([5, 6, 7, 8]) + assert str(f) == "8*x^3 + 7*x^2 + 6*x + 5" + assert repr(f) == "fmpz_mod_poly([5, 6, 7, 8], fmpz_mod_poly_ctx(11))" + + # Get and Set tests + f = R([5, 6, 7, 8]) + assert f[0] == 5 + assert repr(f[0]) == "fmpz_mod(5, 11)" + f[0] = 7 + assert repr(f[0]) == "fmpz_mod(7, 11)" + assert str(f) == "8*x^3 + 7*x^2 + 6*x + 7" + + # Comparisons + f1 = R([1,2,3]) + f2 = R([12,13,14]) + f3 = R([4,5,6]) + f4 = R([3]) + + assert (f1 == f2) is True + assert (f1 != f3) is True + assert (f1 != "1") is True + assert (f4 == 3) is True + + f1 = R([0]) + f2 = R([1]) + f3 = R([0, 1]) + + assert f1.is_zero() is True + assert f2.is_one() is True + assert f3.is_gen() is True + all_tests = [ test_pyflint, diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 340e9ec1..ca49bee0 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -67,7 +67,7 @@ cdef class fmpz_mod_ctx: Return the modulus from the context as an fmpz type - >>> mod_ctx = fmpz_mod_ctx(2**127 - 1) + >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) >>> mod_ctx.modulus() 170141183460469231731687303715884105727 @@ -345,7 +345,7 @@ cdef class fmpz_mod(flint_scalar): if not typecheck(other, fmpz_mod): other = self.ctx.any_as_fmpz_mod(other) - if typecheck(self, fmpz_mod) and typecheck(other, fmpz_mod): + if typecheck(other, fmpz_mod): res = fmpz_equal(self.val, (other).val) and \ (self.ctx == (other).ctx) if op == 2: diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 0a185de7..f2f755e8 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -3,11 +3,15 @@ from flint.flintlib.fmpz_mod_poly cimport * from flint.flint_base.flint_base cimport flint_poly from flint.types.fmpz_mod cimport fmpz_mod_ctx + cdef class fmpz_mod_poly_ctx: cdef fmpz_mod_ctx mod + cdef any_as_fmpz_mod_poly(self, obj) + cdef set_any_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, obj) + cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val) cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_t val - cdef fmpz_mod_ctx ctx + cdef fmpz_mod_poly_ctx ctx cpdef long length(self) cpdef long degree(self) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index d70e4bad..6724ae77 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,9 +1,12 @@ +from cpython.list cimport PyList_GET_SIZE from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz cimport( fmpz_equal, - fmpz_set + fmpz_set, + fmpz_init, + fmpz_clear ) -from flint.types.fmpz cimport fmpz +from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod from flint.types.fmpz_poly cimport any_as_fmpz_poly, fmpz_poly @@ -44,6 +47,94 @@ cdef class fmpz_mod_poly_ctx: """ return self.mod.modulus() + def gen(self): + """ + Return the generator of the polynomial `x` + + >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) + >>> mod_ctx.gen() + fmpz_mod_poly([0, 1], fmpz_mod_poly_ctx(170141183460469231731687303715884105727)) + >>> str(mod_ctx.gen()) + 'x' + """ + return self.any_as_fmpz_mod_poly([0, 1]) + + cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val): + cdef long i, n + cdef fmpz_t x + + n = PyList_GET_SIZE(val) + fmpz_mod_poly_fit_length(poly, n, self.mod.val) + + # TODO: should we support conversion from nmod? + fmpz_init(x) + for i in range(n): + if typecheck(val[i], fmpz_mod): + if self.mod != ((val[i])).ctx: + raise ValueError("moduli must match") + fmpz_mod_poly_set_coeff_fmpz( + poly, i, ((val[i])).val, self.mod.val + ) + elif typecheck(val[i], fmpz): + fmpz_mod_poly_set_coeff_fmpz( + poly, i, ((val[i])).val, self.mod.val + ) + else: + val_fmpz = any_as_fmpz(val[i]) + if val_fmpz is NotImplemented: + fmpz_clear(x) + raise TypeError("unsupported coefficient in list") + fmpz_mod_poly_set_coeff_fmpz( + poly, i, ((val_fmpz)).val, self.mod.val + ) + fmpz_clear(x) + return 0 + + cdef set_any_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, obj): + if typecheck(obj, list): + return self.set_list_as_fmpz_mod_poly(poly, obj) + + # Convert fmpz_mod to constant poly + if typecheck(obj, fmpz_mod): + if self.mod != (obj).ctx: + raise ValueError("moduli must match") + fmpz_mod_poly_set_fmpz( + poly, (obj).val, self.mod.val + ) + return 0 + + # Reduced fmpz_poly modulo mod + if typecheck(obj, fmpz_poly): + fmpz_mod_poly_set_fmpz_poly( + poly, (obj).val, self.mod.val + ) + return 0 + + + obj = any_as_fmpz(obj) + if obj is NotImplemented: + return NotImplemented + fmpz_mod_poly_set_fmpz( + poly, (obj).val, self.mod.val + ) + return 0 + + cdef any_as_fmpz_mod_poly(self, obj): + # Convert fmpz_mod_poly + if typecheck(obj, fmpz_mod_poly): + if self.mod != (obj).ctx: + raise ValueError("moduli must match") + return obj + + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + check = self.set_any_as_fmpz_mod_poly(res.val, obj) + if check is NotImplemented: + return NotImplemented + + res.ctx = self + return res + def __eq__(self, other): # Most often, we expect both `fmpz_mod_poly` to be pointing # to the same ctx, so this seems the fastest way to check @@ -66,56 +157,80 @@ cdef class fmpz_mod_poly_ctx: return f"fmpz_mod_poly_ctx({self.modulus()})" def __call__(self, val): - return fmpz_mod_poly(val, self.mod) + return fmpz_mod_poly(val, self) cdef class fmpz_mod_poly(flint_poly): """ """ def __cinit__(self): - fmpz_mod_poly_init(self.val, self.ctx.val) + fmpz_mod_poly_init(self.val, self.ctx.mod.val) def __dealloc__(self): - fmpz_mod_poly_clear(self.val, self.ctx.val) + fmpz_mod_poly_clear(self.val, self.ctx.mod.val) def __init__(self, val, ctx): - if not typecheck(ctx, fmpz_mod_ctx): + if not typecheck(ctx, fmpz_mod_poly_ctx): raise TypeError self.ctx = ctx - - val = any_as_fmpz_poly(val) - if val is NotImplemented: - raise TypeError - - fmpz_mod_poly_set_fmpz_poly( - self.val, (val).val, self.ctx.val - ) + self.ctx.set_any_as_fmpz_mod_poly(self.val, val) def __getitem__(self, long i): cdef fmpz_mod x x = fmpz_mod.__new__(fmpz_mod) - x.ctx = self.ctx + x.ctx = self.ctx.mod if i < 0: return x - fmpz_mod_poly_get_coeff_fmpz(x.val, self.val, i, self.ctx.val) + fmpz_mod_poly_get_coeff_fmpz( + x.val, self.val, i, self.ctx.mod.val + ) return x def __setitem__(self, long i, x): if i < 0: raise ValueError("cannot assign to index < 0 of polynomial") - v = self.ctx.any_as_fmpz_mod(x) + v = self.ctx.mod.any_as_fmpz_mod(x) if v is NotImplemented: raise TypeError - fmpz_mod_poly_set_coeff_fmpz(self.val, i, (v).val, self.ctx.val) + fmpz_mod_poly_set_coeff_fmpz( + self.val, i, (v).val, self.ctx.mod.val + ) - def repr(self): - return "fmpz_mod_poly([%s])" % (", ".join(map(str, self.coeffs()))) + def __repr__(self): + return f"fmpz_mod_poly([{', '.join(map(str, self.coeffs()))}], {repr(self.ctx)})" def __len__(self): - return fmpz_mod_poly_length(self.val, self.ctx.val) + return fmpz_mod_poly_length(self.val, self.ctx.mod.val) cpdef long length(self): - return fmpz_mod_poly_length(self.val, self.ctx.val) + return fmpz_mod_poly_length(self.val, self.ctx.mod.val) cpdef long degree(self): - return fmpz_mod_poly_degree(self.val, self.ctx.val) + return fmpz_mod_poly_degree(self.val, self.ctx.mod.val) + + def is_zero(self): + return 0 != fmpz_mod_poly_is_zero(self.val, self.ctx.mod.val) + + def is_one(self): + return 0 != fmpz_mod_poly_is_one(self.val, self.ctx.mod.val) + + def is_gen(self): + return 0 != fmpz_mod_poly_is_gen(self.val, self.ctx.mod.val) + + def __richcmp__(self, other, int op): + cdef bint res + if op != 2 and op != 3: + raise TypeError("fmpz_mod_poly cannot be ordered") + + if not typecheck(other, fmpz_mod_poly): + other = self.ctx.any_as_fmpz_mod_poly(other) + + if typecheck(other, fmpz_mod_poly): + res = (self.ctx == (other).ctx) and \ + fmpz_mod_poly_equal(self.val, (other).val, self.ctx.mod.val) + if op == 2: + return res + else: + return not res + else: + return NotImplemented From e6b28d4f72c24e45eb3c9a7c30d1475adb362a1f Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 21 Sep 2023 17:13:52 +0100 Subject: [PATCH 04/35] Start work on factor; bug --- src/flint/test/test.py | 2 +- src/flint/types/fmpz_mod_poly.pyx | 47 ++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 2239eb09..b1494a83 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1864,7 +1864,7 @@ def test_fmpz_mod_poly(): # Printing f = R([5, 6, 7, 8]) assert str(f) == "8*x^3 + 7*x^2 + 6*x + 5" - assert repr(f) == "fmpz_mod_poly([5, 6, 7, 8], fmpz_mod_poly_ctx(11))" + # assert repr(f) == "fmpz_mod_poly([5, 6, 7, 8], fmpz_mod_poly_ctx(11))" # Get and Set tests f = R([5, 6, 7, 8]) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 6724ae77..1ad6f845 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,10 +1,14 @@ from cpython.list cimport PyList_GET_SIZE +from flint.flintlib.fmpz_mod cimport fmpz_mod_set_fmpz from flint.flintlib.fmpz_mod_poly cimport * +from flint.flintlib.fmpz_mod_poly_factor cimport * + from flint.flintlib.fmpz cimport( fmpz_equal, fmpz_set, fmpz_init, - fmpz_clear + fmpz_clear, + fmpz_set_si ) from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod @@ -196,9 +200,6 @@ cdef class fmpz_mod_poly(flint_poly): self.val, i, (v).val, self.ctx.mod.val ) - def __repr__(self): - return f"fmpz_mod_poly([{', '.join(map(str, self.coeffs()))}], {repr(self.ctx)})" - def __len__(self): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) @@ -234,3 +235,41 @@ cdef class fmpz_mod_poly(flint_poly): return not res else: return NotImplemented + + + def is_irreducible(self): + pass + + def is_squarefree(self): + pass + + def factor(self, algorithm=None): + """ + 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. + """ + cdef fmpz_mod_poly_factor_t fac + cdef int i + fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) + + if algorithm == None: + fmpz_mod_poly_factor(fac, self.val, self.ctx.mod.val) + elif algorithm == "cantor_zassenhaus": + fmpz_mod_poly_factor_cantor_zassenhaus(fac, self.val, self.ctx.mod.val) + elif algorithm == "kaltofen_shoup": + fmpz_mod_poly_factor_kaltofen_shoup(fac, self.val, self.ctx.mod.val) + elif algorithm == "berlekamp": + fmpz_mod_poly_factor_berlekamp(fac, self.val, self.ctx.mod.val) + else: + raise ValueError("unknown algorithm") + res = [0] * fac.num + + cdef fmpz_mod_poly u + for i in range(fac.num): + u = fmpz_mod_poly.__new__(fmpz_mod_poly) + u.ctx = self.ctx + fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) + exp = fac.exp[i] + res[i] = (u, exp) + return res From 4e6449081ed2da4e470f2452fb68a0cf41f81538 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 21 Sep 2023 17:47:01 +0100 Subject: [PATCH 05/35] Monic, Leading Coefficent and Factor --- src/flint/types/fmpz_mod_poly.pyx | 49 +++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 1ad6f845..9940d7d5 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -8,7 +8,8 @@ from flint.flintlib.fmpz cimport( fmpz_set, fmpz_init, fmpz_clear, - fmpz_set_si + fmpz_set_si, + fmpz_is_one ) from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod @@ -236,6 +237,46 @@ cdef class fmpz_mod_poly(flint_poly): else: return NotImplemented + def leading_coefficient(self): + """ + Return the leading coefficient of this polynomial. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.leading_coefficient() + fmpz_mod(3, 163) + """ + return self[self.degree()] + + def monic(self, check=True): + """ + Return this polynomial divided by its leading coefficient. + + If `check` is True, raises ValueError if the leading coefficient + is not invertible modulo N. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.monic() + x^2 + 55*x + 109 + """ + cdef fmpz_mod_poly res + cdef fmpz_t f + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + if not check: + fmpz_mod_poly_make_monic( + res.val, self.val, self.ctx.mod.val + ) + else: + fmpz_init(f) + fmpz_mod_poly_make_monic_f( + f, res.val, self.val, self.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError(f"Leading coefficient is not invertible") + res.ctx = self.ctx + return res def is_irreducible(self): pass @@ -248,6 +289,10 @@ cdef class fmpz_mod_poly(flint_poly): 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. + + TODO: docstrings + TODO: add a check that at least the leading term is + invertible to stop segfaults? """ cdef fmpz_mod_poly_factor_t fac cdef int i @@ -272,4 +317,4 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) - return res + return self.leading_coefficient(), res From 36724e39f02bf91fe95f33f9fca9ceaae2c74c80 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 21 Sep 2023 19:13:17 +0100 Subject: [PATCH 06/35] More progress, but badly unittested currently --- src/flint/types/fmpz_mod.pyx | 4 +- src/flint/types/fmpz_mod_poly.pyx | 245 +++++++++++++++++++++++++++++- 2 files changed, 243 insertions(+), 6 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index ca49bee0..6955b07c 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -67,7 +67,7 @@ cdef class fmpz_mod_ctx: Return the modulus from the context as an fmpz type - >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) + >>> mod_ctx = fmpz_mod_ctx(2**127 - 1) >>> mod_ctx.modulus() 170141183460469231731687303715884105727 @@ -130,7 +130,7 @@ cdef class fmpz_mod_ctx: return False def __hash__(self): - return hash(repr(self)) + return hash(self.modulus()) def __str__(self): return f"Context for fmpz_mod with modulus: {self.modulus()}" diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 9940d7d5..62ba90d4 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -52,6 +52,26 @@ cdef class fmpz_mod_poly_ctx: """ return self.mod.modulus() + def zero(self): + """ + TODO + """ + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self + fmpz_mod_poly_zero(res.val, self.mod.val) + return res + + def one(self): + """ + TODO + """ + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self + fmpz_mod_poly_one(res.val, self.mod.val) + return res + def gen(self): """ Return the generator of the polynomial `x` @@ -127,7 +147,7 @@ cdef class fmpz_mod_poly_ctx: cdef any_as_fmpz_mod_poly(self, obj): # Convert fmpz_mod_poly if typecheck(obj, fmpz_mod_poly): - if self.mod != (obj).ctx: + if self != (obj).ctx: raise ValueError("moduli must match") return obj @@ -153,7 +173,7 @@ cdef class fmpz_mod_poly_ctx: return False def __hash__(self): - return hash(repr(self)) + return hash(self.modulus()) def __str__(self): return f"Context for fmpz_mod_poly with modulus: {self.modulus()}" @@ -204,6 +224,139 @@ cdef class fmpz_mod_poly(flint_poly): def __len__(self): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) + def __hash__(self): + return hash(map(int, self.coeffs())) + + # ---------------- # + # Arithmetic # + # ---------------- # + + def __pos__(self): + return self + + def __neg__(self): + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_neg(res.val, self.val, self.ctx.mod.val) + return res + + def __add__(self, other): + cdef fmpz_mod_poly res + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + fmpz_mod_poly_add( + res.val, self.val, (other).val, self.ctx.mod.val + ) + res.ctx = self.ctx + return res + + def __radd__(self, other): + return self.__add__(other) + + @staticmethod + def _sub_(left, right): + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + + # Case when left and right are already fmpz_mod_poly + if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): + if not (left).ctx == (right).ctx: + raise ValueError("moduli must match") + + # Case when right is not fmpz_mod_poly, try to convert to fmpz + elif typecheck(left, fmpz_mod_poly): + right = (left).ctx.any_as_fmpz_mod_poly(right) + if right is NotImplemented: + return NotImplemented + + # Case when left is not fmpz_mod_poly, try to convert to fmpz + else: + left = (right).ctx.any_as_fmpz_mod_poly(left) + if left is NotImplemented: + return NotImplemented + + res.ctx = (left).ctx + fmpz_mod_poly_sub( + res.val, (left).val, (right).val, res.ctx.mod.val + ) + return res + + def __sub__(s, t): + return fmpz_mod_poly._sub_(s, t) + + def __rsub__(s, t): + return fmpz_mod_poly._sub_(t, s) + + def __mul__(self, other): + # TODO: + # Allow scalar multiplication for efficiency, rather + # than casting `other` to a polynomial? + cdef fmpz_mod_poly res + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + fmpz_mod_poly_mul( + res.val, self.val, (other).val, self.ctx.mod.val + ) + res.ctx = self.ctx + return res + + def __rmul__(self, other): + return self.__mul__(other) + + @staticmethod + def _div_(left, right): + # TODO: + # Allow scalar division for efficiency, rather + # than casting `other` to a polynomial? + + cdef bint check + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + + # Case when left and right are already fmpz_mod_poly + if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): + if not (left).ctx == (right).ctx: + raise ValueError("moduli must match") + + # Case when right is not fmpz_mod_poly, try to convert to fmpz + elif typecheck(left, fmpz_mod_poly): + right = (left).ctx.any_as_fmpz_mod_poly(right) + if right is NotImplemented: + return NotImplemented + + # Case when left is not fmpz_mod_poly, try to convert to fmpz + else: + left = (right).ctx.any_as_fmpz_mod_poly(left) + if left is NotImplemented: + return NotImplemented + + res.ctx = (left).ctx + check = fmpz_mod_poly_divides( + res.val, (left).val, (right).val, res.ctx.mod.val + ) + if check == 0: + raise ValueError( + f"{right} does not divide {left}" + ) + + return res + + def __truediv__(s, t): + return fmpz_mod_poly._div_(s, t) + + def __rtruediv__(s, t): + return fmpz_mod_poly._div_(t, s) + + def __floordiv__(self, other): + return NotImplemented + cpdef long length(self): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) @@ -211,12 +364,24 @@ cdef class fmpz_mod_poly(flint_poly): return fmpz_mod_poly_degree(self.val, self.ctx.mod.val) def is_zero(self): + """ + Return `True` if the polynomial is the zero polynomial + and `False` otherwise + """ return 0 != fmpz_mod_poly_is_zero(self.val, self.ctx.mod.val) def is_one(self): + """ + Return `True` if the polynomial is the zero polynomial + and `False` otherwise + """ return 0 != fmpz_mod_poly_is_one(self.val, self.ctx.mod.val) def is_gen(self): + """ + Return `True` if the polynomial is the zero polynomial + and `False` otherwise + """ return 0 != fmpz_mod_poly_is_gen(self.val, self.ctx.mod.val) def __richcmp__(self, other, int op): @@ -248,6 +413,27 @@ cdef class fmpz_mod_poly(flint_poly): """ return self[self.degree()] + def reverse(self, degree=None): + """ + TODO + """ + cdef fmpz_mod_poly res + cdef slong d + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + + if degree is not None: + d = degree + if d != degree or d < 0: + raise ValueError(f"degree argument must be a non-negative integer, got {degree}") + else: + d = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) + + length = d + 1 + fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) + return res + def monic(self, check=True): """ Return this polynomial divided by its leading coefficient. @@ -279,11 +465,61 @@ cdef class fmpz_mod_poly(flint_poly): return res def is_irreducible(self): - pass + """ + Return whether this polynomial is irreducible. + """ + return 1 == fmpz_mod_poly_is_irreducible(self.val, self.ctx.mod.val) def is_squarefree(self): - pass + """ + Return whether this polynomial is squarefree. + """ + return 1 == fmpz_mod_poly_is_squarefree(self.val, self.ctx.mod.val) + + def radical(self): + """ + Return the radical of self, the product of the irreducible + factors of the polynomial. This is also referred to as the + square-free part of the polynomial. + """ + res = self.ctx.one() + # TODO: this should be the squarefree leading coeff I guess... + res *= self.leading_coefficient() + _, factors = self.factor() + for p, _ in factors: + res *= p + return 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. + + TODO: docstrings + TODO: add a check that at least the leading term is + invertible to stop segfaults? + TODO: Other option is to only support prime moduli? + """ + cdef fmpz_mod_poly_factor_t fac + cdef int i + fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) + fmpz_mod_poly_factor_squarefree(fac, self.val, self.ctx.mod.val) + + res = [0] * fac.num + + cdef fmpz_mod_poly u + for i in range(fac.num): + u = fmpz_mod_poly.__new__(fmpz_mod_poly) + u.ctx = self.ctx + fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) + exp = fac.exp[i] + res[i] = (u, exp) + return self.leading_coefficient(), res + # TODO: we could make a factorisation class which we could then + # implement the factor methods such as pow and concat. I think + # sage does something like this with `Factorisation` classes. def factor(self, algorithm=None): """ Factors self into irreducible factors, returning a tuple @@ -293,6 +529,7 @@ cdef class fmpz_mod_poly(flint_poly): TODO: docstrings TODO: add a check that at least the leading term is invertible to stop segfaults? + TODO: Other option is to only support prime moduli? """ cdef fmpz_mod_poly_factor_t fac cdef int i From 0a848674f6452f98edc1e81c693777cae97d22c2 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 22 Sep 2023 11:36:01 +0100 Subject: [PATCH 07/35] Add additional bool to check if modulus is prime on init --- src/flint/types/fmpz_mod.pxd | 1 + src/flint/types/fmpz_mod.pyx | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd index 11b68928..99d4ad49 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -8,6 +8,7 @@ from flint.flintlib.fmpz_mod cimport ( cdef class fmpz_mod_ctx: cdef fmpz_mod_ctx_t val + cdef bint _is_prime cdef fmpz_mod_discrete_log_pohlig_hellman_t *L cdef any_as_fmpz_mod(self, obj) cdef _precompute_dlog_prime(self) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 6955b07c..fc7987b0 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -39,6 +39,7 @@ cdef class fmpz_mod_ctx: fmpz_one(one.val) fmpz_mod_ctx_init(self.val, one.val) self.L = NULL + self._is_prime = 0 def __dealloc__(self): @@ -62,6 +63,10 @@ cdef class fmpz_mod_ctx: # Set the modulus fmpz_mod_ctx_set_modulus(self.val, (mod).val) + # Check whether the modulus is prime + # TODO: should we use a stronger test? + self._is_prime = fmpz_is_probabprime(self.val.n) + def modulus(self): """ Return the modulus from the context as an fmpz @@ -76,6 +81,17 @@ cdef class fmpz_mod_ctx: fmpz_set(n.val, (self.val.n)) return n + def is_prime(self): + """ + Return whether the modulus is prime + + >>> fmpz_mod_ctx(2**127).is_prime() + False + >>> fmpz_mod_ctx(2**127 - 1).is_prime() + True + """ + return self._is_prime == 1 + cdef _precompute_dlog_prime(self): """ Initalise the dlog data, all discrete logs are solved with an @@ -252,7 +268,7 @@ cdef class fmpz_mod(flint_scalar): return res - def discrete_log(self, a, check=False): + def discrete_log(self, a): """ Solve the discrete logarithm problem, using `self = g` as a base. Assumes a solution, :math:`a = g^x \pmod p` exists. @@ -269,10 +285,8 @@ cdef class fmpz_mod(flint_scalar): cdef bint is_prime # Ensure that the modulus is prime - if check: - is_prime = fmpz_is_probabprime(self.ctx.val.n) - if not is_prime: - raise ValueError("modulus must be prime") + if not self.ctx.is_prime(): + raise NotImplementedError("algorithm assumes modulus is prime") # Then check the type of the input if typecheck(a, fmpz_mod): From 61229379cf8f3029952a2ea090016b72eb12ad1c Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 22 Sep 2023 11:36:22 +0100 Subject: [PATCH 08/35] Continue adding functions and docstrings --- src/flint/test/test.py | 13 +- src/flint/types/fmpz_mod_poly.pyx | 363 +++++++++++++++++++++++------- 2 files changed, 296 insertions(+), 80 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index b1494a83..927200a7 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1591,10 +1591,17 @@ def test_fmpz_mod(): p_med = 2**127 - 1 p_big = 2**255 - 19 + F_cmp = fmpz_mod_ctx(10) F_sml = fmpz_mod_ctx(p_sml) F_med = fmpz_mod_ctx(p_med) F_big = fmpz_mod_ctx(p_big) + assert F_sml.is_prime() is True + assert F_med.is_prime() is True + assert F_big.is_prime() is True + assert F_cmp.is_prime() is False + + # Context tests assert raises(lambda: fmpz_mod_ctx("AAA"), TypeError) assert raises(lambda: fmpz_mod_ctx(-1), ValueError) @@ -1776,17 +1783,17 @@ def test_fmpz_mod_dlog(): # Input modulus must be prime F = fmpz_mod_ctx(4) g, a = F(1), F(2) - assert raises(lambda: g.discrete_log(a, check=True), ValueError) + assert raises(lambda: g.discrete_log(a), NotImplementedError) # Moduli must match F1, F2 = fmpz_mod_ctx(2), fmpz_mod_ctx(3) g = F1(2) a = F2(4) - assert raises(lambda: g.discrete_log(a, check=True), ValueError) + assert raises(lambda: g.discrete_log(a), ValueError) # Need to use either fmpz_mod or something which can be case to # fmpz - assert raises(lambda: g.discrete_log("A", check=True), TypeError) + assert raises(lambda: g.discrete_log("A"), TypeError) F = fmpz_mod_ctx(163) g = F(2) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 62ba90d4..7cbb7c26 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,19 +1,15 @@ from cpython.list cimport PyList_GET_SIZE -from flint.flintlib.fmpz_mod cimport fmpz_mod_set_fmpz from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz_mod_poly_factor cimport * from flint.flintlib.fmpz cimport( - fmpz_equal, - fmpz_set, fmpz_init, fmpz_clear, - fmpz_set_si, fmpz_is_one ) from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod -from flint.types.fmpz_poly cimport any_as_fmpz_poly, fmpz_poly +from flint.types.fmpz_poly cimport fmpz_poly from flint.flint_base.flint_base cimport flint_poly from flint.utils.typecheck cimport typecheck @@ -52,9 +48,24 @@ cdef class fmpz_mod_poly_ctx: """ return self.mod.modulus() + def is_prime(self): + """ + Return whether the modulus is prime + + >>> fmpz_mod_poly_ctx(2**127).is_prime() + False + >>> fmpz_mod_poly_ctx(2**127 - 1).is_prime() + True + """ + return self.mod.is_prime() + def zero(self): """ - TODO + Return the zero element of this polynomial ring + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.zero() + 0 """ cdef fmpz_mod_poly res res = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -64,7 +75,11 @@ cdef class fmpz_mod_poly_ctx: def one(self): """ - TODO + Return the one element of this polynomial ring + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.one() + 1 """ cdef fmpz_mod_poly res res = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -76,11 +91,11 @@ cdef class fmpz_mod_poly_ctx: """ Return the generator of the polynomial `x` - >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) - >>> mod_ctx.gen() - fmpz_mod_poly([0, 1], fmpz_mod_poly_ctx(170141183460469231731687303715884105727)) - >>> str(mod_ctx.gen()) - 'x' + >>> R = fmpz_mod_poly_ctx(2**127 - 1) + >>> mod_ctx.gen() + fmpz_mod_poly([0, 1], fmpz_mod_poly_ctx(170141183460469231731687303715884105727)) + >>> str(mod_ctx.gen()) + 'x' """ return self.any_as_fmpz_mod_poly([0, 1]) @@ -200,33 +215,6 @@ cdef class fmpz_mod_poly(flint_poly): self.ctx = ctx self.ctx.set_any_as_fmpz_mod_poly(self.val, val) - def __getitem__(self, long i): - cdef fmpz_mod x - x = fmpz_mod.__new__(fmpz_mod) - x.ctx = self.ctx.mod - if i < 0: - return x - fmpz_mod_poly_get_coeff_fmpz( - x.val, self.val, i, self.ctx.mod.val - ) - return x - - def __setitem__(self, long i, x): - if i < 0: - raise ValueError("cannot assign to index < 0 of polynomial") - v = self.ctx.mod.any_as_fmpz_mod(x) - if v is NotImplemented: - raise TypeError - fmpz_mod_poly_set_coeff_fmpz( - self.val, i, (v).val, self.ctx.mod.val - ) - - def __len__(self): - return fmpz_mod_poly_length(self.val, self.ctx.mod.val) - - def __hash__(self): - return hash(map(int, self.coeffs())) - # ---------------- # # Arithmetic # # ---------------- # @@ -315,10 +303,8 @@ cdef class fmpz_mod_poly(flint_poly): # TODO: # Allow scalar division for efficiency, rather # than casting `other` to a polynomial? - cdef bint check cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) # Case when left and right are already fmpz_mod_poly if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): @@ -337,6 +323,7 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented + res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = (left).ctx check = fmpz_mod_poly_divides( res.val, (left).val, (right).val, res.ctx.mod.val @@ -356,7 +343,78 @@ cdef class fmpz_mod_poly(flint_poly): def __floordiv__(self, other): return NotImplemented - + + def __pow__(self, e): + cdef fmpz_mod_poly res + if e < 0: + raise ValueError("Exponent must be non-negative") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_pow( + res.val, self.val, (e), self.ctx.mod.val + ) + return res + + @staticmethod + def _mod_(s, t): + pass + + def __mod__(s, t): + return fmpz_mod_poly._mod_(s, t) + + def __rmod__(s, t): + return fmpz_mod_poly._mod_(t, s) + + # = + # Other Magic Methods + # = + + def __richcmp__(self, other, int op): + cdef bint res + if op != 2 and op != 3: + raise TypeError("fmpz_mod_poly cannot be ordered") + + if not typecheck(other, fmpz_mod_poly): + other = self.ctx.any_as_fmpz_mod_poly(other) + + if typecheck(other, fmpz_mod_poly): + res = (self.ctx == (other).ctx) and \ + fmpz_mod_poly_equal(self.val, (other).val, self.ctx.mod.val) + if op == 2: + return res + else: + return not res + else: + return NotImplemented + + def __getitem__(self, long i): + cdef fmpz_mod x + x = fmpz_mod.__new__(fmpz_mod) + x.ctx = self.ctx.mod + if i < 0: + return x + fmpz_mod_poly_get_coeff_fmpz( + x.val, self.val, i, self.ctx.mod.val + ) + return x + + def __setitem__(self, long i, x): + if i < 0: + raise ValueError("cannot assign to index < 0 of polynomial") + v = self.ctx.mod.any_as_fmpz_mod(x) + if v is NotImplemented: + raise TypeError + fmpz_mod_poly_set_coeff_fmpz( + self.val, i, (v).val, self.ctx.mod.val + ) + + def __len__(self): + return fmpz_mod_poly_length(self.val, self.ctx.mod.val) + + def __hash__(self): + return hash(map(int, self.coeffs())) + cpdef long length(self): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) @@ -367,6 +425,11 @@ cdef class fmpz_mod_poly(flint_poly): """ Return `True` if the polynomial is the zero polynomial and `False` otherwise + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R(0) + >>> f.is_zero() + True """ return 0 != fmpz_mod_poly_is_zero(self.val, self.ctx.mod.val) @@ -374,6 +437,11 @@ cdef class fmpz_mod_poly(flint_poly): """ Return `True` if the polynomial is the zero polynomial and `False` otherwise + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R(1) + >>> f.is_one() + True """ return 0 != fmpz_mod_poly_is_one(self.val, self.ctx.mod.val) @@ -381,26 +449,37 @@ cdef class fmpz_mod_poly(flint_poly): """ Return `True` if the polynomial is the zero polynomial and `False` otherwise + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([0,1]) + >>> f.is_gen() + True """ return 0 != fmpz_mod_poly_is_gen(self.val, self.ctx.mod.val) - def __richcmp__(self, other, int op): - cdef bint res - if op != 2 and op != 3: - raise TypeError("fmpz_mod_poly cannot be ordered") + def is_constant(self): + """ + Return True if this is a constant polynomial. - if not typecheck(other, fmpz_mod_poly): - other = self.ctx.any_as_fmpz_mod_poly(other) + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> x.is_constant() + False + >>> R(123).is_constant() + True + """ + return self.degree() <= 0 - if typecheck(other, fmpz_mod_poly): - res = (self.ctx == (other).ctx) and \ - fmpz_mod_poly_equal(self.val, (other).val, self.ctx.mod.val) - if op == 2: - return res - else: - return not res - else: - return NotImplemented + def constant_coefficient(self): + """ + Return the leading coefficient of this polynomial. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.leading_coefficient() + fmpz_mod(1, 163) + """ + return self[0] def leading_coefficient(self): """ @@ -415,7 +494,20 @@ cdef class fmpz_mod_poly(flint_poly): def reverse(self, degree=None): """ - TODO + Return a polynomial with the coefficients of this polynomial + reversed. + + If `degree` is not None, the output polynomial will be zero-padded + or truncated before being reversed. Note: degree must be non-negative. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3,4,5]) + >>> f.reverse() + x^4 + 2*x^3 + 3*x^2 + 4*x + 5 + >>> f.reverse(degree=1) + x + 2 + >>> f.reverse(degree=100) + x^100 + 2*x^99 + 3*x^98 + 4*x^97 + 5*x^96 """ cdef fmpz_mod_poly res cdef slong d @@ -467,28 +559,126 @@ cdef class fmpz_mod_poly(flint_poly): def is_irreducible(self): """ Return whether this polynomial is irreducible. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f = (x**2 + 5*x + 3) + >>> f.is_irreducible() + True + >>> f = (x**2 + x + 3) + >>> f.is_irreducible() + False """ return 1 == fmpz_mod_poly_is_irreducible(self.val, self.ctx.mod.val) def is_squarefree(self): """ Return whether this polynomial is squarefree. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = (x + 1)**2 * (x + 3) + >>> f.is_squarefree() + False + >>> f = (x + 1) * (x + 3) + >>> f.is_squarefree() + True + """ return 1 == fmpz_mod_poly_is_squarefree(self.val, self.ctx.mod.val) + def gcd(self, other): + """ + Return the greatest common divisor of self and other. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = x*(x + 1) + >>> f.gcd(x+1) + x + 1 + >>> f.gcd(x*x) + x + + """ + cdef fmpz_mod_poly res + + if not self.ctx.is_prime(): + raise NotImplementedError("gcd algorithm assumes that the modulus is prime") + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_gcd( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res + + def derivative(self): + """ + The formal derivative of this polynomial + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 111*x**4 + 58*x**3 + 98*x**2 + 117*x + 7 + >>> f.derivative() + 118*x^3 + 11*x^2 + 33*x + 117 + + """ + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_derivative( + res.val, self.val, self.ctx.mod.val + ) + return res + + def discriminant(self): + """ + Return the discriminant of self. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 6*x**4 + 7*x**3 + 7*x**2 + 8*x + 6 + >>> f.discriminant() + fmpz_mod(50, 163) + + """ + cdef fmpz_mod res + + if not self.ctx.is_prime(): + raise NotImplementedError("discriminant algorithm assumes that the base is a field") + + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx.mod + fmpz_mod_poly_discriminant( + res.val, self.val, self.ctx.mod.val + ) + return res + def radical(self): """ Return the radical of self, the product of the irreducible factors of the polynomial. This is also referred to as the square-free part of the polynomial. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = (x + 1)**3 * (x + 2) + >>> f.radical() + x^2 + 3*x + 2 + """ - res = self.ctx.one() - # TODO: this should be the squarefree leading coeff I guess... - res *= self.leading_coefficient() - _, factors = self.factor() - for p, _ in factors: - res *= p - return res + if not self.ctx.is_prime(): + raise NotImplementedError("radical algorithm assumes that the base is a field") + + return self / self.gcd(self.derivative()) + + # TODO: we could make a factorisation class which we could then + # implement the factor methods such as pow and concat. I think + # sage does something like this with `Factorisation` classes. def factor_squarefree(self): """ @@ -496,13 +686,21 @@ cdef class fmpz_mod_poly(flint_poly): (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. - TODO: docstrings - TODO: add a check that at least the leading term is - invertible to stop segfaults? - TODO: Other option is to only support prime moduli? + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = (x + 1) * (x + 2) + >>> f.factor_squarefree() + (fmpz_mod(1, 163), [(x^2 + 3*x + 2, 1)]) + >>> f = (x + 1) * (x + 2)**5 + >>> f.factor_squarefree() + (fmpz_mod(1, 163), [(x + 1, 1), (x + 2, 5)]) """ cdef fmpz_mod_poly_factor_t fac cdef int i + + if not self.ctx.is_prime(): + raise NotImplementedError("factor_squarefree algorithm assumes that the modulus is prime") + fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) fmpz_mod_poly_factor_squarefree(fac, self.val, self.ctx.mod.val) @@ -517,24 +715,28 @@ cdef class fmpz_mod_poly(flint_poly): res[i] = (u, exp) return self.leading_coefficient(), res - # TODO: we could make a factorisation class which we could then - # implement the factor methods such as pow and concat. I think - # sage does something like this with `Factorisation` classes. def factor(self, algorithm=None): """ 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. - TODO: docstrings - TODO: add a check that at least the leading term is - invertible to stop segfaults? - TODO: Other option is to only support prime moduli? + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 6*x**4 + 7*x**3 + 7*x**2 + 8*x + 6 + >>> f.factor() + (fmpz_mod(6, 163), [(x^4 + 137*x^3 + 137*x^2 + 110*x + 1, 1)]) + >>> f = (x + 1)**3 * (x + 2) + >>> f.factor() + (fmpz_mod(1, 163), [(x + 1, 3), (x + 2, 1)]) """ cdef fmpz_mod_poly_factor_t fac cdef int i - fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) + if not self.ctx.is_prime(): + raise NotImplementedError("factor algorithm assumes that the modulus is prime") + + fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) if algorithm == None: fmpz_mod_poly_factor(fac, self.val, self.ctx.mod.val) elif algorithm == "cantor_zassenhaus": @@ -545,6 +747,7 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_factor_berlekamp(fac, self.val, self.ctx.mod.val) else: raise ValueError("unknown algorithm") + res = [0] * fac.num cdef fmpz_mod_poly u @@ -555,3 +758,9 @@ cdef class fmpz_mod_poly(flint_poly): exp = fac.exp[i] res[i] = (u, exp) return self.leading_coefficient(), res + + def roots(self): + return NotImplemented + + def complex_roots(self): + return NotImplemented From 48270660fe3517535e53ef4b3b150827f3b3a533 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 22 Sep 2023 14:27:42 +0100 Subject: [PATCH 09/35] Address review, add random elements and further methods and docstrings --- src/flint/types/fmpz_mod.pyx | 47 ++++++++- src/flint/types/fmpz_mod_poly.pyx | 167 +++++++++++++++++++++++++++--- 2 files changed, 198 insertions(+), 16 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index fc7987b0..847afe40 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -1,6 +1,8 @@ +from flint.pyflint cimport global_random_state from flint.flintlib.fmpz cimport( fmpz_t, fmpz_one, + fmpz_zero, fmpz_set, fmpz_init, fmpz_clear, @@ -10,7 +12,8 @@ from flint.flintlib.fmpz cimport( fmpz_invmod, fmpz_divexact, fmpz_gcd, - fmpz_is_one + fmpz_is_one, + fmpz_randm ) from flint.flintlib.fmpz cimport fmpz_mod as fmpz_type_mod from flint.flintlib.fmpz_mod cimport * @@ -92,6 +95,48 @@ cdef class fmpz_mod_ctx: """ return self._is_prime == 1 + def zero(self): + """ + Return the zero element + + >>> F = fmpz_mod_ctx(163) + >>> F.zero() + fmpz_mod(0, 163) + """ + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + fmpz_zero(res.val) + res.ctx = self + + return res + + def one(self): + """ + Return the one element + + >>> F = fmpz_mod_ctx(163) + >>> F.one() + fmpz_mod(1, 163) + """ + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + fmpz_one(res.val) + res.ctx = self + + return res + + def random_element(self): + r""" + Return a random element in :math:`\mathbb{Z}/N\mathbb{Z}` + """ + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self + + fmpz_randm(res.val, global_random_state, self.val.n) + + return res + cdef _precompute_dlog_prime(self): """ Initalise the dlog data, all discrete logs are solved with an diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 7cbb7c26..85c4180d 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,10 +1,13 @@ from cpython.list cimport PyList_GET_SIZE +from flint.pyflint cimport global_random_state +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t, fmpz_mod_ctx_init, fmpz_mod_neg from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz_mod_poly_factor cimport * from flint.flintlib.fmpz cimport( fmpz_init, fmpz_clear, + fmpz_one, fmpz_is_one ) from flint.types.fmpz cimport fmpz, any_as_fmpz @@ -14,6 +17,13 @@ from flint.types.fmpz_poly cimport fmpz_poly from flint.flint_base.flint_base cimport flint_poly from flint.utils.typecheck cimport typecheck +# Global context with modulus one to make `__cinit__` happy within +# `fmpz_mod_poly` +cdef fmpz_t _fmpz_one +cdef fmpz_mod_ctx_t _fmpz_mod_one_ctx +fmpz_one(_fmpz_one) +fmpz_mod_ctx_init(_fmpz_mod_one_ctx, _fmpz_one) + cdef class fmpz_mod_poly_ctx: """ NOTE: @@ -41,8 +51,8 @@ cdef class fmpz_mod_poly_ctx: Return the modulus from the context as an fmpz type - >>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1) - >>> mod_ctx.modulus() + >>> R = fmpz_mod_poly_ctx(2**127 - 1) + >>> R.modulus() 170141183460469231731687303715884105727 """ @@ -91,14 +101,64 @@ cdef class fmpz_mod_poly_ctx: """ Return the generator of the polynomial `x` - >>> R = fmpz_mod_poly_ctx(2**127 - 1) - >>> mod_ctx.gen() - fmpz_mod_poly([0, 1], fmpz_mod_poly_ctx(170141183460469231731687303715884105727)) - >>> str(mod_ctx.gen()) - 'x' + >>> R = fmpz_mod_poly_ctx(163) + >>> R.gen() + x """ return self.any_as_fmpz_mod_poly([0, 1]) + def random_element(self, degree=3, monic=False, irreducible=False): + """ + Return a random element of degree `degree`. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R.random_element() + >>> f.degree() + 3 + >>> f = R.random_element(degree=123) + >>> f.degree() + 123 + >>> f = R.random_element(monic=True) + >>> f.is_monic() + True + >>> f = R.random_element(degree=13, monic=True, irreducible=True) + >>> f.degree() + 13 + >>> f.is_monic() + True + >>> f.is_irreducible() + True + """ + cdef slong length + if not (isinstance(monic, bool) and isinstance(irreducible, bool)): + raise ValueError("Both `monic` and `irreducible` must be of type bool") + + length = degree + 1 + if length <= 0: + raise ValueError("The degree argument must be non-negative") + + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self + if (monic and irreducible): + fmpz_mod_poly_randtest_monic_irreducible( + res.val, global_random_state, length, self.mod.val + ) + elif monic: + fmpz_mod_poly_randtest_monic( + res.val, global_random_state, length, self.mod.val + ) + elif irreducible: + fmpz_mod_poly_randtest_irreducible( + res.val, global_random_state, length, self.mod.val + ) + else: + fmpz_mod_poly_randtest( + res.val, global_random_state, length, self.mod.val + ) + return res + + cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val): cdef long i, n cdef fmpz_t x @@ -204,7 +264,7 @@ cdef class fmpz_mod_poly(flint_poly): """ """ def __cinit__(self): - fmpz_mod_poly_init(self.val, self.ctx.mod.val) + fmpz_mod_poly_init(self.val, _fmpz_mod_one_ctx) def __dealloc__(self): fmpz_mod_poly_clear(self.val, self.ctx.mod.val) @@ -356,6 +416,13 @@ cdef class fmpz_mod_poly(flint_poly): ) return res + def __lshift__(self, n): + pass + + def __rshift__(self, n): + pass + + @staticmethod def _mod_(s, t): pass @@ -415,6 +482,18 @@ cdef class fmpz_mod_poly(flint_poly): def __hash__(self): return hash(map(int, self.coeffs())) + def __call__(self, val): + cdef fmpz_mod res + + val = self.ctx.mod.any_as_fmpz_mod(val) + if val is NotImplemented: + return val + + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx.mod + fmpz_mod_poly_evaluate_fmpz(res.val, self.val, (val).val, self.ctx.mod.val) + return res + cpdef long length(self): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) @@ -476,7 +555,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) - >>> f.leading_coefficient() + >>> f.constant_coefficient() fmpz_mod(1, 163) """ return self[0] @@ -526,6 +605,9 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res + def shift(self, n): + pass + def monic(self, check=True): """ Return this polynomial divided by its leading coefficient. @@ -556,16 +638,31 @@ cdef class fmpz_mod_poly(flint_poly): res.ctx = self.ctx return res + def is_monic(self): + """ + Return whether this polynomial is monic. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = x**2 + 5*x + 3 + >>> f.is_monic() + True + >>> f = 5*x**2 + x + 3 + >>> f.is_monic() + False + """ + return self.leading_coefficient().is_one() + def is_irreducible(self): """ Return whether this polynomial is irreducible. >>> R = fmpz_mod_poly_ctx(163) - >>> f = R([1,2,3]) - >>> f = (x**2 + 5*x + 3) + >>> x = R.gen() + >>> f = x**2 + 5*x + 3 >>> f.is_irreducible() True - >>> f = (x**2 + x + 3) + >>> f = x**2 + x + 3 >>> f.is_irreducible() False """ @@ -759,8 +856,48 @@ cdef class fmpz_mod_poly(flint_poly): res[i] = (u, exp) return self.leading_coefficient(), res - def roots(self): - return NotImplemented + def roots(self, multiplicities=True): + """ + Return the roots of the polynomial in (Z/NZ)* + Requires that the modulus N is prime. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = (x - 1) * (x - 2)**3 * (x - 3)**5 + >>> f.roots() + [(fmpz_mod(1, 163), 1), (fmpz_mod(2, 163), 3), (fmpz_mod(3, 163), 5)] + >>> f.roots(multiplicities=False) + [fmpz_mod(1, 163), fmpz_mod(2, 163), fmpz_mod(3, 163)] + """ + cdef fmpz_mod_poly_factor_t fac + cdef int i, with_multiplicity + + if multiplicities: + with_multiplicity = 1 + + if not self.ctx.is_prime(): + raise NotImplementedError("factor algorithm assumes that the modulus is prime") + + fmpz_mod_poly_factor_init(fac, self.ctx.mod.val) + fmpz_mod_poly_roots(fac, self.val, with_multiplicity, self.ctx.mod.val) + res = [0] * fac.num + + cdef fmpz_mod root + for i in range(fac.num): + root = fmpz_mod.__new__(fmpz_mod) + root.ctx = self.ctx.mod + fmpz_mod_poly_get_coeff_fmpz( + root.val, &fac.poly[i], 0, self.ctx.mod.val + ) + fmpz_mod_neg( + root.val, root.val, root.ctx.val + ) + if multiplicities: + mul = fac.exp[i] + res[i] = (root, mul) + else: + res[i] = root + return res def complex_roots(self): - return NotImplemented + return NotImplementedError("the method `complex_roots` is not yet implemented for fmpz_mod_poly") From 00b87f5b1e447101fa17eecb9ad7ac104d6e82b7 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 22 Sep 2023 18:20:01 +0100 Subject: [PATCH 10/35] Add more functions to fmpz_mod_poly, also add is_unit to fmpz_mod --- src/flint/flintlib/fmpz_mod_poly.pxd | 1 + src/flint/types/fmpz_mod.pyx | 16 +++ src/flint/types/fmpz_mod_poly.pxd | 1 + src/flint/types/fmpz_mod_poly.pyx | 193 ++++++++++++++++++++++----- 4 files changed, 177 insertions(+), 34 deletions(-) diff --git a/src/flint/flintlib/fmpz_mod_poly.pxd b/src/flint/flintlib/fmpz_mod_poly.pxd index 227beb00..d241e0df 100644 --- a/src/flint/flintlib/fmpz_mod_poly.pxd +++ b/src/flint/flintlib/fmpz_mod_poly.pxd @@ -211,6 +211,7 @@ cdef extern from "flint/fmpz_mod_poly.h": void fmpz_mod_poly_divrem_f(fmpz_t f, fmpz_mod_poly_t Q, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_rem(fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) void _fmpz_mod_poly_rem_f(fmpz_t f, fmpz_struct *R, const fmpz_struct *A, slong lenA, const fmpz_struct *B, slong lenB, const fmpz_t invB, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_rem_f(fmpz_t f, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) void fmpz_mod_poly_rem(fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) # Divisibility testing diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 847afe40..04b26979 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -278,6 +278,22 @@ cdef class fmpz_mod(flint_scalar): res = fmpz_mod_is_one(self.val, self.ctx.val) return res == 1 + def is_unit(self): + """ + Returns whether the element is invertible modulo `N` + + >>> from flint import * + >>> F = fmpz_mod_ctx(10) + >>> F(3).is_unit() + True + >>> F(2).is_unit() + False + """ + cdef fmpz_t g + fmpz_init(g) + fmpz_gcd(g, self.val, self.ctx.val.n) + return 1 == fmpz_is_one(g) + def inverse(self, check=True): r""" Computes :math:`a^{-1} \pmod N` diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index f2f755e8..4239beb2 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -11,6 +11,7 @@ cdef class fmpz_mod_poly_ctx: cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val) cdef class fmpz_mod_poly(flint_poly): + cdef bint initialized cdef fmpz_mod_poly_t val cdef fmpz_mod_poly_ctx ctx cpdef long length(self) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 85c4180d..f219e7bc 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,13 +1,12 @@ from cpython.list cimport PyList_GET_SIZE from flint.pyflint cimport global_random_state -from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t, fmpz_mod_ctx_init, fmpz_mod_neg +from flint.flintlib.fmpz_mod cimport fmpz_mod_neg from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz_mod_poly_factor cimport * from flint.flintlib.fmpz cimport( fmpz_init, fmpz_clear, - fmpz_one, fmpz_is_one ) from flint.types.fmpz cimport fmpz, any_as_fmpz @@ -17,13 +16,6 @@ from flint.types.fmpz_poly cimport fmpz_poly from flint.flint_base.flint_base cimport flint_poly from flint.utils.typecheck cimport typecheck -# Global context with modulus one to make `__cinit__` happy within -# `fmpz_mod_poly` -cdef fmpz_t _fmpz_one -cdef fmpz_mod_ctx_t _fmpz_mod_one_ctx -fmpz_one(_fmpz_one) -fmpz_mod_ctx_init(_fmpz_mod_one_ctx, _fmpz_one) - cdef class fmpz_mod_poly_ctx: """ NOTE: @@ -264,16 +256,18 @@ cdef class fmpz_mod_poly(flint_poly): """ """ def __cinit__(self): - fmpz_mod_poly_init(self.val, _fmpz_mod_one_ctx) + self.initialized = False def __dealloc__(self): - fmpz_mod_poly_clear(self.val, self.ctx.mod.val) + if self.initialized: + fmpz_mod_poly_clear(self.val, self.ctx.mod.val) def __init__(self, val, ctx): if not typecheck(ctx, fmpz_mod_poly_ctx): raise TypeError self.ctx = ctx self.ctx.set_any_as_fmpz_mod_poly(self.val, val) + self.initialized = True # ---------------- # # Arithmetic # @@ -401,8 +395,42 @@ cdef class fmpz_mod_poly(flint_poly): def __rtruediv__(s, t): return fmpz_mod_poly._div_(t, s) + @staticmethod + def _floordiv_(left, right): + cdef fmpz_mod_poly res + + # Case when left and right are already fmpz_mod_poly + if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): + if not (left).ctx == (right).ctx: + raise ValueError("moduli must match") + + # Case when right is not fmpz_mod_poly, try to convert to fmpz + elif typecheck(left, fmpz_mod_poly): + right = (left).ctx.any_as_fmpz_mod_poly(right) + if right is NotImplemented: + return NotImplemented + + # Case when left is not fmpz_mod_poly, try to convert to fmpz + else: + left = (right).ctx.any_as_fmpz_mod_poly(left) + if left is NotImplemented: + return NotImplemented + + if not right.leading_coefficient().is_unit(): + raise ValueError(f"The leading term of {right} must be a unit modulo N") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = (left).ctx + fmpz_mod_poly_div( + res.val, (left).val, (right).val, res.ctx.mod.val + ) + return res + def __floordiv__(self, other): - return NotImplemented + return fmpz_mod_poly._floordiv_(self, other) + + def __rfloordiv__(self, other): + return fmpz_mod_poly._floordiv_(other, self) def __pow__(self, e): cdef fmpz_mod_poly res @@ -416,16 +444,85 @@ cdef class fmpz_mod_poly(flint_poly): ) return res + def shift(self, slong n): + """ + Returns `self` shifted by `n` coefficients. If `n` is positive, + zero coefficients are inserted, when `n` is negative, if `n` is + greater than or equal to the length of `self`, the zero polynomial + is returned. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.shift(0) + 3*x^2 + 2*x + 1 + >>> f.shift(1) + 3*x^3 + 2*x^2 + x + >>> f.shift(4) + 3*x^6 + 2*x^5 + x^4 + >>> f.shift(-1) + 3*x + 2 + >>> f.shift(-4) + 0 + + """ + if n == 0: + return self + + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + + if n > 0: + fmpz_mod_poly_shift_left( + res.val, self.val, n, self.ctx.mod.val + ) + elif n < 0: + fmpz_mod_poly_shift_right( + res.val, self.val, -n, self.ctx.mod.val + ) + + return res + def __lshift__(self, n): - pass + return self.shift(n) def __rshift__(self, n): - pass - + return self.shift(-n) @staticmethod - def _mod_(s, t): - pass + def _mod_(left, right): + cdef fmpz_t f + cdef fmpz_mod_poly res + + # Case when left and right are already fmpz_mod_poly + if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): + if not (left).ctx == (right).ctx: + raise ValueError("moduli must match") + + # Case when right is not fmpz_mod_poly, try to convert to fmpz + elif typecheck(left, fmpz_mod_poly): + right = (left).ctx.any_as_fmpz_mod_poly(right) + if right is NotImplemented: + return NotImplemented + + # Case when left is not fmpz_mod_poly, try to convert to fmpz + else: + left = (right).ctx.any_as_fmpz_mod_poly(left) + if left is NotImplemented: + return NotImplemented + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = (left).ctx + fmpz_init(f) + fmpz_mod_poly_rem_f( + f, res.val, (left).val, (right).val, res.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError( + f"Cannot compute remainder of {left} modulo {right}" + ) + + return res def __mod__(s, t): return fmpz_mod_poly._mod_(s, t) @@ -605,8 +702,20 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res - def shift(self, n): - pass + def is_monic(self): + """ + Return whether this polynomial is monic. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = x**2 + 5*x + 3 + >>> f.is_monic() + True + >>> f = 5*x**2 + x + 3 + >>> f.is_monic() + False + """ + return self.leading_coefficient().is_one() def monic(self, check=True): """ @@ -638,21 +747,6 @@ cdef class fmpz_mod_poly(flint_poly): res.ctx = self.ctx return res - def is_monic(self): - """ - Return whether this polynomial is monic. - - >>> R = fmpz_mod_poly_ctx(163) - >>> x = R.gen() - >>> f = x**2 + 5*x + 3 - >>> f.is_monic() - True - >>> f = 5*x**2 + x + 3 - >>> f.is_monic() - False - """ - return self.leading_coefficient().is_one() - def is_irreducible(self): """ Return whether this polynomial is irreducible. @@ -684,6 +778,37 @@ cdef class fmpz_mod_poly(flint_poly): """ return 1 == fmpz_mod_poly_is_squarefree(self.val, self.ctx.mod.val) + def divrem(self, other): + """ + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([123, 129, 63, 14, 51, 76, 133]) + >>> g = R([106, 134, 32, 41, 158, 115, 115]) + >>> f.divrem(g) + (21, 106*x^5 + 156*x^4 + 131*x^3 + 43*x^2 + 86*x + 16) + """ + cdef fmpz_t f + cdef fmpz_mod_poly Q, R + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return NotImplemented + + Q = fmpz_mod_poly.__new__(fmpz_mod_poly) + R = fmpz_mod_poly.__new__(fmpz_mod_poly) + Q.ctx = self.ctx + R.ctx = self.ctx + + fmpz_init(f) + fmpz_mod_poly_divrem_f( + f, Q.val, R.val, self.val, (other).val, self.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError( + f"Cannot compute divrem of {self} with {other}" + ) + + return Q, R + def gcd(self, other): """ Return the greatest common divisor of self and other. From a3fec0810ca01d25d9cbdb1764aaa12f2642c0c3 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Tue, 26 Sep 2023 18:03:13 +0100 Subject: [PATCH 11/35] Add a few more functions, coverage currently at 22% --- src/flint/types/fmpz_mod_poly.pyx | 272 +++++++++++++++++++++++++++--- 1 file changed, 252 insertions(+), 20 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index f219e7bc..af23bc14 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -97,11 +97,17 @@ cdef class fmpz_mod_poly_ctx: >>> R.gen() x """ - return self.any_as_fmpz_mod_poly([0, 1]) + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self + fmpz_mod_poly_set_coeff_ui(res.val, 1, 1, self.mod.val) + return res def random_element(self, degree=3, monic=False, irreducible=False): """ - Return a random element of degree `degree`. + Return a random element of degree `degree`. If `monic` is True, + ensures the output is monic. If `irreducible` is True, ensures + that the output is irreducible. >>> R = fmpz_mod_poly_ctx(163) >>> f = R.random_element() @@ -269,10 +275,6 @@ cdef class fmpz_mod_poly(flint_poly): self.ctx.set_any_as_fmpz_mod_poly(self.val, val) self.initialized = True - # ---------------- # - # Arithmetic # - # ---------------- # - def __pos__(self): return self @@ -530,10 +532,6 @@ cdef class fmpz_mod_poly(flint_poly): def __rmod__(s, t): return fmpz_mod_poly._mod_(t, s) - # = - # Other Magic Methods - # = - def __richcmp__(self, other, int op): cdef bint res if op != 2 and op != 3: @@ -687,10 +685,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res cdef slong d - - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx - + if degree is not None: d = degree if d != degree or d < 0: @@ -699,9 +694,50 @@ cdef class fmpz_mod_poly(flint_poly): d = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) length = d + 1 + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res + def truncate(self, slong n): + r""" + Notionally truncate the polynomial to have length `n`. If + `n` is larger than the length of the input, then `self` is + returned. If n is not positive, then the zero polynomial + is returned. + + Effectively returns this polynomial :math:`\mod x^n`. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.truncate(3) is f + True + >>> f.truncate(2) + 2*x + 1 + >>> f.truncate(1) + 1 + >>> f.truncate(0) + 0 + >>> f.truncate(-1) + 0 + + """ + cdef fmpz_mod_poly res + + length = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) + if n > length: + return self + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + + if n <= 0: + return res + + fmpz_mod_poly_set_trunc(res.val, self.val, n, self.ctx.mod.val) + return res + def is_monic(self): """ Return whether this polynomial is monic. @@ -778,13 +814,86 @@ cdef class fmpz_mod_poly(flint_poly): """ return 1 == fmpz_mod_poly_is_squarefree(self.val, self.ctx.mod.val) + def square(self): + """ + Returns the square of `self` + """ + cdef fmpz_mod_poly res + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_sqr( + res.val, self.val, self.ctx.mod.val + ) + return res + + def mul_mod(self, other, modulus): + """ + Computes the multiplication of `self` with `other` + modulo the polynomial `modulus` + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 30*x**6 + 104*x**5 + 76*x**4 + 33*x**3 + 70*x**2 + 44*x + 65 + >>> g = 43*x**6 + 91*x**5 + 77*x**4 + 113*x**3 + 71*x**2 + 132*x + 60 + >>> mod = x**4 + 93*x**3 + 78*x**2 + 72*x + 149 + >>> + >>> f.mul_mod(g, mod) + 106*x^3 + 44*x^2 + 53*x + 77 + """ + cdef fmpz_mod_poly res + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + modulus = self.ctx.any_as_fmpz_mod_poly(modulus) + if modulus is NotImplemented: + return modulus + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + + fmpz_mod_poly_mulmod( + res.val, self.val, (other).val, (modulus).val, res.ctx.mod.val + ) + return res + + def pow_mod(self, e, modulus): + """ + Returns `self` raised to the power `e` modulo `modulus` + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 30*x**6 + 104*x**5 + 76*x**4 + 33*x**3 + 70*x**2 + 44*x + 65 + >>> g = 43*x**6 + 91*x**5 + 77*x**4 + 113*x**3 + 71*x**2 + 132*x + 60 + >>> mod = x**4 + 93*x**3 + 78*x**2 + 72*x + 149 + >>> + >>> f.pow_mod(123, mod) + 3*x^3 + 25*x^2 + 115*x + 161 + """ + cdef fmpz_mod_poly res + + modulus = self.ctx.any_as_fmpz_mod_poly(modulus) + if modulus is NotImplemented: + return modulus + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_powmod_ui_binexp( + res.val, self.val, e, (modulus).val, res.ctx.mod.val + ) + return res + def divrem(self, other): """ - >>> R = fmpz_mod_poly_ctx(163) - >>> f = R([123, 129, 63, 14, 51, 76, 133]) - >>> g = R([106, 134, 32, 41, 158, 115, 115]) - >>> f.divrem(g) - (21, 106*x^5 + 156*x^4 + 131*x^3 + 43*x^2 + 86*x + 16) + Return Q, R such that self = Q*other + R + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([123, 129, 63, 14, 51, 76, 133]) + >>> g = R([106, 134, 32, 41, 158, 115, 115]) + >>> f.divrem(g) + (21, 106*x^5 + 156*x^4 + 131*x^3 + 43*x^2 + 86*x + 16) """ cdef fmpz_t f cdef fmpz_mod_poly Q, R @@ -838,6 +947,44 @@ cdef class fmpz_mod_poly(flint_poly): ) return res + def xgcd(self, other): + """ + Computes the extended gcd of self and other: (G, S, T) + where G is the gcd(self, other) and S, T are such that: + + G = self*S + other*T + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([143, 19, 37, 138, 102, 127, 95]) + >>> g = R([139, 9, 35, 154, 87, 120, 24]) + >>> f.xgcd(g) + + """ + cdef fmpz_mod_poly G, S, T + cdef fmpz_t f + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + G = fmpz_mod_poly.__new__(fmpz_mod_poly) + S = fmpz_mod_poly.__new__(fmpz_mod_poly) + T = fmpz_mod_poly.__new__(fmpz_mod_poly) + + G.ctx = self.ctx + S.ctx = self.ctx + T.ctx = self.ctx + + fmpz_init(f) + fmpz_mod_poly_xgcd_f( + f, G.val, S.val, T.val, self.val, (other).val, self.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError( + f"Cannot compute xgcd of {self} with {other}" + ) + return (G, S, T) + def derivative(self): """ The formal derivative of this polynomial @@ -896,7 +1043,86 @@ cdef class fmpz_mod_poly(flint_poly): if not self.ctx.is_prime(): raise NotImplementedError("radical algorithm assumes that the base is a field") - return self / self.gcd(self.derivative()) + return self / self.gcd(self.derivative()) + + def inverse_mod(self, other): + """ + Returns the inverse of self modulo other + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = f = R([123, 129, 63, 14, 51, 76, 133]) + >>> f = R([123, 129, 63, 14, 51, 76, 133]) + >>> g = R([139, 9, 35, 154, 87, 120, 24]) + >>> f.inverse_mod(g) + 41*x^5 + 121*x^4 + 47*x^3 + 41*x^2 + 6*x + 5 + """ + cdef fmpz_mod_poly res + cdef fmpz_t f + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_init(f) + fmpz_mod_poly_invmod_f( + f, res.val, self.val, (other).val, res.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError( + f"Cannot compute inverse series of {self} modulo {other}" + ) + return res + + def inverse_series_trunc(self, slong n): + """ + Returns the inverse of self modulo x^n. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([123, 129, 63, 14, 51, 76, 133]) + >>> f.inverse_series(3) + 159*x^2 + 151*x + 110 + >>> f.inverse_series(4) + 23*x^3 + 159*x^2 + 151*x + 110 + >>> f.inverse_series(5) + 45*x^4 + 23*x^3 + 159*x^2 + 151*x + 110 + """ + cdef fmpz_t f + cdef fmpz_mod_poly res + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_init(f) + fmpz_mod_poly_inv_series_f( + f, res.val, self.val, n, res.ctx.mod.val + ) + if not fmpz_is_one(f): + raise ValueError( + f"Cannot compute inverse series of {self} modulo x^{n}" + ) + return res + + def resultant(self): + pass + + def evaluate(self): + pass + + def multipoint_evaluate(self): + pass + + def square_root(self): + pass + + def inverse_square_root(self): + pass + + def inflate(self): + pass + + def deflate(self): + pass # TODO: we could make a factorisation class which we could then # implement the factor methods such as pow and concat. I think @@ -1026,3 +1252,9 @@ cdef class fmpz_mod_poly(flint_poly): def complex_roots(self): return NotImplementedError("the method `complex_roots` is not yet implemented for fmpz_mod_poly") + + def berlekamp_massey(self): + raise NotImplemented + + def radix_conversion(self): + raise NotImplemented From 586307ae992fd28b364c413c49e586f2954b5903 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Tue, 26 Sep 2023 23:05:32 +0100 Subject: [PATCH 12/35] include new types to docstring tests --- src/flint/test/__main__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 542d6739..517eb15c 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -56,6 +56,8 @@ def run_doctests(verbose=None): flint.types.fmpz_poly, flint.types.fmpz_mat, flint.types.fmpz_series, + flint.types.fmpz_mod, + flint.types.fmpz_mod_poly, flint.types.fmpq, flint.types.fmpq_poly, flint.types.fmpq_mat, From b2e2894b6c5607b4773edcd3f3a1d6b91dfc36aa Mon Sep 17 00:00:00 2001 From: giacomopope Date: Tue, 26 Sep 2023 23:37:41 +0100 Subject: [PATCH 13/35] Ensure 100% coverage of fmpz_mod, fix docstring test bugs, fmpz_mod_poly at 70% coverage --- src/flint/test/test.py | 10 ++++++++++ src/flint/types/fmpz_mod.pyx | 32 +++++++++++++------------------ src/flint/types/fmpz_mod_poly.pyx | 10 ++++++---- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 927200a7..5e0955b8 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1629,6 +1629,13 @@ def test_fmpz_mod(): test_x = (-123) % test_mod # canonical value test_y = ((-456) % test_mod)**2 # non-canoncial value + test_z = F_test.random_element() + assert int(test_z) < F_test.modulus() + assert int(test_z) >= 0 + + assert raises(lambda: F_test(F_cmp(1)), ValueError) + assert raises(lambda: F_test("abc"), NotImplementedError) + F_test_copy = fmpz_mod_ctx(test_mod) F_other = fmpz_mod_ctx(11) @@ -1647,6 +1654,7 @@ def test_fmpz_mod(): assert (F_test(test_x) == F_test(test_x + test_mod)) is True assert (F_test(test_x) == F_test(1)) is False assert (F_test(test_x) != F_test(1)) is True + assert (F_test(test_x) != "abc") is True assert (hash(F_test(test_x)) == hash(test_x)) is True assert (hash(F_test(F_test(test_x))) == hash(test_x)) is True @@ -1704,6 +1712,8 @@ def test_fmpz_mod(): assert F_test(test_x) - fmpz(test_y) == F_test(test_x) - F_test(test_y) assert raises(lambda: F_test(test_x) - F_other(test_y), ValueError) assert raises(lambda: F_test(test_x) - "AAA", TypeError) + assert raises(lambda: "AAA" - F_test(test_x), TypeError) + # Multiplication diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 04b26979..40c9ee1e 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -153,7 +153,6 @@ cdef class fmpz_mod_ctx: cdef any_as_fmpz_mod(self, obj): # If `obj` is an `fmpz_mod`, just check moduli # match - # TODO: we could allow conversion from one modulus to another? if typecheck(obj, fmpz_mod): if self != (obj).ctx: raise ValueError("moduli must match") @@ -270,7 +269,7 @@ cdef class fmpz_mod(flint_scalar): >>> mod_ctx = fmpz_mod_ctx(163) >>> mod_ctx(0).is_one() False - >>> mod_ctx(1).is_zero() + >>> mod_ctx(1).is_one() True """ @@ -350,13 +349,9 @@ cdef class fmpz_mod(flint_scalar): raise NotImplementedError("algorithm assumes modulus is prime") # Then check the type of the input - if typecheck(a, fmpz_mod): - if self.ctx != (a).ctx: - raise ValueError("moduli must match") - else: - a = self.ctx.any_as_fmpz_mod(a) - if a is NotImplemented: - raise TypeError + a = self.ctx.any_as_fmpz_mod(a) + if a is NotImplemented: + raise TypeError(f"Cannot solve the discrete log with {type(a)} as input") # First, Ensure that self.ctx.L has performed precomputations # This generates a `y` which is a primative root, and used as @@ -417,18 +412,17 @@ cdef class fmpz_mod(flint_scalar): if op != 2 and op != 3: raise TypeError("fmpz_mod cannot be ordered") - if not typecheck(other, fmpz_mod): - other = self.ctx.any_as_fmpz_mod(other) + other = self.ctx.any_as_fmpz_mod(other) + if other is NotImplemented: + return NotImplemented - if typecheck(other, fmpz_mod): - res = fmpz_equal(self.val, (other).val) and \ - (self.ctx == (other).ctx) - if op == 2: - return res - else: - return not res + res = fmpz_equal(self.val, (other).val) and \ + (self.ctx == (other).ctx) + if op == 2: + return res else: - return NotImplemented + return not res + def __bool__(self): return not self.is_zero() diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index af23bc14..2f29f47f 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -758,7 +758,8 @@ cdef class fmpz_mod_poly(flint_poly): Return this polynomial divided by its leading coefficient. If `check` is True, raises ValueError if the leading coefficient - is not invertible modulo N. + is not invertible modulo N. If `check` is False and the leading + coefficient is not invertible, the output is undefined. >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) @@ -958,6 +959,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> f = R([143, 19, 37, 138, 102, 127, 95]) >>> g = R([139, 9, 35, 154, 87, 120, 24]) >>> f.xgcd(g) + (x^3 + 128*x^2 + 123*x + 91, 17*x^2 + 49*x + 104, 21*x^2 + 5*x + 25) """ cdef fmpz_mod_poly G, S, T @@ -1081,11 +1083,11 @@ cdef class fmpz_mod_poly(flint_poly): >>> R = fmpz_mod_poly_ctx(163) >>> f = R([123, 129, 63, 14, 51, 76, 133]) - >>> f.inverse_series(3) + >>> f.inverse_series_trunc(3) 159*x^2 + 151*x + 110 - >>> f.inverse_series(4) + >>> f.inverse_series_trunc(4) 23*x^3 + 159*x^2 + 151*x + 110 - >>> f.inverse_series(5) + >>> f.inverse_series_trunc(5) 45*x^4 + 23*x^3 + 159*x^2 + 151*x + 110 """ cdef fmpz_t f From b060b234677b674dd62fb633c3ab45f35da49098 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 13:59:06 +0100 Subject: [PATCH 14/35] Add base class docstrings --- src/flint/flint_base/flint_base.pyx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 4e33ae6e..e2a67511 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -27,6 +27,14 @@ cdef class flint_poly(flint_elem): yield self[i] def coeffs(self): + """ + Returns the coefficients of ``self`` as a list + + >>> from flint import fmpz_poly + >>> f = fmpz_poly([1,2,3,4,5]) + >>> f.coeffs() + [1,2,3,4,5] + """ return list(self) def str(self, bint ascending=False): From c30ad7afdd87fa53d039831ca3fe4982b21fac93 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 13:59:29 +0100 Subject: [PATCH 15/35] Improve docstrings and get coverage to 100% --- src/flint/test/test.py | 243 +++++++++++++++++++++++++++++- src/flint/types/fmpz_mod_poly.pyx | 175 ++++++++++++--------- 2 files changed, 346 insertions(+), 72 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 5e0955b8..7bb4dcee 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1831,7 +1831,7 @@ def test_fmpz_mod_dlog(): assert g**x == a def test_fmpz_mod_poly(): - from flint import fmpz_poly, fmpz_mod_poly_ctx, fmpz_mod_ctx, fmpz + from flint import fmpz_poly, fmpz_mod_poly, fmpz_mod_poly_ctx, fmpz_mod_ctx, fmpz # fmpz_mod_poly_ctx tests F = fmpz_mod_ctx(11) @@ -1855,21 +1855,53 @@ def test_fmpz_mod_poly(): assert repr(R3) == "fmpz_mod_poly_ctx(13)" assert R1.modulus() == 11 - assert R1([0,1]) == R1.gen() + + assert R1.is_prime() + assert R1.zero() == 0 + assert R1.one() == 1 + assert R1.gen() == R1([0,1]) + + # Random testing + f = R1.random_element() + assert f.degree() == 3 + f = R1.random_element(degree=5, monic=True) + assert f.degree() == 5 + assert f.is_monic() + f = R1.random_element(degree=100, irreducible=True) + assert f.degree() == 100 + assert f.is_irreducible() + f = R1.random_element(degree=1, monic=True, irreducible=True) + assert f.degree() == 1 + assert f.is_irreducible() + assert f.is_monic() + assert raises(lambda: R1.random_element(degree=-123), ValueError) + assert raises(lambda: R1.random_element(monic="A"), ValueError) + assert raises(lambda: R1.random_element(irreducible="A"), ValueError) + # Conversion tests F = fmpz_mod_ctx(11) + F_other = fmpz_mod_ctx(10) R = fmpz_mod_poly_ctx(F) + R_other = fmpz_mod_poly_ctx(F_other) + + assert raises(lambda: fmpz_mod_poly(1, "A"), TypeError) # Need a valid context + assert raises(lambda: R(R_other([1,2,3])), ValueError), f"{R(R_other([1,2,3])) = }" # moduli must match + assert raises(lambda: R(F_other(2)), ValueError) # moduli must match + assert raises(lambda: R([F(1), F_other(2)]), ValueError) # moduli must match + assert raises(lambda: R([F(1), "A"]), TypeError) # need to be able to cast to fmpz_mod f1 = R([int(-1),int(-2),int(-3)]) f2 = R([fmpz(-1),fmpz(-2),fmpz(-3)]) f3 = R([F(-1),F(-2),F(-3)]) f4 = R(fmpz_poly([-1, -2, -3])) + f5 = R(f4) assert str(f1) == "8*x^2 + 9*x + 10" assert str(f2) == "8*x^2 + 9*x + 10" assert str(f3) == "8*x^2 + 9*x + 10" assert str(f4) == "8*x^2 + 9*x + 10" + assert str(f5) == "8*x^2 + 9*x + 10" f1 = R(5) f2 = R(fmpz(6)) @@ -1890,6 +1922,13 @@ def test_fmpz_mod_poly(): f[0] = 7 assert repr(f[0]) == "fmpz_mod(7, 11)" assert str(f) == "8*x^3 + 7*x^2 + 6*x + 7" + assert f[-1] == 0 + + # TODO: I had to use this instead of f[-1] = 1 + # for the lambda... is this ok? + assert raises(lambda: f.__setitem__(-1, 1), ValueError) + assert raises(lambda: f.__setitem__(1, "A"), TypeError) + # Comparisons f1 = R([1,2,3]) @@ -1901,6 +1940,14 @@ def test_fmpz_mod_poly(): assert (f1 != f3) is True assert (f1 != "1") is True assert (f4 == 3) is True + assert (hash(f1) == hash(f2)) is True + assert raises(lambda: f1 > f2, TypeError) + assert raises(lambda: f1 >= f2, TypeError) + assert raises(lambda: f1 < f2, TypeError) + assert raises(lambda: f1 <= f2, TypeError) + + assert len(f1) == f1.length() == 3 + assert f1.degree() == 2 f1 = R([0]) f2 = R([1]) @@ -1910,6 +1957,198 @@ def test_fmpz_mod_poly(): assert f2.is_one() is True assert f3.is_gen() is True + # Arithmetic + p_sml = 163 + p_med = 2**127 - 1 + p_big = 2**255 - 19 + + F_sml = fmpz_mod_ctx(p_sml) + F_med = fmpz_mod_ctx(p_med) + F_big = fmpz_mod_ctx(p_big) + + R_sml = fmpz_mod_poly_ctx(F_sml) + R_med = fmpz_mod_poly_ctx(F_med) + R_big = fmpz_mod_poly_ctx(F_big) + + F_cmp = fmpz_mod_ctx(10) + R_cmp = fmpz_mod_poly_ctx(F_cmp) + f_cmp = R_cmp([1,2,3,4,5]) + f_bad = R_cmp([2,2,2,2,2]) + + for (F_test, R_test) in [(F_sml, R_sml), (F_med, R_med), (F_big, R_big)]: + + f = R_test([-1,-2]) + g = R_test([-3,-4]) + + # pos, neg + assert f is +f + assert -f == R_test([1,2]) + + # add + assert raises(lambda: f + f_cmp, ValueError) + assert raises(lambda: f + "AAA", TypeError) + assert raises(lambda: "AAA" + f, TypeError) + assert f + g == R_test([-4,-6]) + assert f + 1 == R_test([0,-2]) + assert f + fmpz(1) == R_test([0,-2]) + assert f + F_test(1) == R_test([0,-2]) + assert 1 + f == R_test([0,-2]) + assert fmpz(1) + f == R_test([0,-2]) + assert F_test(1) + f == R_test([0,-2]) + + # sub + assert raises(lambda: f - f_cmp, ValueError) + assert raises(lambda: f - "AAA", TypeError) + assert raises(lambda: "AAA" - f, TypeError) + assert f - g == R_test([2, 2]) + assert f - 1 == R_test([-2,-2]) + assert f - fmpz(1) == R_test([-2,-2]) + assert f - F_test(1) == R_test([-2,-2]) + assert 1 - f == R_test([2, 2]) + assert fmpz(1) - f == R_test([2, 2]) + assert F_test(1) - f == R_test([2, 2]) + + # mul + assert raises(lambda: f * f_cmp, ValueError) + assert raises(lambda: f * "AAA", TypeError) + assert raises(lambda: "AAA" * f, TypeError) + assert f * g == R_test([3, 4 + 6, 8]) + assert f * 2 == R_test([-2,-4]) + assert f * fmpz(2) == R_test([-2,-4]) + assert f * F_test(2) == R_test([-2,-4]) + assert 2 * f == R_test([-2,-4]) + assert fmpz(2) * f == R_test([-2,-4]) + assert F_test(2) * f == R_test([-2,-4]) + + # true div + assert raises(lambda: f / f_cmp, ValueError) + assert raises(lambda: f / "AAA", TypeError) + assert raises(lambda: "AAA" / f, TypeError) + assert (f * g) / g == f + assert (f + f) / 2 == f + assert (f + f) / fmpz(2) == f + assert (f + f) / F_test(2) == f + assert 2 / R_test(2) == 1 + assert raises(lambda: f / g, ValueError) + + # floor div + assert raises(lambda: 1 // f_bad, ValueError) + assert raises(lambda: f // f_cmp, ValueError) + assert raises(lambda: f // "AAA", TypeError) + assert raises(lambda: "AAA" // f, TypeError) + assert (f * g) // g == f + assert (f + f) // 2 == f + assert (f + f) // fmpz(2) == f + assert (f + f) // F_test(2) == f + assert 2 // R_test(2) == 1 + assert (f + 1) // f == 1 + + # pow + assert raises(lambda: f**(-2), ValueError) + assert f*f == f**2 + assert f*f == f**fmpz(2) + + # Shifts + assert R_test([1,2,3]) << 3 == R_test([0,0,0,1,2,3]) + assert R_test([1,2,3]) >> 1 == R_test([2,3]) + + # Mod + assert raises(lambda: f % f_bad, ValueError) + assert raises(lambda: 123 % f_bad, ValueError) + assert raises(lambda: f % "AAA", TypeError) + assert raises(lambda: tuple() % f, TypeError), f'{"AAA" % f = }' + + assert f % 1 == 0 + assert R_test.one() % 1 == 0 + assert 100 % R_test.one() == 0 + assert (f*g + 1) % f == 1 + assert (f*g + g) % f == (g % f) + assert f % R_test([0,1]) == f.constant_coefficient() + + # Evaluation + h = R_test([0, 1]) + assert h(1) == 1 + assert h(-1) == R_test.modulus() - 1 + h = R_test([0, 0, 1]) + assert h(1) == h(-1) + assert raises(lambda: h("AAA"), TypeError) + + # Reverse + assert raises(lambda: h.reverse(degree=-100), ValueError) + assert R_test([-1,-2,-3]).reverse() == R_test([-3,-2,-1]) + + # monic + assert raises(lambda: f_bad.monic(), ValueError) + assert R_test([1,2]).monic() == R_test([1 / F_test(2), 1]) + assert R_test([1,2]).monic(check=False) == R_test([1 / F_test(2), 1]) + + # Square + assert f*f == f**2 == f.square() + + # mul_mod + assert f.mul_mod(f, g) == (f*f) % g + assert raises(lambda: f.mul_mod(f, "AAA"), TypeError) + assert raises(lambda: f.mul_mod("AAA", g), TypeError) + + # pow_mod + assert f.pow_mod(2, g) == (f*f) % g + assert raises(lambda: f.pow_mod(2, "AAA"), TypeError) + + # divrem + S, T = f.divrem(g) + assert S*g + T == f + assert raises(lambda: f.divrem("AAA"), TypeError) + assert raises(lambda: f_bad.divrem(f_bad), ValueError) + + # gcd + assert raises(lambda: f_cmp.gcd(f_cmp), NotImplementedError) + assert raises(lambda: f.gcd("f"), TypeError) + + # xgcd + assert raises(lambda: (f_cmp).xgcd(f_cmp), ValueError) + assert raises(lambda: (f).xgcd("f_cmp"), TypeError) + + + # disc. + assert raises(lambda: (f_cmp).discriminant(), NotImplementedError) + + # Radical + assert raises(lambda: (f_cmp).radical(), NotImplementedError) + + # inverse_mod + f_inv = f.inverse_mod(g) + assert (f * f_inv) % g == 1 + assert raises(lambda: f.inverse_mod("AAA"), TypeError) + assert raises(lambda: (f_cmp).inverse_mod(f_cmp), ValueError) + + f_inv = f.inverse_series_trunc(2) + assert (f * f_inv) % R_test([0,0,1]) == 1 + assert raises(lambda: R_cmp([0,0,1]).inverse_series_trunc(2), ValueError) + + + # factor + ff = R_test([3,2,1]) * R_test([3,2,1]) * R_test([5,4,3]) + ff_rebuild = R_test.one() + c, facs = ff.factor() + ff_rebuild *= c + for p, e in facs: + assert p.is_irreducible() + ff_rebuild *= p**e + assert ff_rebuild == ff + + assert set(ff.factor()[1]) == set(ff.factor(algorithm="cantor_zassenhaus")[1]) + assert set(ff.factor()[1]) == set(ff.factor(algorithm="kaltofen_shoup")[1]) + assert set(ff.factor()[1]) == set(ff.factor(algorithm="berlekamp")[1]) + assert raises(lambda: R_test([0,0,1]).factor(algorithm="AAA"), ValueError) + assert raises(lambda: R_test([0,0,1]).complex_roots(), NotImplementedError) + + + # composite moduli not supported + assert raises(lambda: R_cmp([0,0,1]).factor(), NotImplementedError) + assert raises(lambda: R_cmp([0,0,1]).factor_squarefree(), NotImplementedError) + assert raises(lambda: R_cmp([0,0,1]).roots(), NotImplementedError) + assert raises(lambda: R_cmp([0,0,1]).complex_roots(), NotImplementedError) + all_tests = [ test_pyflint, diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 2f29f47f..db31ddcc 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -17,13 +17,13 @@ from flint.flint_base.flint_base cimport flint_poly from flint.utils.typecheck cimport typecheck cdef class fmpz_mod_poly_ctx: - """ - NOTE: + r""" + Context object for creating :class:`~.fmpz_mod_poly` initalised + with a modulus :math:`N`. + + >>> fmpz_mod_poly_ctx(2**127 - 1) + fmpz_mod_poly_ctx(170141183460469231731687303715884105727) - Technically this could just be the same as `fmpz_mod_ctx`, - however, the usage of fmpz_mod_ctx allows the creation of - `fmpz_mod` types by calling the context class. For symmetry - we allow this to be the case here. """ def __cinit__(self): pass @@ -40,7 +40,7 @@ cdef class fmpz_mod_poly_ctx: def modulus(self): """ - Return the modulus from the context as an fmpz + Return the modulus from the context as an ``fmpz`` type >>> R = fmpz_mod_poly_ctx(2**127 - 1) @@ -91,7 +91,7 @@ cdef class fmpz_mod_poly_ctx: def gen(self): """ - Return the generator of the polynomial `x` + Return the generator of the polynomial: `x` >>> R = fmpz_mod_poly_ctx(163) >>> R.gen() @@ -105,8 +105,8 @@ cdef class fmpz_mod_poly_ctx: def random_element(self, degree=3, monic=False, irreducible=False): """ - Return a random element of degree `degree`. If `monic` is True, - ensures the output is monic. If `irreducible` is True, ensures + Return a random element of degree ``degree``. If ``monic`` is ``True``, + ensures the output is monic. If ``irreducible`` is ``True``, ensures that the output is irreducible. >>> R = fmpz_mod_poly_ctx(163) @@ -192,6 +192,15 @@ cdef class fmpz_mod_poly_ctx: if typecheck(obj, list): return self.set_list_as_fmpz_mod_poly(poly, obj) + # Set val from fmpz_mod_poly + if typecheck(obj, fmpz_mod_poly): + if self != (obj).ctx: + raise ValueError("moduli must match") + fmpz_mod_poly_set( + poly, (obj).val, self.mod.val + ) + return 0 + # Convert fmpz_mod to constant poly if typecheck(obj, fmpz_mod): if self.mod != (obj).ctx: @@ -448,9 +457,9 @@ cdef class fmpz_mod_poly(flint_poly): def shift(self, slong n): """ - Returns `self` shifted by `n` coefficients. If `n` is positive, - zero coefficients are inserted, when `n` is negative, if `n` is - greater than or equal to the length of `self`, the zero polynomial + Returns ``self`` shifted by ``n`` coefficients. If ``n`` is positive, + zero coefficients are inserted, when ``n`` is negative, if ``n`` is + greater than or equal to the length of ``self``, the zero polynomial is returned. >>> R = fmpz_mod_poly_ctx(163) @@ -575,14 +584,14 @@ cdef class fmpz_mod_poly(flint_poly): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) def __hash__(self): - return hash(map(int, self.coeffs())) + return hash((int(c) for c in self.coeffs())) - def __call__(self, val): + def __call__(self, input): cdef fmpz_mod res - val = self.ctx.mod.any_as_fmpz_mod(val) + val = self.ctx.mod.any_as_fmpz_mod(input) if val is NotImplemented: - return val + raise TypeError(f"Cannot evaluate the polynomial with input: {input}") res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx.mod @@ -590,15 +599,33 @@ cdef class fmpz_mod_poly(flint_poly): return res cpdef long length(self): + """ + Return the length of the polynomial + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.length() + 3 + + """ return fmpz_mod_poly_length(self.val, self.ctx.mod.val) cpdef long degree(self): + """ + Return the degree of the polynomial + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.degree() + 2 + + """ return fmpz_mod_poly_degree(self.val, self.ctx.mod.val) def is_zero(self): """ - Return `True` if the polynomial is the zero polynomial - and `False` otherwise + Return ``True`` if the polynomial is the zero polynomial + and ``False`` otherwise >>> R = fmpz_mod_poly_ctx(163) >>> f = R(0) @@ -609,8 +636,8 @@ cdef class fmpz_mod_poly(flint_poly): def is_one(self): """ - Return `True` if the polynomial is the zero polynomial - and `False` otherwise + Return ``True`` if the polynomial is the zero polynomial + and `Fal`se` otherwise >>> R = fmpz_mod_poly_ctx(163) >>> f = R(1) @@ -621,8 +648,8 @@ cdef class fmpz_mod_poly(flint_poly): def is_gen(self): """ - Return `True` if the polynomial is the zero polynomial - and `False` otherwise + Return ``True`` if the polynomial is the zero polynomial + and ``False`` otherwise >>> R = fmpz_mod_poly_ctx(163) >>> f = R([0,1]) @@ -633,7 +660,7 @@ cdef class fmpz_mod_poly(flint_poly): def is_constant(self): """ - Return True if this is a constant polynomial. + Return ``True`` if this is a constant polynomial. >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -646,7 +673,7 @@ cdef class fmpz_mod_poly(flint_poly): def constant_coefficient(self): """ - Return the leading coefficient of this polynomial. + Return the constant coefficient of this polynomial. >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) @@ -671,8 +698,8 @@ cdef class fmpz_mod_poly(flint_poly): Return a polynomial with the coefficients of this polynomial reversed. - If `degree` is not None, the output polynomial will be zero-padded - or truncated before being reversed. Note: degree must be non-negative. + If ``degree`` is not None, the output polynomial will be zero-padded + or truncated before being reversed. NOTE: degree must be non-negative. >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3,4,5]) @@ -702,9 +729,9 @@ cdef class fmpz_mod_poly(flint_poly): def truncate(self, slong n): r""" - Notionally truncate the polynomial to have length `n`. If - `n` is larger than the length of the input, then `self` is - returned. If n is not positive, then the zero polynomial + Notionally truncate the polynomial to have length ``n``. If + ``n`` is larger than the length of the input, then ``self`` is + returned. If ``n`` is not positive, then the zero polynomial is returned. Effectively returns this polynomial :math:`\mod x^n`. @@ -757,8 +784,8 @@ cdef class fmpz_mod_poly(flint_poly): """ Return this polynomial divided by its leading coefficient. - If `check` is True, raises ValueError if the leading coefficient - is not invertible modulo N. If `check` is False and the leading + If ``check`` is True, raises ValueError if the leading coefficient + is not invertible modulo N. If ``check`` is False and the leading coefficient is not invertible, the output is undefined. >>> R = fmpz_mod_poly_ctx(163) @@ -817,7 +844,12 @@ cdef class fmpz_mod_poly(flint_poly): def square(self): """ - Returns the square of `self` + Returns the square of ``self`` + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.square() + 9*x^4 + 12*x^3 + 10*x^2 + 4*x + 1 """ cdef fmpz_mod_poly res @@ -830,8 +862,8 @@ cdef class fmpz_mod_poly(flint_poly): def mul_mod(self, other, modulus): """ - Computes the multiplication of `self` with `other` - modulo the polynomial `modulus` + Computes the multiplication of ``self`` with ``other`` + modulo the polynomial ``modulus`` >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -846,11 +878,11 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - return other + raise TypeError("Cannot interpret {other} as a polynomial") modulus = self.ctx.any_as_fmpz_mod_poly(modulus) if modulus is NotImplemented: - return modulus + raise TypeError("Cannot interpret {modulus} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -862,7 +894,8 @@ cdef class fmpz_mod_poly(flint_poly): def pow_mod(self, e, modulus): """ - Returns `self` raised to the power `e` modulo `modulus` + Returns ``self`` raised to the power ``e`` modulo ``modulus``: + :math:`f^e \mod g` >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -877,7 +910,7 @@ cdef class fmpz_mod_poly(flint_poly): modulus = self.ctx.any_as_fmpz_mod_poly(modulus) if modulus is NotImplemented: - return modulus + raise TypeError("Cannot interpret {modulus} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -888,7 +921,8 @@ cdef class fmpz_mod_poly(flint_poly): def divrem(self, other): """ - Return Q, R such that self = Q*other + R + Return `Q`, `R` such that for ``self`` = `F` and ``other`` = `G`, + `F = Q*G + R` >>> R = fmpz_mod_poly_ctx(163) >>> f = R([123, 129, 63, 14, 51, 76, 133]) @@ -901,7 +935,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - return NotImplemented + raise TypeError("Cannot interpret {other} as a polynomial") Q = fmpz_mod_poly.__new__(fmpz_mod_poly) R = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -939,7 +973,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - return other + raise TypeError("Cannot interpret {other} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -949,11 +983,11 @@ cdef class fmpz_mod_poly(flint_poly): return res def xgcd(self, other): - """ - Computes the extended gcd of self and other: (G, S, T) - where G is the gcd(self, other) and S, T are such that: + r""" + Computes the extended gcd of self and other: (`G`, `S`, `T`) + where `G` is the ``gcd(self, other)`` and `S`, `T` are such that: - G = self*S + other*T + :math:`G = \textrm{self}*S + \textrm{other}*T` >>> R = fmpz_mod_poly_ctx(163) >>> f = R([143, 19, 37, 138, 102, 127, 95]) @@ -967,7 +1001,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - return other + raise TypeError("Cannot interpret {other} as a polynomial") G = fmpz_mod_poly.__new__(fmpz_mod_poly) S = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -1008,7 +1042,7 @@ cdef class fmpz_mod_poly(flint_poly): def discriminant(self): """ - Return the discriminant of self. + Return the discriminant of ``self``. >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -1031,7 +1065,7 @@ cdef class fmpz_mod_poly(flint_poly): def radical(self): """ - Return the radical of self, the product of the irreducible + Return the radical of ``self``, the product of the irreducible factors of the polynomial. This is also referred to as the square-free part of the polynomial. @@ -1049,7 +1083,7 @@ cdef class fmpz_mod_poly(flint_poly): def inverse_mod(self, other): """ - Returns the inverse of self modulo other + Returns the inverse of ``self`` modulo ``other`` >>> R = fmpz_mod_poly_ctx(163) >>> f = f = R([123, 129, 63, 14, 51, 76, 133]) @@ -1063,7 +1097,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - return other + raise TypeError("Cannot interpret {other} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -1079,7 +1113,7 @@ cdef class fmpz_mod_poly(flint_poly): def inverse_series_trunc(self, slong n): """ - Returns the inverse of self modulo x^n. + Returns the inverse of ``self`` modulo `x^n`. >>> R = fmpz_mod_poly_ctx(163) >>> f = R([123, 129, 63, 14, 51, 76, 133]) @@ -1108,9 +1142,6 @@ cdef class fmpz_mod_poly(flint_poly): def resultant(self): pass - def evaluate(self): - pass - def multipoint_evaluate(self): pass @@ -1126,15 +1157,21 @@ cdef class fmpz_mod_poly(flint_poly): def deflate(self): pass + def berlekamp_massey(self): + pass + + def radix_conversion(self): + pass + # TODO: we could make a factorisation class which we could then # implement the factor methods such as pow and concat. I think # sage does something like this with `Factorisation` classes. 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. + Factors self into irreducible, squarefree factors, returning a tuple + ``(c, factors)`` where `c` is the content of the coefficients and + factors is a list of ``(poly, exp)`` pairs. >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -1168,8 +1205,8 @@ cdef class fmpz_mod_poly(flint_poly): def factor(self, algorithm=None): """ 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. + ``(c, factors)`` where `c` is the content of the coefficients and + factors is a list of ``(poly, exp)`` pairs. >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -1210,9 +1247,9 @@ cdef class fmpz_mod_poly(flint_poly): return self.leading_coefficient(), res def roots(self, multiplicities=True): - """ - Return the roots of the polynomial in (Z/NZ)* - Requires that the modulus N is prime. + r""" + Return the roots of the polynomial in :math:`(\mathbb{Z}/N\mathbb{Z})^*` + Requires that the modulus `N` is prime. >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() @@ -1253,10 +1290,8 @@ cdef class fmpz_mod_poly(flint_poly): return res def complex_roots(self): - return NotImplementedError("the method `complex_roots` is not yet implemented for fmpz_mod_poly") - - def berlekamp_massey(self): - raise NotImplemented - - def radix_conversion(self): - raise NotImplemented + """ + This method is not currently implemented for polynomials in + :math:`(\mathbb{Z}/N\mathbb{Z})[X]` + """ + raise NotImplementedError("the method `complex_roots` is not yet implemented for fmpz_mod_poly") From a2a7c1fb09901bcef2d69df1a3b881871f99c5e0 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 13:59:51 +0100 Subject: [PATCH 16/35] Include fmpz_mod_poly into documentation: --- doc/source/fmpz_mod_poly.rst | 13 +++++++++++++ doc/source/index.rst | 1 + 2 files changed, 14 insertions(+) create mode 100644 doc/source/fmpz_mod_poly.rst diff --git a/doc/source/fmpz_mod_poly.rst b/doc/source/fmpz_mod_poly.rst new file mode 100644 index 00000000..1af54bb6 --- /dev/null +++ b/doc/source/fmpz_mod_poly.rst @@ -0,0 +1,13 @@ +**fmpz_mod_poly** -- polynomials over integers mod n +=============================================================================== + +.. autoclass :: flint.fmpz_mod_poly_ctx + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpz_mod_poly + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/index.rst b/doc/source/index.rst index 193e5701..2df7aa3f 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -70,6 +70,7 @@ Polynomial types fmpz_poly.rst fmpq_poly.rst nmod_poly.rst + fmpz_mod_poly.rst arb_poly.rst acb_poly.rst From 857ee267bf3b7628ab59015043f6df4628174c76 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 15:04:11 +0100 Subject: [PATCH 17/35] Address first round of comments from the review --- src/flint/flint_base/flint_base.pyx | 2 +- src/flint/test/__main__.py | 2 ++ src/flint/test/test.py | 4 ++++ src/flint/types/fmpz_mod_poly.pyx | 6 +++--- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index e2a67511..fbb61478 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -33,7 +33,7 @@ cdef class flint_poly(flint_elem): >>> from flint import fmpz_poly >>> f = fmpz_poly([1,2,3,4,5]) >>> f.coeffs() - [1,2,3,4,5] + [1, 2, 3, 4, 5] """ return list(self) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 517eb15c..c22e5f48 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -52,6 +52,8 @@ def run_doctests(verbose=None): """Run the python-flint doctests""" # Here verbose=True shows a lot of output. modules = [flint.pyflint, + flint.flint_base.flint_base, + flint.flint_base.flint_context, flint.types.fmpz, flint.types.fmpz_poly, flint.types.fmpz_mat, diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 7bb4dcee..8e8f9f4d 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1842,9 +1842,13 @@ def test_fmpz_mod_poly(): assert raises(lambda: fmpz_mod_ctx("AAA"), TypeError) assert raises(lambda: fmpz_mod_ctx(-1), ValueError) assert (R1 == R1) is True + assert (R1 != R1) is False assert (R1 == R2) is True + assert (R1 != R2) is False assert (R1 != R3) is True + assert (R1 == R3) is False assert (R1 != "AAA") is True + assert (R1 == "AAA") is False assert (hash(R1) == hash(R1)) is True assert (hash(R1) == hash(R2)) is True diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index db31ddcc..4da8da6e 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -313,8 +313,7 @@ cdef class fmpz_mod_poly(flint_poly): @staticmethod def _sub_(left, right): cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - + # Case when left and right are already fmpz_mod_poly if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): if not (left).ctx == (right).ctx: @@ -332,6 +331,7 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented + res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = (left).ctx fmpz_mod_poly_sub( res.val, (left).val, (right).val, res.ctx.mod.val @@ -584,7 +584,7 @@ cdef class fmpz_mod_poly(flint_poly): return fmpz_mod_poly_length(self.val, self.ctx.mod.val) def __hash__(self): - return hash((int(c) for c in self.coeffs())) + return hash(tuple(self.coeffs())) def __call__(self, input): cdef fmpz_mod res From 9cabaa2b4916ea7d0e495bf108d7c426647cca6a Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 17:05:37 +0100 Subject: [PATCH 18/35] Add a custom, DomainError, Exception --- src/flint/utils/flint_exceptions.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/flint/utils/flint_exceptions.py diff --git a/src/flint/utils/flint_exceptions.py b/src/flint/utils/flint_exceptions.py new file mode 100644 index 00000000..580b4549 --- /dev/null +++ b/src/flint/utils/flint_exceptions.py @@ -0,0 +1,8 @@ + + +class DomainError(Exception): + """ + Exception intended to be called when a method is called on a + ring or field for which the domain is invalid. + """ + pass From 024a09c80eeb4823a6861a577c9b7626bdfb81ae Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 17:05:57 +0100 Subject: [PATCH 19/35] Improve based off feedback and add additional functions --- src/flint/test/test.py | 47 +++++-- src/flint/types/fmpz_mod_poly.pyx | 215 +++++++++++++++++++++++------- 2 files changed, 204 insertions(+), 58 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 8e8f9f4d..c86d8fbe 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -4,6 +4,8 @@ import pickle import doctest +from flint.utils.flint_exceptions import DomainError + import flint if sys.version_info[0] >= 3: @@ -1926,10 +1928,15 @@ def test_fmpz_mod_poly(): f[0] = 7 assert repr(f[0]) == "fmpz_mod(7, 11)" assert str(f) == "8*x^3 + 7*x^2 + 6*x + 7" - assert f[-1] == 0 - # TODO: I had to use this instead of f[-1] = 1 - # for the lambda... is this ok? + # TODO: currently repr does pretty printing + # just like str, we should address this. Mainly, + # the issue is we want nice `repr` behaviour in + # interactive shells, which currently is why this + # choice has been made + assert str(f) == repr(f) + + assert f[-1] == 0 assert raises(lambda: f.__setitem__(-1, 1), ValueError) assert raises(lambda: f.__setitem__(1, "A"), TypeError) @@ -2089,14 +2096,14 @@ def test_fmpz_mod_poly(): # Square assert f*f == f**2 == f.square() - # mul_mod - assert f.mul_mod(f, g) == (f*f) % g - assert raises(lambda: f.mul_mod(f, "AAA"), TypeError) - assert raises(lambda: f.mul_mod("AAA", g), TypeError) + # mulmod + assert f.mulmod(f, g) == (f*f) % g + assert raises(lambda: f.mulmod(f, "AAA"), TypeError) + assert raises(lambda: f.mulmod("AAA", g), TypeError) - # pow_mod - assert f.pow_mod(2, g) == (f*f) % g - assert raises(lambda: f.pow_mod(2, "AAA"), TypeError) + # powmod + assert f.powmod(2, g) == (f*f) % g + assert raises(lambda: f.powmod(2, "AAA"), TypeError) # divrem S, T = f.divrem(g) @@ -2129,6 +2136,21 @@ def test_fmpz_mod_poly(): assert (f * f_inv) % R_test([0,0,1]) == 1 assert raises(lambda: R_cmp([0,0,1]).inverse_series_trunc(2), ValueError) + # Resultant + f1 = R_test([-3, 1]) + f2 = R_test([-5, 1]) + assert f1.resultant(f2) == (3 - 5) + assert raises(lambda: f.resultant("AAA"), TypeError) + + # sqrt + f1 = R_test.random_element(irreducible=True) + assert raises(lambda: f1.sqrt(), ValueError) + assert (f1*f1).sqrt() in [f1, -f1] + + # deflate + f1 = R_test([1,0,2,0,3]) + assert raises(lambda: f1.deflate(100), ValueError) + assert f1.deflate(2) == R_test([1,2,3]) # factor ff = R_test([3,2,1]) * R_test([3,2,1]) * R_test([5,4,3]) @@ -2144,14 +2166,13 @@ def test_fmpz_mod_poly(): assert set(ff.factor()[1]) == set(ff.factor(algorithm="kaltofen_shoup")[1]) assert set(ff.factor()[1]) == set(ff.factor(algorithm="berlekamp")[1]) assert raises(lambda: R_test([0,0,1]).factor(algorithm="AAA"), ValueError) - assert raises(lambda: R_test([0,0,1]).complex_roots(), NotImplementedError) - + assert raises(lambda: R_test([0,0,1]).complex_roots(), DomainError) # composite moduli not supported assert raises(lambda: R_cmp([0,0,1]).factor(), NotImplementedError) assert raises(lambda: R_cmp([0,0,1]).factor_squarefree(), NotImplementedError) assert raises(lambda: R_cmp([0,0,1]).roots(), NotImplementedError) - assert raises(lambda: R_cmp([0,0,1]).complex_roots(), NotImplementedError) + assert raises(lambda: R_cmp([0,0,1]).complex_roots(), DomainError) all_tests = [ diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 4da8da6e..bd556c6d 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -14,7 +14,9 @@ from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod from flint.types.fmpz_poly cimport fmpz_poly from flint.flint_base.flint_base cimport flint_poly + from flint.utils.typecheck cimport typecheck +from flint.utils.flint_exceptions import DomainError cdef class fmpz_mod_poly_ctx: r""" @@ -181,7 +183,7 @@ cdef class fmpz_mod_poly_ctx: val_fmpz = any_as_fmpz(val[i]) if val_fmpz is NotImplemented: fmpz_clear(x) - raise TypeError("unsupported coefficient in list") + raise TypeError(f"unsupported coefficient in list") fmpz_mod_poly_set_coeff_fmpz( poly, i, ((val_fmpz)).val, self.mod.val ) @@ -448,10 +450,11 @@ cdef class fmpz_mod_poly(flint_poly): if e < 0: raise ValueError("Exponent must be non-negative") + cdef ulong e_ulong = e res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx fmpz_mod_poly_pow( - res.val, self.val, (e), self.ctx.mod.val + res.val, self.val, e_ulong, self.ctx.mod.val ) return res @@ -476,9 +479,6 @@ cdef class fmpz_mod_poly(flint_poly): 0 """ - if n == 0: - return self - cdef fmpz_mod_poly res res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -491,6 +491,10 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_shift_right( res.val, self.val, -n, self.ctx.mod.val ) + else: # do nothing, just copy self + fmpz_mod_poly_set( + res.val, self.val, self.ctx.mod.val + ) return res @@ -529,6 +533,7 @@ cdef class fmpz_mod_poly(flint_poly): f, res.val, (left).val, (right).val, res.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError( f"Cannot compute remainder of {left} modulo {right}" ) @@ -738,7 +743,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) - >>> f.truncate(3) is f + >>> f.truncate(3) == f True >>> f.truncate(2) 2*x + 1 @@ -753,16 +758,19 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res length = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) - if n > length: - return self - res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx - if n <= 0: + if n <= 0: # return zero return res - - fmpz_mod_poly_set_trunc(res.val, self.val, n, self.ctx.mod.val) + elif n > length: # do nothing + fmpz_mod_poly_set( + res.val, self.val, self.ctx.mod.val + ) + else: + fmpz_mod_poly_set_trunc( + res.val, self.val, n, self.ctx.mod.val + ) return res def is_monic(self): @@ -807,6 +815,7 @@ cdef class fmpz_mod_poly(flint_poly): f, res.val, self.val, self.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError(f"Leading coefficient is not invertible") res.ctx = self.ctx return res @@ -860,7 +869,7 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def mul_mod(self, other, modulus): + def mulmod(self, other, modulus): """ Computes the multiplication of ``self`` with ``other`` modulo the polynomial ``modulus`` @@ -871,18 +880,18 @@ cdef class fmpz_mod_poly(flint_poly): >>> g = 43*x**6 + 91*x**5 + 77*x**4 + 113*x**3 + 71*x**2 + 132*x + 60 >>> mod = x**4 + 93*x**3 + 78*x**2 + 72*x + 149 >>> - >>> f.mul_mod(g, mod) + >>> f.mulmod(g, mod) 106*x^3 + 44*x^2 + 53*x + 77 """ cdef fmpz_mod_poly res other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - raise TypeError("Cannot interpret {other} as a polynomial") + raise TypeError(f"Cannot interpret {other} as a polynomial") modulus = self.ctx.any_as_fmpz_mod_poly(modulus) if modulus is NotImplemented: - raise TypeError("Cannot interpret {modulus} as a polynomial") + raise TypeError(f"Cannot interpret {modulus} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -892,7 +901,7 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def pow_mod(self, e, modulus): + def powmod(self, e, modulus): """ Returns ``self`` raised to the power ``e`` modulo ``modulus``: :math:`f^e \mod g` @@ -903,14 +912,14 @@ cdef class fmpz_mod_poly(flint_poly): >>> g = 43*x**6 + 91*x**5 + 77*x**4 + 113*x**3 + 71*x**2 + 132*x + 60 >>> mod = x**4 + 93*x**3 + 78*x**2 + 72*x + 149 >>> - >>> f.pow_mod(123, mod) + >>> f.powmod(123, mod) 3*x^3 + 25*x^2 + 115*x + 161 """ cdef fmpz_mod_poly res modulus = self.ctx.any_as_fmpz_mod_poly(modulus) if modulus is NotImplemented: - raise TypeError("Cannot interpret {modulus} as a polynomial") + raise TypeError(f"Cannot interpret {modulus} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -935,7 +944,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - raise TypeError("Cannot interpret {other} as a polynomial") + raise TypeError(f"Cannot interpret {other} as a polynomial") Q = fmpz_mod_poly.__new__(fmpz_mod_poly) R = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -947,6 +956,7 @@ cdef class fmpz_mod_poly(flint_poly): f, Q.val, R.val, self.val, (other).val, self.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError( f"Cannot compute divrem of {self} with {other}" ) @@ -973,7 +983,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - raise TypeError("Cannot interpret {other} as a polynomial") + raise TypeError(f"Cannot interpret {other} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -1001,7 +1011,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - raise TypeError("Cannot interpret {other} as a polynomial") + raise TypeError(f"Cannot interpret {other} as a polynomial") G = fmpz_mod_poly.__new__(fmpz_mod_poly) S = fmpz_mod_poly.__new__(fmpz_mod_poly) @@ -1016,6 +1026,7 @@ cdef class fmpz_mod_poly(flint_poly): f, G.val, S.val, T.val, self.val, (other).val, self.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError( f"Cannot compute xgcd of {self} with {other}" ) @@ -1097,7 +1108,7 @@ cdef class fmpz_mod_poly(flint_poly): other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: - raise TypeError("Cannot interpret {other} as a polynomial") + raise TypeError(f"Cannot interpret {other} as a polynomial") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx @@ -1106,8 +1117,9 @@ cdef class fmpz_mod_poly(flint_poly): f, res.val, self.val, (other).val, res.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError( - f"Cannot compute inverse series of {self} modulo {other}" + f"Cannot compute inverse of {self} modulo {other}" ) return res @@ -1134,38 +1146,151 @@ cdef class fmpz_mod_poly(flint_poly): f, res.val, self.val, n, res.ctx.mod.val ) if not fmpz_is_one(f): + fmpz_clear(f) raise ValueError( f"Cannot compute inverse series of {self} modulo x^{n}" ) return res - def resultant(self): - pass + def resultant(self, other): + """ + Returns the resultant of ``self`` with ``other``. - def multipoint_evaluate(self): - pass + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3,4,5]) + >>> g = R([9,8,7]) + >>> f.resultant(g) + fmpz_mod(57, 163) - def square_root(self): - pass + """ + cdef fmpz_mod res - def inverse_square_root(self): - pass + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + raise TypeError(f"Cannot interpret {other} as a polynomial") - def inflate(self): - pass + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx.mod + fmpz_mod_poly_resultant( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res - def deflate(self): - pass + def sqrt(self): + """ + If ``self`` is a perfect square, compute the square root - def berlekamp_massey(self): - pass + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,1]) + >>> (f*f).sqrt() + x + 1 - def radix_conversion(self): - pass + """ + cdef fmpz_mod_poly res + cdef int check - # TODO: we could make a factorisation class which we could then - # implement the factor methods such as pow and concat. I think - # sage does something like this with `Factorisation` classes. + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + check = fmpz_mod_poly_sqrt( + res.val, self.val, res.ctx.mod.val + ) + if check != 1: + raise ValueError( + f"Cannot compute square-root {self}" + ) + return res + + def sqrt_trunc(self, slong n): + """ + Returns the squareroot of ``self`` modulo `x^n`. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = R([1,2,3]) + >>> h = f.sqrt_trunc(5) + >>> h + 82*x^4 + 162*x^3 + x^2 + x + 1 + >>> h.mulmod(h, x**5) == f + True + + """ + cdef fmpz_mod_poly res + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_sqrt_series( + res.val, self.val, n, res.ctx.mod.val + ) + return res + + def inverse_sqrt_trunc(self, slong n): + """ + Returns the inverse squareroot of ``self`` modulo `x^n`. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> h = f.inverse_sqrt_trunc(5) + >>> h + 78*x^4 + 2*x^3 + 162*x + 1 + >>> (h*h).inverse_series_trunc(5) == f + True + """ + cdef fmpz_mod_poly res + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_invsqrt_series( + res.val, self.val, n, res.ctx.mod.val + ) + return res + + def inflate(self, ulong n): + r""" + Returns the result of the polynomial `f = \textrm{self}` to + `f(x^n)` + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.inflate(10) + 3*x^20 + 2*x^10 + 1 + + """ + cdef fmpz_mod_poly res + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_inflate( + res.val, self.val, n, res.ctx.mod.val + ) + return res + + def deflate(self, ulong n): + r""" + Returns the result of the polynomial `f = \textrm{self}` to + `f(x^{1/n})` + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,0,2,0,3]) + >>> f + 3*x^4 + 2*x^2 + 1 + >>> f.deflate(2) + 3*x^2 + 2*x + 1 + + """ + cdef fmpz_mod_poly res + + n_max = fmpz_mod_poly_deflation( + self.val, self.ctx.mod.val + ) + if n > n_max: + raise ValueError(f"Cannot deflate with {n = }, maximum allowed value is {n_max = }") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_deflate( + res.val, self.val, n, res.ctx.mod.val + ) + return res def factor_squarefree(self): """ @@ -1291,7 +1416,7 @@ cdef class fmpz_mod_poly(flint_poly): def complex_roots(self): """ - This method is not currently implemented for polynomials in + This method is not implemented for polynomials in :math:`(\mathbb{Z}/N\mathbb{Z})[X]` """ - raise NotImplementedError("the method `complex_roots` is not yet implemented for fmpz_mod_poly") + raise DomainError("Cannot compute compex roots for polynomials over integers modulo N") From 9a01c5dd2da7414a7b46ed937235782d8a16e72f Mon Sep 17 00:00:00 2001 From: giacomopope Date: Wed, 27 Sep 2023 18:27:50 +0100 Subject: [PATCH 20/35] Begin adding Berlekamp-Massey algorithm --- src/flint/types/fmpz_mod_poly.pxd | 5 ++ src/flint/types/fmpz_mod_poly.pyx | 109 ++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 4239beb2..4fcd03c9 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -16,3 +16,8 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_ctx ctx cpdef long length(self) cpdef long degree(self) + +cdef class BerlekampMassey: + cdef bint initialized + cdef fmpz_mod_berlekamp_massey_t B + cdef fmpz_mod_poly_ctx ctx diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index bd556c6d..2772fb6e 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1420,3 +1420,112 @@ cdef class fmpz_mod_poly(flint_poly): :math:`(\mathbb{Z}/N\mathbb{Z})[X]` """ raise DomainError("Cannot compute compex roots for polynomials over integers modulo N") + +cdef class BerlekampMassey: + def __cinit__(self): + self.initialized = False + + def __dealloc__(self): + if self.initialized: + fmpz_mod_berlekamp_massey_clear(self.B, self.ctx.mod.val) + + def __init__(self, ctx): + if not typecheck(ctx, fmpz_mod_poly_ctx): + raise TypeError + self.ctx = ctx + fmpz_mod_berlekamp_massey_init(self.B, self.ctx.mod.val) + self.initialized = True + + def __str__(self): + return f"Berlekamp-Massey algorithm with context: {repr(self.ctx)}" + + def __repr__(self): + return f"BerlekampMassey({repr(self.ctx)})" + + def start_over(self): + """ + Empty the stream of points + """ + fmpz_mod_berlekamp_massey_start_over(self.B, self.ctx.mod.val) + + def add_point(self, point): + """ + Add a point to the stream processed by `B`. Addition of + these points will not update `V` or `R`. To update the + values use the method `reduce`. + """ + point = self.ctx.mod.any_as_fmpz_mod(point) + if point is NotImplemented: + raise TypeError + + fmpz_mod_berlekamp_massey_add_point( + self.B, (point).val, self.ctx.mod.val + ) + + def add_points(self, points): + """ + Add a list of points to the stream processed by `B`. + Addition of these points will not update `V` or `R`. + To update the values use the method `reduce`. + + TODO: + Should I instead use: + + fmpz_mod_berlekamp_massey_add_point + + Then I need to convert the python list to + const fmpz *a, what's the best method? + """ + if not isinstance(points, list): + raise TypeError + + for point in points: + self.add_point(point) + + def add_zeros(self, slong count): + """ + Add ``count`` zeros + """ + fmpz_mod_berlekamp_massey_add_zeros( + self.B, count, self.ctx.mod.val + ) + + def reduce(self): + """ + Ensure that the polynomials `V`, `R` are up to date. + + Returns ``1`` if the values have been updated and ``0`` otherwise + """ + return fmpz_mod_berlekamp_massey_reduce(self.B, self.ctx.mod.val) + + def point_count(self): + """ + Return the number of points stored in self + """ + return fmpz_mod_berlekamp_massey_point_count(self.B) + + def V(self): + """ + Return the polynomial `V` in `B` + """ + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + res.val = fmpz_mod_berlekamp_massey_V_poly(self.B) + return res + + def R(self): + """ + Return the polynomial `R` in `B` + """ + cdef fmpz_mod_poly res + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + res.val = fmpz_mod_berlekamp_massey_R_poly(self.B) + return res + + def VR(self): + """ + Return the polynomials `V` and `R` in `B` + """ + return self.V(), self.R() From 87a77b73249015e8c4187fbbd9265821cc9e8c2a Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 28 Sep 2023 11:47:43 +0100 Subject: [PATCH 21/35] Add minpoly, remove BM, problems with multipoint eval --- src/flint/test/test.py | 13 ++ src/flint/types/fmpz_mod.pxd | 3 +- src/flint/types/fmpz_mod.pyx | 1 + src/flint/types/fmpz_mod_poly.pxd | 5 - src/flint/types/fmpz_mod_poly.pyx | 222 +++++++++++++++--------------- 5 files changed, 125 insertions(+), 119 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index c86d8fbe..1d872563 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2174,6 +2174,19 @@ def test_fmpz_mod_poly(): assert raises(lambda: R_cmp([0,0,1]).roots(), NotImplementedError) assert raises(lambda: R_cmp([0,0,1]).complex_roots(), DomainError) + # minpoly + assert raises(lambda: R_cmp.minpoly([1,2,3,4]), NotImplementedError) + assert raises(lambda: R_test.minpoly(1), ValueError) + assert raises(lambda: R_test.minpoly([1,2,3,"AAA"]), ValueError) + + # multipoint_evaluation + assert raises(lambda: R_test([1,2,3]).multipoint_evaluation([1,2,3,"AAA"]), ValueError) + f = R_test([1,2,3]) + l = [-1,-2,-3] + # TODO: not working for large modulus! + # assert [f(x) for x in l] == f.multipoint_evaluation(l) + + all_tests = [ test_pyflint, diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd index 99d4ad49..5c47eac9 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -1,11 +1,10 @@ from flint.flint_base.flint_base cimport flint_scalar -from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz cimport fmpz_struct, fmpz_t from flint.flintlib.fmpz_mod cimport ( fmpz_mod_ctx_t, fmpz_mod_discrete_log_pohlig_hellman_t ) - cdef class fmpz_mod_ctx: cdef fmpz_mod_ctx_t val cdef bint _is_prime diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 40c9ee1e..470a8eb2 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -227,6 +227,7 @@ cdef class fmpz_mod(flint_scalar): fmpz_clear(self.val) if self.x_g: fmpz_clear(self.x_g[0]) + libc.stdlib.free(self.x_g) def __init__(self, val, ctx): if not typecheck(ctx, fmpz_mod_ctx): diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 4fcd03c9..4239beb2 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -16,8 +16,3 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_ctx ctx cpdef long length(self) cpdef long degree(self) - -cdef class BerlekampMassey: - cdef bint initialized - cdef fmpz_mod_berlekamp_massey_t B - cdef fmpz_mod_poly_ctx ctx diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 2772fb6e..f46de9d2 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,21 +1,24 @@ +cimport cython +cimport libc.stdlib from cpython.list cimport PyList_GET_SIZE + from flint.pyflint cimport global_random_state -from flint.flintlib.fmpz_mod cimport fmpz_mod_neg +from flint.flintlib.fmpz_mod cimport fmpz_mod_neg, fmpz_mod_set_fmpz from flint.flintlib.fmpz_mod_poly cimport * from flint.flintlib.fmpz_mod_poly_factor cimport * - from flint.flintlib.fmpz cimport( fmpz_init, fmpz_clear, fmpz_is_one ) + from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod from flint.types.fmpz_poly cimport fmpz_poly from flint.flint_base.flint_base cimport flint_poly - from flint.utils.typecheck cimport typecheck + from flint.utils.flint_exceptions import DomainError cdef class fmpz_mod_poly_ctx: @@ -268,9 +271,61 @@ cdef class fmpz_mod_poly_ctx: def __call__(self, val): return fmpz_mod_poly(val, self) + def minpoly(self, vals): + """ + Returns a minimal generating polynomial for sequence `vals`. + + A minimal generating polynomial is a monic polynomial, of minimal degree `d`, + that annihilates any consecutive `d+1` terms in seq. + + Assumes that the modulus is prime. + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.minpoly([1,1,2,3,5,8]) + x^2 + 162*x + 162 + >>> R.minpoly([2,4,6,8,10]) + x^2 + 161*x + 1 + """ + cdef fmpz_mod_poly res + + if not self.is_prime(): + raise NotImplementedError("minpoly algorithm assumes that the modulus is prime") + + if not isinstance(vals, (list, tuple)): + raise ValueError("Input must be a list or tuple of points") + + n = len(vals) + xs = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + for i in range(n): + val_fmpz_mod = self.mod.any_as_fmpz_mod(vals[i]) + if val_fmpz_mod is NotImplemented: + libc.stdlib.free(xs) + raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") + xs[i] = ((val_fmpz_mod)).val[0] + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self + fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) + + libc.stdlib.free(xs) + return res cdef class fmpz_mod_poly(flint_poly): """ + The *fmpz_mod_poly* type represents univariate polynomials + over integer modulo an arbitrary-size modulus. + For wordsize modulus, see :class:`~.nmod_poly`. + + An *fmpz_mod* element is constructed from an :class:`~.fmpz_mod_ctx` + either by passing it as an argument to the type, or + by directly calling the context + + >>> fmpz_mod(-1, fmpz_mod_ctx(2**127 - 1)) + fmpz_mod(170141183460469231731687303715884105726, 170141183460469231731687303715884105727) + >>> ZmodN = fmpz_mod_ctx(2**127 - 1) + >>> ZmodN(-2) + fmpz_mod(170141183460469231731687303715884105725, 170141183460469231731687303715884105727) + """ def __cinit__(self): self.initialized = False @@ -602,6 +657,58 @@ cdef class fmpz_mod_poly(flint_poly): res.ctx = self.ctx.mod fmpz_mod_poly_evaluate_fmpz(res.val, self.val, (val).val, self.ctx.mod.val) return res + + def multipoint_evaluation(self, vals): + """ + Returns a list of values computed from evaluating + ``self`` at the ``n`` values given in the vector ``val`` + + TODO: We could allow passing as an optional input the + subproduct tree, which would allow for faster, repeated + multipoint evaluations + + TODO!! This currently segfaults for very large input, + suggesting a memory leak. Is this something our side of + on the FLINT side? + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3,4,5]) + >>> [f(x) for x in [-1,-2,-3]] + [fmpz_mod(3, 163), fmpz_mod(57, 163), fmpz_mod(156, 163)] + >>> f.multipoint_evaluation([-1,-2,-3]) + [fmpz_mod(3, 163), fmpz_mod(57, 163), fmpz_mod(156, 163)] + """ + cdef fmpz_mod f + + if not isinstance(vals, (list, tuple)): + raise ValueError("Input must be a list of points") + + n = len(vals) + xs = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + for i in range(n): + val = self.ctx.mod.any_as_fmpz_mod(vals[i]) + if val is NotImplemented: + libc.stdlib.free(xs) + raise ValueError(f"Unable to cast {val} to an `fmpz_mod`") + xs[i] = ((val)).val[0] + + # Call for multipoint eval, iterative horner will be used + # for small arrays (len < 32) and a fast eval for larger ones + # using a subproduct tree + ys = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + fmpz_mod_poly_evaluate_fmpz_vec(ys, self.val, xs, n, self.ctx.mod.val) + + evaluations = [] + for i in range(n): + f = fmpz_mod.__new__(fmpz_mod) + f.ctx = self.ctx.mod + fmpz_mod_set_fmpz(f.val, &ys[i], self.ctx.mod.val) + evaluations.append(f) + + libc.stdlib.free(xs) + libc.stdlib.free(ys) + + return evaluations cpdef long length(self): """ @@ -1420,112 +1527,3 @@ cdef class fmpz_mod_poly(flint_poly): :math:`(\mathbb{Z}/N\mathbb{Z})[X]` """ raise DomainError("Cannot compute compex roots for polynomials over integers modulo N") - -cdef class BerlekampMassey: - def __cinit__(self): - self.initialized = False - - def __dealloc__(self): - if self.initialized: - fmpz_mod_berlekamp_massey_clear(self.B, self.ctx.mod.val) - - def __init__(self, ctx): - if not typecheck(ctx, fmpz_mod_poly_ctx): - raise TypeError - self.ctx = ctx - fmpz_mod_berlekamp_massey_init(self.B, self.ctx.mod.val) - self.initialized = True - - def __str__(self): - return f"Berlekamp-Massey algorithm with context: {repr(self.ctx)}" - - def __repr__(self): - return f"BerlekampMassey({repr(self.ctx)})" - - def start_over(self): - """ - Empty the stream of points - """ - fmpz_mod_berlekamp_massey_start_over(self.B, self.ctx.mod.val) - - def add_point(self, point): - """ - Add a point to the stream processed by `B`. Addition of - these points will not update `V` or `R`. To update the - values use the method `reduce`. - """ - point = self.ctx.mod.any_as_fmpz_mod(point) - if point is NotImplemented: - raise TypeError - - fmpz_mod_berlekamp_massey_add_point( - self.B, (point).val, self.ctx.mod.val - ) - - def add_points(self, points): - """ - Add a list of points to the stream processed by `B`. - Addition of these points will not update `V` or `R`. - To update the values use the method `reduce`. - - TODO: - Should I instead use: - - fmpz_mod_berlekamp_massey_add_point - - Then I need to convert the python list to - const fmpz *a, what's the best method? - """ - if not isinstance(points, list): - raise TypeError - - for point in points: - self.add_point(point) - - def add_zeros(self, slong count): - """ - Add ``count`` zeros - """ - fmpz_mod_berlekamp_massey_add_zeros( - self.B, count, self.ctx.mod.val - ) - - def reduce(self): - """ - Ensure that the polynomials `V`, `R` are up to date. - - Returns ``1`` if the values have been updated and ``0`` otherwise - """ - return fmpz_mod_berlekamp_massey_reduce(self.B, self.ctx.mod.val) - - def point_count(self): - """ - Return the number of points stored in self - """ - return fmpz_mod_berlekamp_massey_point_count(self.B) - - def V(self): - """ - Return the polynomial `V` in `B` - """ - cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx - res.val = fmpz_mod_berlekamp_massey_V_poly(self.B) - return res - - def R(self): - """ - Return the polynomial `R` in `B` - """ - cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx - res.val = fmpz_mod_berlekamp_massey_R_poly(self.B) - return res - - def VR(self): - """ - Return the polynomials `V` and `R` in `B` - """ - return self.V(), self.R() From 85f82a4ed57e2b402c7a2323ecf23f40a72563e3 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 28 Sep 2023 12:13:57 +0100 Subject: [PATCH 22/35] Fix stupid copy paste bug --- src/flint/test/test.py | 2 +- src/flint/types/fmpz_mod_poly.pyx | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 1d872563..2156376a 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2182,7 +2182,7 @@ def test_fmpz_mod_poly(): # multipoint_evaluation assert raises(lambda: R_test([1,2,3]).multipoint_evaluation([1,2,3,"AAA"]), ValueError) f = R_test([1,2,3]) - l = [-1,-2,-3] + l = [-1,-2,-3,-4,-5] # TODO: not working for large modulus! # assert [f(x) for x in l] == f.multipoint_evaluation(l) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index f46de9d2..aa6eb91c 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -316,15 +316,15 @@ cdef class fmpz_mod_poly(flint_poly): over integer modulo an arbitrary-size modulus. For wordsize modulus, see :class:`~.nmod_poly`. - An *fmpz_mod* element is constructed from an :class:`~.fmpz_mod_ctx` + An *fmpz_mod_poly* element is constructed from an :class:`~.fmpz_mod_poly_ctx` either by passing it as an argument to the type, or - by directly calling the context + by directly calling the context: - >>> fmpz_mod(-1, fmpz_mod_ctx(2**127 - 1)) - fmpz_mod(170141183460469231731687303715884105726, 170141183460469231731687303715884105727) - >>> ZmodN = fmpz_mod_ctx(2**127 - 1) - >>> ZmodN(-2) - fmpz_mod(170141183460469231731687303715884105725, 170141183460469231731687303715884105727) + >>> fmpz_mod_poly([1,-2,3], fmpz_mod_poly_ctx(2**127 - 1)) + 3*x^2 + 170141183460469231731687303715884105725*x + 1 + >>> R = fmpz_mod_poly_ctx(2**127 - 1) + >>> R([4,5,6]) + 6*x^2 + 5*x + 4 """ def __cinit__(self): From 428beceddb6f47bee9f96ee21f42191628af61df Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 12:50:11 +0100 Subject: [PATCH 23/35] Add support for vec types --- src/flint/flintlib/fmpz_mod_vec.pxd | 15 +++++++ src/flint/flintlib/fmpz_vec.pxd | 68 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/flint/flintlib/fmpz_mod_vec.pxd create mode 100644 src/flint/flintlib/fmpz_vec.pxd diff --git a/src/flint/flintlib/fmpz_mod_vec.pxd b/src/flint/flintlib/fmpz_mod_vec.pxd new file mode 100644 index 00000000..126ff186 --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_vec.pxd @@ -0,0 +1,15 @@ +from flint.flintlib.flint cimport slong, fmpz_struct +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t +from flint.flintlib.fmpz cimport fmpz_t + +cdef extern from "flint/fmpz_mod_vec.h": + void _fmpz_mod_vec_set_fmpz_vec(fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_neg(fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_add(fmpz_struct * a, const fmpz_struct * b, const fmpz_struct * c, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_sub(fmpz_struct * a, const fmpz_struct * b, const fmpz_struct * c, slong n, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_scalar_mul_fmpz_mod(fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_scalar_addmul_fmpz_mod(fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_scalar_div_fmpz_mod(fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_dot(fmpz_t d, const fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_dot_rev(fmpz_t d, const fmpz_struct * A, const fmpz_struct * B, slong len, const fmpz_mod_ctx_t ctx) + void _fmpz_mod_vec_mul(fmpz_struct * A, const fmpz_struct * B, const fmpz_struct * C, slong len, const fmpz_mod_ctx_t ctx) diff --git a/src/flint/flintlib/fmpz_vec.pxd b/src/flint/flintlib/fmpz_vec.pxd new file mode 100644 index 00000000..26924893 --- /dev/null +++ b/src/flint/flintlib/fmpz_vec.pxd @@ -0,0 +1,68 @@ +from flint.flintlib.flint cimport mp_srcptr, flint_bitcnt_t, flint_rand_t, mp_ptr, slong, fmpz_struct, mp_limb_t, ulong +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.nmod cimport nmod_t + +cdef extern from "flint/fmpz_vec.h": + fmpz_struct * _fmpz_vec_init(slong len) + void _fmpz_vec_clear(fmpz_struct * vec, slong len) + void _fmpz_vec_randtest(fmpz_struct * f, flint_rand_t state, slong len, flint_bitcnt_t bits) + void _fmpz_vec_randtest_unsigned(fmpz_struct * f, flint_rand_t state, slong len, flint_bitcnt_t bits) + slong _fmpz_vec_max_bits(const fmpz_struct * vec, slong len) + slong _fmpz_vec_max_bits_ref(const fmpz_struct * vec, slong len) + void _fmpz_vec_sum_max_bits(slong * sumabs, slong * maxabs, const fmpz_struct * vec, slong len) + ulong _fmpz_vec_max_limbs(const fmpz_struct * vec, slong len) + void _fmpz_vec_height(fmpz_t height, const fmpz_struct * vec, slong len) + slong _fmpz_vec_height_index(const fmpz_struct * vec, slong len) + # int _fmpz_vec_fread(FILE * file, fmpz_struct ** vec, slong * len) + int _fmpz_vec_read(fmpz_struct ** vec, slong * len) + # int _fmpz_vec_fprint(FILE * file, const fmpz_struct * vec, slong len) + int _fmpz_vec_print(const fmpz_struct * vec, slong len) + void _fmpz_vec_get_nmod_vec(mp_ptr res, const fmpz_struct * poly, slong len, nmod_t mod) + void _fmpz_vec_set_nmod_vec(fmpz_struct * res, mp_srcptr poly, slong len, nmod_t mod) + void _fmpz_vec_get_fft(mp_limb_t ** coeffs_f, const fmpz_struct * coeffs_m, slong l, slong length) + void _fmpz_vec_set_fft(fmpz_struct * coeffs_m, slong length, const mp_ptr * coeffs_f, slong limbs, slong sign) + slong _fmpz_vec_get_d_vec_2exp(double * appv, const fmpz_struct * vec, slong len) + void _fmpz_vec_set(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + void _fmpz_vec_swap(fmpz_struct * vec1, fmpz_struct * vec2, slong len2) + void _fmpz_vec_zero(fmpz_struct * vec, slong len) + void _fmpz_vec_neg(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + void _fmpz_vec_scalar_abs(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + int _fmpz_vec_equal(const fmpz_struct * vec1, const fmpz_struct * vec2, slong len) + int _fmpz_vec_is_zero(const fmpz_struct * vec, slong len) + void _fmpz_vec_max(fmpz_struct * vec1, const fmpz_struct * vec2, const fmpz_struct * vec3, slong len) + void _fmpz_vec_max_inplace(fmpz_struct * vec1, const fmpz_struct * vec2, slong len) + void _fmpz_vec_sort(fmpz_struct * vec, slong len) + void _fmpz_vec_add(fmpz_struct * res, const fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + void _fmpz_vec_sub(fmpz_struct * res, const fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + void _fmpz_vec_scalar_mul_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t x) + void _fmpz_vec_scalar_mul_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_mul_ui(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong c) + void _fmpz_vec_scalar_mul_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong exp) + void _fmpz_vec_scalar_divexact_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t x) + void _fmpz_vec_scalar_divexact_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_divexact_ui(fmpz_struct * vec1, const fmpz_struct * vec2, ulong len2, ulong c) + void _fmpz_vec_scalar_fdiv_q_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t c) + void _fmpz_vec_scalar_fdiv_q_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_fdiv_q_ui(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong c) + void _fmpz_vec_scalar_fdiv_q_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong exp) + void _fmpz_vec_scalar_fdiv_r_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong exp) + void _fmpz_vec_scalar_tdiv_q_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t c) + void _fmpz_vec_scalar_tdiv_q_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_tdiv_q_ui(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong c) + void _fmpz_vec_scalar_tdiv_q_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong exp) + void _fmpz_vec_scalar_addmul_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_addmul_ui(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, ulong c) + void _fmpz_vec_scalar_addmul_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t c) + void _fmpz_vec_scalar_addmul_si_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c, ulong exp) + void _fmpz_vec_scalar_submul_fmpz(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, const fmpz_t x) + void _fmpz_vec_scalar_submul_si(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c) + void _fmpz_vec_scalar_submul_si_2exp(fmpz_struct * vec1, const fmpz_struct * vec2, slong len2, slong c, ulong e) + void _fmpz_vec_sum(fmpz_t res, const fmpz_struct * vec, slong len) + void _fmpz_vec_prod(fmpz_t res, const fmpz_struct * vec, slong len) + void _fmpz_vec_scalar_mod_fmpz(fmpz_struct *res, const fmpz_struct *vec, slong len, const fmpz_t p) + void _fmpz_vec_scalar_smod_fmpz(fmpz_struct *res, const fmpz_struct *vec, slong len, const fmpz_t p) + void _fmpz_vec_content(fmpz_t res, const fmpz_struct * vec, slong len) + void _fmpz_vec_content_chained(fmpz_t res, const fmpz_struct * vec, slong len, const fmpz_t input) + void _fmpz_vec_lcm(fmpz_t res, const fmpz_struct * vec, slong len) + void _fmpz_vec_dot(fmpz_t res, const fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) + void _fmpz_vec_dot_ptr(fmpz_t res, const fmpz_struct * vec1, fmpz_struct ** const vec2, slong offset, slong len) From bc85cd35fed8fdf879bd911baf7378bb45b7e7e4 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 12:50:39 +0100 Subject: [PATCH 24/35] Clean up set_any function and improve memory management --- src/flint/types/fmpz_mod.pxd | 1 + src/flint/types/fmpz_mod.pyx | 48 +++++++++++++++---------------- src/flint/types/fmpz_mod_poly.pyx | 35 +++++++++------------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd index 5c47eac9..81938672 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -9,6 +9,7 @@ cdef class fmpz_mod_ctx: cdef fmpz_mod_ctx_t val cdef bint _is_prime cdef fmpz_mod_discrete_log_pohlig_hellman_t *L + cdef set_any_as_fmpz_mod(self, fmpz_t val, obj) cdef any_as_fmpz_mod(self, obj) cdef _precompute_dlog_prime(self) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 470a8eb2..687f0eee 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -150,27 +150,41 @@ cdef class fmpz_mod_ctx: self.L[0], self.val.n ) - cdef any_as_fmpz_mod(self, obj): - # If `obj` is an `fmpz_mod`, just check moduli - # match + cdef set_any_as_fmpz_mod(self, fmpz_t val, obj): + # Try and convert obj to fmpz if typecheck(obj, fmpz_mod): if self != (obj).ctx: raise ValueError("moduli must match") - return obj - + fmpz_set(val, (obj).val) + return 0 + # Try and convert obj to fmpz if not typecheck(obj, fmpz): obj = any_as_fmpz(obj) if obj is NotImplemented: return NotImplemented + + fmpz_mod_set_fmpz(val, (obj).val, self.val) + + return 0 + + cdef any_as_fmpz_mod(self, obj): + # If `obj` is an `fmpz_mod`, just check moduli + # match + if typecheck(obj, fmpz_mod): + if self != (obj).ctx: + raise ValueError("moduli must match") + return obj # We have been able to cast `obj` to an `fmpz` so # we create a new `fmpz_mod` and set the val cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) + check = self.set_any_as_fmpz_mod(res.val, obj) + if check is NotImplemented: + return NotImplemented res.ctx = self - fmpz_mod_set_fmpz(res.val, (obj).val, self.val) - + return res def __eq__(self, other): @@ -233,23 +247,9 @@ cdef class fmpz_mod(flint_scalar): if not typecheck(ctx, fmpz_mod_ctx): raise TypeError self.ctx = ctx - - # When the input is also an fmpz_mod we just need - # moduli to match - if typecheck(val, fmpz_mod): - if self.ctx != (val).ctx: - raise ValueError("moduli must match") - # fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) - fmpz_set(self.val, (val).val) - return - - # For all other cases, the easiest is to first convert to - # fmpz type and set this way - if not typecheck(val, fmpz): - val = any_as_fmpz(val) - if val is NotImplemented: - raise NotImplementedError - fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) + check = self.ctx.set_any_as_fmpz_mod(self.val, val) + if check is NotImplemented: + raise NotImplementedError(f"Cannot convert {val} to type `fmpz_mod`") def is_zero(self): """ diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index aa6eb91c..f4e29fba 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1,5 +1,3 @@ -cimport cython -cimport libc.stdlib from cpython.list cimport PyList_GET_SIZE from flint.pyflint cimport global_random_state @@ -11,6 +9,7 @@ from flint.flintlib.fmpz cimport( fmpz_clear, fmpz_is_one ) +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_ctx, fmpz_mod @@ -295,19 +294,18 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("Input must be a list or tuple of points") n = len(vals) - xs = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + xs = _fmpz_vec_init(n) for i in range(n): - val_fmpz_mod = self.mod.any_as_fmpz_mod(vals[i]) - if val_fmpz_mod is NotImplemented: - libc.stdlib.free(xs) + check = self.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + if check is NotImplemented: + _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") - xs[i] = ((val_fmpz_mod)).val[0] res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) - libc.stdlib.free(xs) + _fmpz_vec_clear(xs, n) return res cdef class fmpz_mod_poly(flint_poly): @@ -667,10 +665,6 @@ cdef class fmpz_mod_poly(flint_poly): subproduct tree, which would allow for faster, repeated multipoint evaluations - TODO!! This currently segfaults for very large input, - suggesting a memory leak. Is this something our side of - on the FLINT side? - >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3,4,5]) >>> [f(x) for x in [-1,-2,-3]] @@ -684,18 +678,17 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Input must be a list of points") n = len(vals) - xs = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + xs = _fmpz_vec_init(n) for i in range(n): - val = self.ctx.mod.any_as_fmpz_mod(vals[i]) - if val is NotImplemented: - libc.stdlib.free(xs) - raise ValueError(f"Unable to cast {val} to an `fmpz_mod`") - xs[i] = ((val)).val[0] + check = self.ctx.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + if check is NotImplemented: + _fmpz_vec_clear(xs, n) + raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") # Call for multipoint eval, iterative horner will be used # for small arrays (len < 32) and a fast eval for larger ones # using a subproduct tree - ys = libc.stdlib.malloc(cython.sizeof(fmpz_struct) * n) + ys = _fmpz_vec_init(n) fmpz_mod_poly_evaluate_fmpz_vec(ys, self.val, xs, n, self.ctx.mod.val) evaluations = [] @@ -705,8 +698,8 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_set_fmpz(f.val, &ys[i], self.ctx.mod.val) evaluations.append(f) - libc.stdlib.free(xs) - libc.stdlib.free(ys) + _fmpz_vec_clear(xs, n) + _fmpz_vec_clear(ys, n) return evaluations From cb60fe76bf2e583a16a0f75de54eb61c36baa186 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 12:50:54 +0100 Subject: [PATCH 25/35] Add tests --- src/flint/test/test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 2156376a..5ed0eb16 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2181,10 +2181,11 @@ def test_fmpz_mod_poly(): # multipoint_evaluation assert raises(lambda: R_test([1,2,3]).multipoint_evaluation([1,2,3,"AAA"]), ValueError) + assert raises(lambda: R_test([1,2,3]).multipoint_evaluation("AAA"), ValueError) + f = R_test([1,2,3]) l = [-1,-2,-3,-4,-5] - # TODO: not working for large modulus! - # assert [f(x) for x in l] == f.multipoint_evaluation(l) + assert [f(x) for x in l] == f.multipoint_evaluation(l) From 69b2e50f4ff7bb5031e29054884818cc8071c19e Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 16:28:32 +0100 Subject: [PATCH 26/35] Address bug from merging in test. Rename shift and (in/de)flation functions --- src/flint/test/test.py | 15 ++++--- src/flint/types/fmpz_mod_poly.pyx | 74 ++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 62347579..13340117 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2060,8 +2060,10 @@ def test_fmpz_mod_poly(): assert f*f == f**fmpz(2) # Shifts - assert R_test([1,2,3]) << 3 == R_test([0,0,0,1,2,3]) - assert R_test([1,2,3]) >> 1 == R_test([2,3]) + assert raises(lambda: R_test([1,2,3]).left_shift(-1), ValueError) + assert raises(lambda: R_test([1,2,3]).right_shift(-1), ValueError) + assert R_test([1,2,3]).left_shift(3) == R_test([0,0,0,1,2,3]) + assert R_test([1,2,3]).right_shift(1) == R_test([2,3]) # Mod assert raises(lambda: f % f_bad, ValueError) @@ -2119,7 +2121,6 @@ def test_fmpz_mod_poly(): assert raises(lambda: (f_cmp).xgcd(f_cmp), ValueError) assert raises(lambda: (f).xgcd("f_cmp"), TypeError) - # disc. assert raises(lambda: (f_cmp).discriminant(), NotImplementedError) @@ -2147,10 +2148,10 @@ def test_fmpz_mod_poly(): assert raises(lambda: f1.sqrt(), ValueError) assert (f1*f1).sqrt() in [f1, -f1] - # deflate + # deflation f1 = R_test([1,0,2,0,3]) - assert raises(lambda: f1.deflate(100), ValueError) - assert f1.deflate(2) == R_test([1,2,3]) + assert raises(lambda: f1.deflation(100), ValueError) + assert f1.deflation(2) == R_test([1,2,3]) # factor ff = R_test([3,2,1]) * R_test([3,2,1]) * R_test([5,4,3]) @@ -2407,6 +2408,6 @@ def setbad(obj, i, val): test_arb, test_fmpz_mod, test_fmpz_mod_dlog, - test_fmpz_mod_poly + test_fmpz_mod_poly, test_polys, ] diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index f4e29fba..43fcf20b 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -510,52 +510,74 @@ cdef class fmpz_mod_poly(flint_poly): res.val, self.val, e_ulong, self.ctx.mod.val ) return res - - def shift(self, slong n): + + def left_shift(self, slong n): """ - Returns ``self`` shifted by ``n`` coefficients. If ``n`` is positive, - zero coefficients are inserted, when ``n`` is negative, if ``n`` is - greater than or equal to the length of ``self``, the zero polynomial - is returned. + Returns ``self`` shifted left by ``n`` coefficients by inserting + zero coefficients. This is equivalent to multiplying the polynomial + by x^n >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) - >>> f.shift(0) + >>> f.left_shift(0) 3*x^2 + 2*x + 1 - >>> f.shift(1) + >>> f.left_shift(1) 3*x^3 + 2*x^2 + x - >>> f.shift(4) + >>> f.left_shift(4) 3*x^6 + 2*x^5 + x^4 - >>> f.shift(-1) - 3*x + 2 - >>> f.shift(-4) - 0 """ cdef fmpz_mod_poly res res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = self.ctx + if n < 0: + raise ValueError("Value must be shifted by a non-negative integer") + if n > 0: fmpz_mod_poly_shift_left( res.val, self.val, n, self.ctx.mod.val ) - elif n < 0: - fmpz_mod_poly_shift_right( - res.val, self.val, -n, self.ctx.mod.val - ) else: # do nothing, just copy self fmpz_mod_poly_set( res.val, self.val, self.ctx.mod.val ) - return res + return res - def __lshift__(self, n): - return self.shift(n) + def right_shift(self, slong n): + """ + Returns ``self`` shifted right by ``n`` coefficients. + This is equivalent to the floor division of the polynomial + by x^n - def __rshift__(self, n): - return self.shift(-n) + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.right_shift(0) + 3*x^2 + 2*x + 1 + >>> f.right_shift(1) + 3*x + 2 + >>> f.right_shift(4) + 0 + """ + cdef fmpz_mod_poly res + + if n < 0: + raise ValueError("Value must be shifted by a non-negative integer") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + + if n > 0: + fmpz_mod_poly_shift_right( + res.val, self.val, n, self.ctx.mod.val + ) + else: # do nothing, just copy self + fmpz_mod_poly_set( + res.val, self.val, self.ctx.mod.val + ) + + return res @staticmethod def _mod_(left, right): @@ -1344,14 +1366,14 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def inflate(self, ulong n): + def inflation(self, ulong n): r""" Returns the result of the polynomial `f = \textrm{self}` to `f(x^n)` >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) - >>> f.inflate(10) + >>> f.inflation(10) 3*x^20 + 2*x^10 + 1 """ @@ -1364,7 +1386,7 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def deflate(self, ulong n): + def deflation(self, ulong n): r""" Returns the result of the polynomial `f = \textrm{self}` to `f(x^{1/n})` @@ -1373,7 +1395,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> f = R([1,0,2,0,3]) >>> f 3*x^4 + 2*x^2 + 1 - >>> f.deflate(2) + >>> f.deflation(2) 3*x^2 + 2*x + 1 """ From a793997dab8dfcb6bc829da2f57e337247487e4b Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 17:58:11 +0100 Subject: [PATCH 27/35] Include new type into generic test, but with some TODO --- src/flint/test/test.py | 60 +++++++++++++------ src/flint/types/fmpz_mod_poly.pyx | 98 ++++++++++++++++++++++++++----- 2 files changed, 125 insertions(+), 33 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 13340117..20e44619 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2043,7 +2043,7 @@ def test_fmpz_mod_poly(): assert raises(lambda: f / g, ValueError) # floor div - assert raises(lambda: 1 // f_bad, ValueError) + assert raises(lambda: 1 // f_bad, ZeroDivisionError) assert raises(lambda: f // f_cmp, ValueError) assert raises(lambda: f // "AAA", TypeError) assert raises(lambda: "AAA" // f, TypeError) @@ -2107,11 +2107,11 @@ def test_fmpz_mod_poly(): assert f.powmod(2, g) == (f*f) % g assert raises(lambda: f.powmod(2, "AAA"), TypeError) - # divrem - S, T = f.divrem(g) + # divmod + S, T = f.divmod(g) assert S*g + T == f - assert raises(lambda: f.divrem("AAA"), TypeError) - assert raises(lambda: f_bad.divrem(f_bad), ValueError) + assert raises(lambda: f.divmod("AAA"), TypeError) + assert raises(lambda: f_bad.divmod(f_bad), ValueError) # gcd assert raises(lambda: f_cmp.gcd(f_cmp), NotImplementedError) @@ -2150,8 +2150,8 @@ def test_fmpz_mod_poly(): # deflation f1 = R_test([1,0,2,0,3]) - assert raises(lambda: f1.deflation(100), ValueError) - assert f1.deflation(2) == R_test([1,2,3]) + assert raises(lambda: f1.deflate(100), ValueError) + assert f1.deflate(2) == R_test([1,2,3]) # factor ff = R_test([3,2,1]) * R_test([3,2,1]) * R_test([5,4,3]) @@ -2181,12 +2181,12 @@ def test_fmpz_mod_poly(): assert raises(lambda: R_test.minpoly([1,2,3,"AAA"]), ValueError) # multipoint_evaluation - assert raises(lambda: R_test([1,2,3]).multipoint_evaluation([1,2,3,"AAA"]), ValueError) - assert raises(lambda: R_test([1,2,3]).multipoint_evaluation("AAA"), ValueError) + assert raises(lambda: R_test([1,2,3]).multipoint_evaluate([1,2,3,"AAA"]), ValueError) + assert raises(lambda: R_test([1,2,3]).multipoint_evaluate("AAA"), ValueError) f = R_test([1,2,3]) l = [-1,-2,-3,-4,-5] - assert [f(x) for x in l] == f.multipoint_evaluation(l) + assert [f(x) for x in l] == f.multipoint_evaluate(l) def _all_polys(): @@ -2195,6 +2195,15 @@ def _all_polys(): (flint.fmpz_poly, flint.fmpz, False), (flint.fmpq_poly, flint.fmpq, True), (lambda *a: flint.nmod_poly(*a, 17), lambda x: flint.nmod(x, 17), True), + (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(163)), + lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(163)), + True), + (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**127 - 1)), + lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**127 - 1)), + True), + (lambda *a: flint.fmpz_mod_poly(*a, flint.fmpz_mod_poly_ctx(2**255 - 19)), + lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(2**255 - 19)), + True), ] @@ -2204,7 +2213,7 @@ def test_polys(): assert P([S(1)]) == P([1]) == P(P([1])) == P(1) assert raises(lambda: P([None]), TypeError) - assert raises(lambda: P(object()), TypeError) + assert raises(lambda: P(object()), TypeError), f"{P(object()) = }" assert raises(lambda: P(None), TypeError) assert raises(lambda: P(None, None), TypeError) assert raises(lambda: P([1,2], None), TypeError) @@ -2262,7 +2271,7 @@ def setbad(obj, i, val): assert P(v).repr() == f'fmpz_poly({v!r})' elif P == flint.fmpq_poly: assert P(v).repr() == f'fmpq_poly({v!r})' - else: + elif P == flint.nmod_poly: assert P(v).repr() == f'nmod_poly({v!r}, 17)' assert repr(P([])) == '0' @@ -2347,17 +2356,21 @@ def setbad(obj, i, val): else: assert raises(lambda: P([2, 2]) / 2, TypeError) - assert raises(lambda: 1 / P([1, 1]), TypeError) - assert raises(lambda: P([1, 2, 1]) / P([1, 1]), TypeError) - assert raises(lambda: P([1, 2, 1]) / P([1, 2]), TypeError) + # TODO: + # I think this should be a ValueError and not a Type Error? + # assert raises(lambda: 1 / P([1, 1]), TypeError) + # assert raises(lambda: P([1, 2, 1]) / P([1, 1]), TypeError) + # assert raises(lambda: P([1, 2, 1]) / P([1, 2]), TypeError) assert P([1, 1]) ** 0 == P([1]) assert P([1, 1]) ** 1 == P([1, 1]) assert P([1, 1]) ** 2 == P([1, 2, 1]) assert raises(lambda: P([1, 1]) ** -1, ValueError) assert raises(lambda: P([1, 1]) ** None, TypeError) - # XXX: Not sure what this should do in general: - assert raises(lambda: pow(P([1, 1]), 2, 3), NotImplementedError) + + # # XXX: Not sure what this should do in general: + # TODO: this test cannot work with how fmpz_mod_poly does pow... + # assert raises(lambda: pow(P([1, 1]), 2, 3), NotImplementedError) assert P([1, 2, 1]).gcd(P([1, 1])) == P([1, 1]) assert raises(lambda: P([1, 2, 1]).gcd(None), TypeError) @@ -2372,7 +2385,13 @@ def setbad(obj, i, val): assert P([1, 2, 1]).factor() == (S(1), [(P([1, 1]), 2)]) assert P([1, 2, 1]).sqrt() == P([1, 1]) - assert P([1, 2, 2]).sqrt() is None + + # TODO: diverging behaviour + # Most polynomials return None when therre's not root, + # but fmpz_mod_poly raises a ValueError + # assert raises(lambda: P([1, 2, 2]).sqrt(), ValueError) + # assert P([1, 2, 2]).sqrt() is None + if P == flint.fmpq_poly: assert P([1, 2, 1], 3).sqrt() is None assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) @@ -2383,7 +2402,10 @@ def setbad(obj, i, val): assert P([1, 2, 1]).derivative() == P([2, 2]) - if is_field: + # TODO: fmpz_mod_poly has no FLINT function for + # integration + has_integral = getattr(P, "integral", None) + if is_field and has_integral: assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 43fcf20b..79737cf1 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -221,7 +221,7 @@ cdef class fmpz_mod_poly_ctx: ) return 0 - + # Lastly try and convert to an fmpz obj = any_as_fmpz(obj) if obj is NotImplemented: return NotImplemented @@ -336,7 +336,9 @@ cdef class fmpz_mod_poly(flint_poly): if not typecheck(ctx, fmpz_mod_poly_ctx): raise TypeError self.ctx = ctx - self.ctx.set_any_as_fmpz_mod_poly(self.val, val) + check = self.ctx.set_any_as_fmpz_mod_poly(self.val, val) + if check is NotImplemented: + raise TypeError self.initialized = True def __pos__(self): @@ -442,6 +444,9 @@ cdef class fmpz_mod_poly(flint_poly): left = (right).ctx.any_as_fmpz_mod_poly(left) if left is NotImplemented: return NotImplemented + + if right == 0: + raise ZeroDivisionError(f"Cannot divide by zero") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = (left).ctx @@ -483,7 +488,7 @@ cdef class fmpz_mod_poly(flint_poly): return NotImplemented if not right.leading_coefficient().is_unit(): - raise ValueError(f"The leading term of {right} must be a unit modulo N") + raise ZeroDivisionError(f"The leading term of {right} must be a unit modulo N") res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = (left).ctx @@ -601,6 +606,9 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented + if right == 0: + raise ZeroDivisionError(f"Cannot reduce modulo zero") + res = fmpz_mod_poly.__new__(fmpz_mod_poly) res.ctx = (left).ctx fmpz_init(f) @@ -667,8 +675,18 @@ cdef class fmpz_mod_poly(flint_poly): return hash(tuple(self.coeffs())) def __call__(self, input): + if typecheck(input, fmpz_mod_poly): + return self.compose(input) + elif isinstance(input, (list, tuple)): + return self.multipoint_evaluate(input) + else: + return self.evalutate(input) + + def evalutate(self, input): + """ + TODO + """ cdef fmpz_mod res - val = self.ctx.mod.any_as_fmpz_mod(input) if val is NotImplemented: raise TypeError(f"Cannot evaluate the polynomial with input: {input}") @@ -677,8 +695,8 @@ cdef class fmpz_mod_poly(flint_poly): res.ctx = self.ctx.mod fmpz_mod_poly_evaluate_fmpz(res.val, self.val, (val).val, self.ctx.mod.val) return res - - def multipoint_evaluation(self, vals): + + def multipoint_evaluate(self, vals): """ Returns a list of values computed from evaluating ``self`` at the ``n`` values given in the vector ``val`` @@ -691,7 +709,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> f = R([1,2,3,4,5]) >>> [f(x) for x in [-1,-2,-3]] [fmpz_mod(3, 163), fmpz_mod(57, 163), fmpz_mod(156, 163)] - >>> f.multipoint_evaluation([-1,-2,-3]) + >>> f.multipoint_evaluate([-1,-2,-3]) [fmpz_mod(3, 163), fmpz_mod(57, 163), fmpz_mod(156, 163)] """ cdef fmpz_mod f @@ -725,6 +743,20 @@ cdef class fmpz_mod_poly(flint_poly): return evaluations + def compose(self, input): + """ + TODO + """ + cdef fmpz_mod_poly res + val = self.ctx.any_as_fmpz_mod_poly(input) + if val is NotImplemented: + raise TypeError(f"Cannot compose the polynomial with input: {input}") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_compose(res.val, self.val, (val).val, self.ctx.mod.val) + return res + cpdef long length(self): """ Return the length of the polynomial @@ -1050,7 +1082,7 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def divrem(self, other): + def divmod(self, other): """ Return `Q`, `R` such that for ``self`` = `F` and ``other`` = `G`, `F = Q*G + R` @@ -1058,7 +1090,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> R = fmpz_mod_poly_ctx(163) >>> f = R([123, 129, 63, 14, 51, 76, 133]) >>> g = R([106, 134, 32, 41, 158, 115, 115]) - >>> f.divrem(g) + >>> f.divmod(g) (21, 106*x^5 + 156*x^4 + 131*x^3 + 43*x^2 + 86*x + 16) """ cdef fmpz_t f @@ -1068,6 +1100,9 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") + if other == 0: + raise ZeroDivisionError(f"Cannot compute divmod as {other =}") + Q = fmpz_mod_poly.__new__(fmpz_mod_poly) R = fmpz_mod_poly.__new__(fmpz_mod_poly) Q.ctx = self.ctx @@ -1080,11 +1115,20 @@ cdef class fmpz_mod_poly(flint_poly): if not fmpz_is_one(f): fmpz_clear(f) raise ValueError( - f"Cannot compute divrem of {self} with {other}" + f"Cannot compute divmod of {self} with {other}" ) return Q, R + def __divmod__(self, other): + return self.divmod(other) + + def __rdivmod__(self, other): + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + return other + return other.divmod(self) + def gcd(self, other): """ Return the greatest common divisor of self and other. @@ -1366,14 +1410,14 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def inflation(self, ulong n): + def inflate(self, ulong n): r""" Returns the result of the polynomial `f = \textrm{self}` to `f(x^n)` >>> R = fmpz_mod_poly_ctx(163) >>> f = R([1,2,3]) - >>> f.inflation(10) + >>> f.inflate(10) 3*x^20 + 2*x^10 + 1 """ @@ -1386,7 +1430,7 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def deflation(self, ulong n): + def deflate(self, ulong n): r""" Returns the result of the polynomial `f = \textrm{self}` to `f(x^{1/n})` @@ -1395,7 +1439,7 @@ cdef class fmpz_mod_poly(flint_poly): >>> f = R([1,0,2,0,3]) >>> f 3*x^4 + 2*x^2 + 1 - >>> f.deflation(2) + >>> f.deflate(2) 3*x^2 + 2*x + 1 """ @@ -1414,6 +1458,32 @@ cdef class fmpz_mod_poly(flint_poly): ) return res + def deflation(self): + r""" + Returns the tuple (g, n) where for `f = \textrm{self}` to + `g = f(x^{1/n})` where n is the largest allowed integer + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,0,2,0,3]) + >>> f + 3*x^4 + 2*x^2 + 1 + >>> f.deflate(2) + 3*x^2 + 2*x + 1 + + """ + cdef fmpz_mod_poly res + if self.is_zero(): + return self, 1 + n = fmpz_mod_poly_deflation( + self.val, self.ctx.mod.val + ) + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_deflate( + res.val, self.val, n, res.ctx.mod.val + ) + return res, int(n) + def factor_squarefree(self): """ Factors self into irreducible, squarefree factors, returning a tuple From 8fd39d17ba96b9d0a31cfc9c8d2f00fef2a1c593 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 29 Sep 2023 18:53:50 +0100 Subject: [PATCH 28/35] Address TODO in tests and modify how _div_ works --- src/flint/test/test.py | 37 +++++--------- src/flint/types/fmpq_poly.pyx | 4 +- src/flint/types/fmpz_mod_poly.pyx | 82 ++++++++++++++++++++----------- src/flint/types/fmpz_poly.pyx | 2 +- src/flint/types/nmod_poly.pyx | 2 +- 5 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 20e44619..2dc6d633 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -437,7 +437,7 @@ def test_fmpz_poly(): assert Z([1,2,-4]).height_bits() == 3 assert Z([1,2,-4]).height_bits(signed=True) == -3 assert Z([1,2,1]).sqrt() == Z([1,1]) - assert Z([1,2,2]).sqrt() is None + assert raises(lambda: Z([1,2,2]).sqrt(), ValueError) assert Z([1,0,2,0,3]).deflation() == (Z([1,2,3]), 2) assert Z([]).deflation() == (Z([]), 1) assert Z([1,1]).deflation() == (Z([1,1]), 1) @@ -2031,16 +2031,16 @@ def test_fmpz_mod_poly(): assert fmpz(2) * f == R_test([-2,-4]) assert F_test(2) * f == R_test([-2,-4]) + # Exact division + assert raises(lambda: f.exact_division(f_cmp), ValueError) + assert (f * g).exact_division(g) == f + assert raises(lambda: f.exact_division(g), ValueError) + # true div - assert raises(lambda: f / f_cmp, ValueError) assert raises(lambda: f / "AAA", TypeError) - assert raises(lambda: "AAA" / f, TypeError) - assert (f * g) / g == f assert (f + f) / 2 == f assert (f + f) / fmpz(2) == f assert (f + f) / F_test(2) == f - assert 2 / R_test(2) == 1 - assert raises(lambda: f / g, ValueError) # floor div assert raises(lambda: 1 // f_bad, ZeroDivisionError) @@ -2356,11 +2356,9 @@ def setbad(obj, i, val): else: assert raises(lambda: P([2, 2]) / 2, TypeError) - # TODO: - # I think this should be a ValueError and not a Type Error? - # assert raises(lambda: 1 / P([1, 1]), TypeError) - # assert raises(lambda: P([1, 2, 1]) / P([1, 1]), TypeError) - # assert raises(lambda: P([1, 2, 1]) / P([1, 2]), TypeError) + assert raises(lambda: 1 / P([1, 1]), TypeError) + assert raises(lambda: P([1, 2, 1]) / P([1, 1]), TypeError) + assert raises(lambda: P([1, 2, 1]) / P([1, 2]), TypeError) assert P([1, 1]) ** 0 == P([1]) assert P([1, 1]) ** 1 == P([1, 1]) @@ -2369,8 +2367,7 @@ def setbad(obj, i, val): assert raises(lambda: P([1, 1]) ** None, TypeError) # # XXX: Not sure what this should do in general: - # TODO: this test cannot work with how fmpz_mod_poly does pow... - # assert raises(lambda: pow(P([1, 1]), 2, 3), NotImplementedError) + assert raises(lambda: pow(P([1, 1]), 2, 3), NotImplementedError) assert P([1, 2, 1]).gcd(P([1, 1])) == P([1, 1]) assert raises(lambda: P([1, 2, 1]).gcd(None), TypeError) @@ -2385,15 +2382,10 @@ def setbad(obj, i, val): assert P([1, 2, 1]).factor() == (S(1), [(P([1, 1]), 2)]) assert P([1, 2, 1]).sqrt() == P([1, 1]) - - # TODO: diverging behaviour - # Most polynomials return None when therre's not root, - # but fmpz_mod_poly raises a ValueError - # assert raises(lambda: P([1, 2, 2]).sqrt(), ValueError) - # assert P([1, 2, 2]).sqrt() is None + assert raises(lambda: P([1, 2, 2]).sqrt(), ValueError), f"{P}, {P([1, 2, 2]).sqrt()}" if P == flint.fmpq_poly: - assert P([1, 2, 1], 3).sqrt() is None + assert raises(lambda: P([1, 2, 1], 3).sqrt(), ValueError) assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) assert P([]).deflation() == (P([]), 1) @@ -2402,10 +2394,7 @@ def setbad(obj, i, val): assert P([1, 2, 1]).derivative() == P([2, 2]) - # TODO: fmpz_mod_poly has no FLINT function for - # integration - has_integral = getattr(P, "integral", None) - if is_field and has_integral: + if is_field: assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) diff --git a/src/flint/types/fmpq_poly.pyx b/src/flint/types/fmpq_poly.pyx index eec0bd58..19d4180c 100644 --- a/src/flint/types/fmpq_poly.pyx +++ b/src/flint/types/fmpq_poly.pyx @@ -409,10 +409,10 @@ cdef class fmpq_poly(flint_poly): n = self.numer() d, r = d.sqrtrem() if r != 0: - return None + raise ValueError(f"Cannot compute square root of {self}") n = n.sqrt() if n is None: - return None + raise ValueError(f"Cannot compute square root of {self}") return fmpq_poly(n, d) def deflation(self): diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 79737cf1..35bb4201 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -420,51 +420,59 @@ cdef class fmpz_mod_poly(flint_poly): def __rmul__(self, other): return self.__mul__(other) - @staticmethod - def _div_(left, right): - # TODO: - # Allow scalar division for efficiency, rather - # than casting `other` to a polynomial? + def exact_division(self, right): + """ + TODO + """ cdef bint check cdef fmpz_mod_poly res - # Case when left and right are already fmpz_mod_poly - if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly): - if not (left).ctx == (right).ctx: - raise ValueError("moduli must match") - # Case when right is not fmpz_mod_poly, try to convert to fmpz - elif typecheck(left, fmpz_mod_poly): - right = (left).ctx.any_as_fmpz_mod_poly(right) - if right is NotImplemented: - return NotImplemented + right = self.ctx.any_as_fmpz_mod_poly(right) + if right is NotImplemented: + return NotImplemented - # Case when left is not fmpz_mod_poly, try to convert to fmpz - else: - left = (right).ctx.any_as_fmpz_mod_poly(left) - if left is NotImplemented: - return NotImplemented - if right == 0: raise ZeroDivisionError(f"Cannot divide by zero") res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = (left).ctx + res.ctx = self.ctx check = fmpz_mod_poly_divides( - res.val, (left).val, (right).val, res.ctx.mod.val + res.val, self.val, (right).val, res.ctx.mod.val ) if check == 0: raise ValueError( - f"{right} does not divide {left}" + f"{right} does not divide {self}" ) return res - def __truediv__(s, t): + def _div_(self, other): + cdef fmpz_mod_poly res + + other = self.ctx.mod.any_as_fmpz_mod(other) + if other is NotImplemented: + return NotImplemented + + if other == 0: + raise ZeroDivisionError(f"Cannot divide by zero") + + if not other.is_unit(): + raise ZeroDivisionError(f"Cannot divide by {other} modulo {self.ctx.modulus()}") + + res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res.ctx = self.ctx + fmpz_mod_poly_scalar_div_fmpz( + res.val, self.val, (other).val, res.ctx.mod.val + ) + + return res + + def __div__(s, t): return fmpz_mod_poly._div_(s, t) - def __rtruediv__(s, t): - return fmpz_mod_poly._div_(t, s) + def __truediv__(s, t): + return fmpz_mod_poly._div_(s, t) @staticmethod def _floordiv_(left, right): @@ -503,7 +511,10 @@ cdef class fmpz_mod_poly(flint_poly): def __rfloordiv__(self, other): return fmpz_mod_poly._floordiv_(other, self) - def __pow__(self, e): + def __pow__(self, e, mod=None): + if mod is not None: + raise NotImplementedError + cdef fmpz_mod_poly res if e < 0: raise ValueError("Exponent must be non-negative") @@ -1217,6 +1228,21 @@ cdef class fmpz_mod_poly(flint_poly): ) return res + def integral(self): + """ + The formal integral of this polynomial. The constant term + from boundary conditions is picked to be zero. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> f = 118*x**3 + 11*x**2 + 33*x + 117 + >>> f.integral() + 111*x^4 + 58*x^3 + 98*x^2 + 117*x + + """ + new_coeffs = [0] + [c/n for n, c in enumerate(self.coeffs(), 1)] + return self.ctx(new_coeffs) + def discriminant(self): """ Return the discriminant of ``self``. @@ -1256,7 +1282,7 @@ cdef class fmpz_mod_poly(flint_poly): if not self.ctx.is_prime(): raise NotImplementedError("radical algorithm assumes that the base is a field") - return self / self.gcd(self.derivative()) + return self.exact_division(self.gcd(self.derivative())) def inverse_mod(self, other): """ diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index f56c4957..27253e48 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -512,7 +512,7 @@ cdef class fmpz_poly(flint_poly): if fmpz_poly_sqrt(v.val, self.val): return v else: - return None + raise ValueError(f"Cannot compute square root of {self}") def deflation(self): cdef fmpz_poly v diff --git a/src/flint/types/nmod_poly.pyx b/src/flint/types/nmod_poly.pyx index 73c02643..0ddaf66b 100644 --- a/src/flint/types/nmod_poly.pyx +++ b/src/flint/types/nmod_poly.pyx @@ -421,7 +421,7 @@ cdef class nmod_poly(flint_poly): if nmod_poly_sqrt(res.val, self.val): return res else: - return None + raise ValueError(f"Cannot compute square root of {self}") def deflation(self): cdef nmod_poly v From 3614a4226885e416f474f912fa1015602d1417c6 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 00:13:45 +0100 Subject: [PATCH 29/35] Hacky fix for memory? --- src/flint/types/fmpz_mod_poly.pyx | 137 +++++++++++------------------- 1 file changed, 49 insertions(+), 88 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 35bb4201..19470c20 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -74,8 +74,7 @@ cdef class fmpz_mod_poly_ctx: 0 """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) fmpz_mod_poly_zero(res.val, self.mod.val) return res @@ -88,8 +87,7 @@ cdef class fmpz_mod_poly_ctx: 1 """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) fmpz_mod_poly_one(res.val, self.mod.val) return res @@ -102,8 +100,7 @@ cdef class fmpz_mod_poly_ctx: x """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) fmpz_mod_poly_set_coeff_ui(res.val, 1, 1, self.mod.val) return res @@ -140,8 +137,7 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("The degree argument must be non-negative") cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) if (monic and irreducible): fmpz_mod_poly_randtest_monic_irreducible( res.val, global_random_state, length, self.mod.val @@ -238,12 +234,11 @@ cdef class fmpz_mod_poly_ctx: return obj cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) check = self.set_any_as_fmpz_mod_poly(res.val, obj) if check is NotImplemented: return NotImplemented - res.ctx = self return res def __eq__(self, other): @@ -301,8 +296,7 @@ cdef class fmpz_mod_poly_ctx: _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) _fmpz_vec_clear(xs, n) @@ -325,12 +319,14 @@ cdef class fmpz_mod_poly(flint_poly): 6*x^2 + 5*x + 4 """ - def __cinit__(self): - self.initialized = False + def __cinit__(self, val, ctx): + if not typecheck(ctx, fmpz_mod_poly_ctx): + raise TypeError + self.ctx = ctx + fmpz_mod_poly_init(self.val, self.ctx.mod.val) def __dealloc__(self): - if self.initialized: - fmpz_mod_poly_clear(self.val, self.ctx.mod.val) + fmpz_mod_poly_clear(self.val, self.ctx.mod.val) def __init__(self, val, ctx): if not typecheck(ctx, fmpz_mod_poly_ctx): @@ -346,8 +342,7 @@ cdef class fmpz_mod_poly(flint_poly): def __neg__(self): cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_neg(res.val, self.val, self.ctx.mod.val) return res @@ -357,11 +352,10 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: return other - res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_add( res.val, self.val, (other).val, self.ctx.mod.val ) - res.ctx = self.ctx return res def __radd__(self, other): @@ -388,8 +382,7 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = (left).ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) fmpz_mod_poly_sub( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -410,11 +403,10 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: return other - res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_mul( res.val, self.val, (other).val, self.ctx.mod.val ) - res.ctx = self.ctx return res def __rmul__(self, other): @@ -435,8 +427,7 @@ cdef class fmpz_mod_poly(flint_poly): if right == 0: raise ZeroDivisionError(f"Cannot divide by zero") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) check = fmpz_mod_poly_divides( res.val, self.val, (right).val, res.ctx.mod.val ) @@ -460,8 +451,7 @@ cdef class fmpz_mod_poly(flint_poly): if not other.is_unit(): raise ZeroDivisionError(f"Cannot divide by {other} modulo {self.ctx.modulus()}") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_scalar_div_fmpz( res.val, self.val, (other).val, res.ctx.mod.val ) @@ -498,8 +488,7 @@ cdef class fmpz_mod_poly(flint_poly): if not right.leading_coefficient().is_unit(): raise ZeroDivisionError(f"The leading term of {right} must be a unit modulo N") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = (left).ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) fmpz_mod_poly_div( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -520,8 +509,7 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Exponent must be non-negative") cdef ulong e_ulong = e - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_pow( res.val, self.val, e_ulong, self.ctx.mod.val ) @@ -544,8 +532,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) if n < 0: raise ValueError("Value must be shifted by a non-negative integer") @@ -581,8 +568,7 @@ cdef class fmpz_mod_poly(flint_poly): if n < 0: raise ValueError("Value must be shifted by a non-negative integer") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) if n > 0: fmpz_mod_poly_shift_right( @@ -620,8 +606,7 @@ cdef class fmpz_mod_poly(flint_poly): if right == 0: raise ZeroDivisionError(f"Cannot reduce modulo zero") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = (left).ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) fmpz_init(f) fmpz_mod_poly_rem_f( f, res.val, (left).val, (right).val, res.ctx.mod.val @@ -691,9 +676,9 @@ cdef class fmpz_mod_poly(flint_poly): elif isinstance(input, (list, tuple)): return self.multipoint_evaluate(input) else: - return self.evalutate(input) + return self.evaluate(input) - def evalutate(self, input): + def evaluate(self, input): """ TODO """ @@ -763,8 +748,7 @@ cdef class fmpz_mod_poly(flint_poly): if val is NotImplemented: raise TypeError(f"Cannot compose the polynomial with input: {input}") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_compose(res.val, self.val, (val).val, self.ctx.mod.val) return res @@ -892,8 +876,7 @@ cdef class fmpz_mod_poly(flint_poly): length = d + 1 - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res @@ -923,8 +906,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res length = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) if n <= 0: # return zero return res @@ -969,7 +951,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef fmpz_t f - res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) if not check: fmpz_mod_poly_make_monic( res.val, self.val, self.ctx.mod.val @@ -982,7 +964,6 @@ cdef class fmpz_mod_poly(flint_poly): if not fmpz_is_one(f): fmpz_clear(f) raise ValueError(f"Leading coefficient is not invertible") - res.ctx = self.ctx return res def is_irreducible(self): @@ -1027,8 +1008,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_sqr( res.val, self.val, self.ctx.mod.val ) @@ -1058,8 +1038,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_mulmod( res.val, self.val, (other).val, (modulus).val, res.ctx.mod.val @@ -1086,8 +1065,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_powmod_ui_binexp( res.val, self.val, e, (modulus).val, res.ctx.mod.val ) @@ -1114,10 +1092,8 @@ cdef class fmpz_mod_poly(flint_poly): if other == 0: raise ZeroDivisionError(f"Cannot compute divmod as {other =}") - Q = fmpz_mod_poly.__new__(fmpz_mod_poly) - R = fmpz_mod_poly.__new__(fmpz_mod_poly) - Q.ctx = self.ctx - R.ctx = self.ctx + Q = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + R = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_init(f) fmpz_mod_poly_divrem_f( @@ -1162,8 +1138,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_gcd( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -1190,13 +1165,9 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - G = fmpz_mod_poly.__new__(fmpz_mod_poly) - S = fmpz_mod_poly.__new__(fmpz_mod_poly) - T = fmpz_mod_poly.__new__(fmpz_mod_poly) - - G.ctx = self.ctx - S.ctx = self.ctx - T.ctx = self.ctx + G = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + S = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + T = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_init(f) fmpz_mod_poly_xgcd_f( @@ -1221,8 +1192,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_derivative( res.val, self.val, self.ctx.mod.val ) @@ -1302,7 +1272,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) res.ctx = self.ctx fmpz_init(f) fmpz_mod_poly_invmod_f( @@ -1331,8 +1301,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_t f cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_init(f) fmpz_mod_poly_inv_series_f( f, res.val, self.val, n, res.ctx.mod.val @@ -1381,8 +1350,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef int check - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) check = fmpz_mod_poly_sqrt( res.val, self.val, res.ctx.mod.val ) @@ -1408,8 +1376,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_sqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1429,8 +1396,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_invsqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1449,8 +1415,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_inflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1477,8 +1442,7 @@ cdef class fmpz_mod_poly(flint_poly): if n > n_max: raise ValueError(f"Cannot deflate with {n = }, maximum allowed value is {n_max = }") - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1503,8 +1467,7 @@ cdef class fmpz_mod_poly(flint_poly): n = fmpz_mod_poly_deflation( self.val, self.ctx.mod.val ) - res = fmpz_mod_poly.__new__(fmpz_mod_poly) - res.ctx = self.ctx + res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1538,8 +1501,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = fmpz_mod_poly.__new__(fmpz_mod_poly) - u.ctx = self.ctx + u = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) @@ -1582,8 +1544,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = fmpz_mod_poly.__new__(fmpz_mod_poly) - u.ctx = self.ctx + u = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) From 9dfd197d1551fefb880ce7ee5fa9ad83ba59a298 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 12:13:14 +0100 Subject: [PATCH 30/35] Simplify init function --- src/flint/types/fmpz_mod_poly.pxd | 1 - src/flint/types/fmpz_mod_poly.pyx | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 4239beb2..f2f755e8 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -11,7 +11,6 @@ cdef class fmpz_mod_poly_ctx: cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val) cdef class fmpz_mod_poly(flint_poly): - cdef bint initialized cdef fmpz_mod_poly_t val cdef fmpz_mod_poly_ctx ctx cpdef long length(self) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 19470c20..616eeb27 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -329,13 +329,9 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_clear(self.val, self.ctx.mod.val) def __init__(self, val, ctx): - if not typecheck(ctx, fmpz_mod_poly_ctx): - raise TypeError - self.ctx = ctx check = self.ctx.set_any_as_fmpz_mod_poly(self.val, val) if check is NotImplemented: - raise TypeError - self.initialized = True + raise TypeError def __pos__(self): return self From 1945859e4d9402d6ca675d0e29d9b6ea1f44e2c2 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 12:51:48 +0100 Subject: [PATCH 31/35] better handling of dealloc --- src/flint/types/fmpz_mod_poly.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 616eeb27..6f541d42 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -326,7 +326,8 @@ cdef class fmpz_mod_poly(flint_poly): fmpz_mod_poly_init(self.val, self.ctx.mod.val) def __dealloc__(self): - fmpz_mod_poly_clear(self.val, self.ctx.mod.val) + if self.ctx is not None: + fmpz_mod_poly_clear(self.val, self.ctx.mod.val) def __init__(self, val, ctx): check = self.ctx.set_any_as_fmpz_mod_poly(self.val, val) From 57b83467abcc355c513474734b267fd32cbfd3d8 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 19:35:44 +0100 Subject: [PATCH 32/35] Add new poly method to context --- src/flint/types/fmpz_mod_poly.pxd | 1 + src/flint/types/fmpz_mod_poly.pyx | 86 ++++++++++++++++--------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index f2f755e8..933ec35d 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -9,6 +9,7 @@ cdef class fmpz_mod_poly_ctx: cdef any_as_fmpz_mod_poly(self, obj) cdef set_any_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, obj) cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val) + cdef new_poly(self) cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_t val diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 6f541d42..1581fc08 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -74,7 +74,7 @@ cdef class fmpz_mod_poly_ctx: 0 """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() fmpz_mod_poly_zero(res.val, self.mod.val) return res @@ -87,7 +87,7 @@ cdef class fmpz_mod_poly_ctx: 1 """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() fmpz_mod_poly_one(res.val, self.mod.val) return res @@ -100,7 +100,7 @@ cdef class fmpz_mod_poly_ctx: x """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() fmpz_mod_poly_set_coeff_ui(res.val, 1, 1, self.mod.val) return res @@ -137,7 +137,7 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("The degree argument must be non-negative") cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() if (monic and irreducible): fmpz_mod_poly_randtest_monic_irreducible( res.val, global_random_state, length, self.mod.val @@ -156,7 +156,6 @@ cdef class fmpz_mod_poly_ctx: ) return res - cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val): cdef long i, n cdef fmpz_t x @@ -234,13 +233,16 @@ cdef class fmpz_mod_poly_ctx: return obj cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() check = self.set_any_as_fmpz_mod_poly(res.val, obj) if check is NotImplemented: return NotImplemented return res + cdef new_poly(self): + return fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + def __eq__(self, other): # Most often, we expect both `fmpz_mod_poly` to be pointing # to the same ctx, so this seems the fastest way to check @@ -296,7 +298,7 @@ cdef class fmpz_mod_poly_ctx: _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) + res = self.new_poly() fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) _fmpz_vec_clear(xs, n) @@ -339,7 +341,7 @@ cdef class fmpz_mod_poly(flint_poly): def __neg__(self): cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_neg(res.val, self.val, self.ctx.mod.val) return res @@ -349,7 +351,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: return other - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_add( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -379,7 +381,7 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) + res = (left).ctx.new_poly() fmpz_mod_poly_sub( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -400,7 +402,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: return other - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_mul( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -424,7 +426,7 @@ cdef class fmpz_mod_poly(flint_poly): if right == 0: raise ZeroDivisionError(f"Cannot divide by zero") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() check = fmpz_mod_poly_divides( res.val, self.val, (right).val, res.ctx.mod.val ) @@ -448,7 +450,7 @@ cdef class fmpz_mod_poly(flint_poly): if not other.is_unit(): raise ZeroDivisionError(f"Cannot divide by {other} modulo {self.ctx.modulus()}") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_scalar_div_fmpz( res.val, self.val, (other).val, res.ctx.mod.val ) @@ -485,7 +487,7 @@ cdef class fmpz_mod_poly(flint_poly): if not right.leading_coefficient().is_unit(): raise ZeroDivisionError(f"The leading term of {right} must be a unit modulo N") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) + res = (left).ctx.new_poly() fmpz_mod_poly_div( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -506,7 +508,7 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Exponent must be non-negative") cdef ulong e_ulong = e - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_pow( res.val, self.val, e_ulong, self.ctx.mod.val ) @@ -529,7 +531,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() if n < 0: raise ValueError("Value must be shifted by a non-negative integer") @@ -565,7 +567,7 @@ cdef class fmpz_mod_poly(flint_poly): if n < 0: raise ValueError("Value must be shifted by a non-negative integer") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() if n > 0: fmpz_mod_poly_shift_right( @@ -603,7 +605,7 @@ cdef class fmpz_mod_poly(flint_poly): if right == 0: raise ZeroDivisionError(f"Cannot reduce modulo zero") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, (left).ctx) + res = (left).ctx.new_poly() fmpz_init(f) fmpz_mod_poly_rem_f( f, res.val, (left).val, (right).val, res.ctx.mod.val @@ -745,7 +747,7 @@ cdef class fmpz_mod_poly(flint_poly): if val is NotImplemented: raise TypeError(f"Cannot compose the polynomial with input: {input}") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_compose(res.val, self.val, (val).val, self.ctx.mod.val) return res @@ -873,7 +875,7 @@ cdef class fmpz_mod_poly(flint_poly): length = d + 1 - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res @@ -903,7 +905,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res length = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() if n <= 0: # return zero return res @@ -948,7 +950,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef fmpz_t f - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() if not check: fmpz_mod_poly_make_monic( res.val, self.val, self.ctx.mod.val @@ -1005,7 +1007,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_sqr( res.val, self.val, self.ctx.mod.val ) @@ -1035,7 +1037,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_mulmod( res.val, self.val, (other).val, (modulus).val, res.ctx.mod.val @@ -1062,7 +1064,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_powmod_ui_binexp( res.val, self.val, e, (modulus).val, res.ctx.mod.val ) @@ -1089,8 +1091,8 @@ cdef class fmpz_mod_poly(flint_poly): if other == 0: raise ZeroDivisionError(f"Cannot compute divmod as {other =}") - Q = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) - R = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + Q = self.ctx.new_poly() + R = self.ctx.new_poly() fmpz_init(f) fmpz_mod_poly_divrem_f( @@ -1135,7 +1137,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_gcd( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -1162,9 +1164,9 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - G = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) - S = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) - T = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + G = self.ctx.new_poly() + S = self.ctx.new_poly() + T = self.ctx.new_poly() fmpz_init(f) fmpz_mod_poly_xgcd_f( @@ -1189,7 +1191,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_derivative( res.val, self.val, self.ctx.mod.val ) @@ -1269,7 +1271,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() res.ctx = self.ctx fmpz_init(f) fmpz_mod_poly_invmod_f( @@ -1298,7 +1300,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_t f cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_init(f) fmpz_mod_poly_inv_series_f( f, res.val, self.val, n, res.ctx.mod.val @@ -1347,7 +1349,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef int check - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() check = fmpz_mod_poly_sqrt( res.val, self.val, res.ctx.mod.val ) @@ -1373,7 +1375,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_sqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1393,7 +1395,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_invsqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1412,7 +1414,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_inflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1439,7 +1441,7 @@ cdef class fmpz_mod_poly(flint_poly): if n > n_max: raise ValueError(f"Cannot deflate with {n = }, maximum allowed value is {n_max = }") - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1464,7 +1466,7 @@ cdef class fmpz_mod_poly(flint_poly): n = fmpz_mod_poly_deflation( self.val, self.ctx.mod.val ) - res = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + res = self.ctx.new_poly() fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1498,7 +1500,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + u = self.ctx.new_poly() fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) @@ -1541,7 +1543,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = fmpz_mod_poly.__new__(fmpz_mod_poly, None, self.ctx) + u = self.ctx.new_poly() fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) From 6a3b4476cfb03aa58bf3433214212ad74bb4cea7 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 20:21:20 +0100 Subject: [PATCH 33/35] Return to 100% coverage --- src/flint/types/fmpz_mod_poly.pxd | 2 +- src/flint/types/fmpz_mod_poly.pyx | 193 ++++++++++++++++++------------ 2 files changed, 120 insertions(+), 75 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pxd b/src/flint/types/fmpz_mod_poly.pxd index 933ec35d..6b79278f 100644 --- a/src/flint/types/fmpz_mod_poly.pxd +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -9,7 +9,7 @@ cdef class fmpz_mod_poly_ctx: cdef any_as_fmpz_mod_poly(self, obj) cdef set_any_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, obj) cdef set_list_as_fmpz_mod_poly(self, fmpz_mod_poly_t poly, val) - cdef new_poly(self) + cdef new_ctype_poly(self) cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_t val diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 1581fc08..57255ab1 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -74,7 +74,7 @@ cdef class fmpz_mod_poly_ctx: 0 """ cdef fmpz_mod_poly res - res = self.new_poly() + res = self.new_ctype_poly() fmpz_mod_poly_zero(res.val, self.mod.val) return res @@ -87,7 +87,7 @@ cdef class fmpz_mod_poly_ctx: 1 """ cdef fmpz_mod_poly res - res = self.new_poly() + res = self.new_ctype_poly() fmpz_mod_poly_one(res.val, self.mod.val) return res @@ -100,7 +100,7 @@ cdef class fmpz_mod_poly_ctx: x """ cdef fmpz_mod_poly res - res = self.new_poly() + res = self.new_ctype_poly() fmpz_mod_poly_set_coeff_ui(res.val, 1, 1, self.mod.val) return res @@ -137,7 +137,7 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("The degree argument must be non-negative") cdef fmpz_mod_poly res - res = self.new_poly() + res = self.new_ctype_poly() if (monic and irreducible): fmpz_mod_poly_randtest_monic_irreducible( res.val, global_random_state, length, self.mod.val @@ -233,14 +233,14 @@ cdef class fmpz_mod_poly_ctx: return obj cdef fmpz_mod_poly res - res = self.new_poly() + res = self.new_ctype_poly() check = self.set_any_as_fmpz_mod_poly(res.val, obj) if check is NotImplemented: return NotImplemented return res - cdef new_poly(self): + cdef new_ctype_poly(self): return fmpz_mod_poly.__new__(fmpz_mod_poly, None, self) def __eq__(self, other): @@ -298,7 +298,7 @@ cdef class fmpz_mod_poly_ctx: _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") - res = self.new_poly() + res = self.new_ctype_poly() fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) _fmpz_vec_clear(xs, n) @@ -341,7 +341,7 @@ cdef class fmpz_mod_poly(flint_poly): def __neg__(self): cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_neg(res.val, self.val, self.ctx.mod.val) return res @@ -351,7 +351,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: return other - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_add( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -381,7 +381,7 @@ cdef class fmpz_mod_poly(flint_poly): if left is NotImplemented: return NotImplemented - res = (left).ctx.new_poly() + res = (left).ctx.new_ctype_poly() fmpz_mod_poly_sub( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -393,16 +393,36 @@ cdef class fmpz_mod_poly(flint_poly): def __rsub__(s, t): return fmpz_mod_poly._sub_(t, s) + def scalar_mul(self, other): + """ + Returns the polynomial equal to ``self`` scaled by the input + ``other`` + """ + cdef fmpz_mod_poly res + + other = self.ctx.mod.any_as_fmpz_mod(other) + if other is NotImplemented: + raise TypeError + + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_scalar_mul_fmpz( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res + def __mul__(self, other): - # TODO: - # Allow scalar multiplication for efficiency, rather - # than casting `other` to a polynomial? cdef fmpz_mod_poly res + + # If input is a scalar, use fastwe multiplication + if (typecheck(other, fmpz) or typecheck(other, fmpz_mod) or typecheck(other, int)): + return self.scalar_mul(other) + + # Otherwise perform polynomial multiplication other = self.ctx.any_as_fmpz_mod_poly(other) if other is NotImplemented: return other - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_mul( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -411,9 +431,40 @@ cdef class fmpz_mod_poly(flint_poly): def __rmul__(self, other): return self.__mul__(other) + def _div_(self, other): + cdef fmpz_mod_poly res + + other = self.ctx.mod.any_as_fmpz_mod(other) + if other is NotImplemented: + return NotImplemented + + if other == 0: + raise ZeroDivisionError(f"Cannot divide by zero") + + if not other.is_unit(): + raise ZeroDivisionError(f"Cannot divide by {other} modulo {self.ctx.modulus()}") + + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_scalar_div_fmpz( + res.val, self.val, (other).val, res.ctx.mod.val + ) + + return res + + def __truediv__(s, t): + return fmpz_mod_poly._div_(s, t) + def exact_division(self, right): """ - TODO + Attempt to compute the exact quotient of self with other + Raises a value error if divison without remainer is not + possible. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,1]) + >>> g = R([1,1]) + >>> f.exact_division(g) + x + 1 """ cdef bint check cdef fmpz_mod_poly res @@ -421,12 +472,12 @@ cdef class fmpz_mod_poly(flint_poly): # Case when right is not fmpz_mod_poly, try to convert to fmpz right = self.ctx.any_as_fmpz_mod_poly(right) if right is NotImplemented: - return NotImplemented + raise TypeError(f"Cannot convert {right} to `fmpz_mod_poly` type.") if right == 0: raise ZeroDivisionError(f"Cannot divide by zero") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() check = fmpz_mod_poly_divides( res.val, self.val, (right).val, res.ctx.mod.val ) @@ -437,32 +488,6 @@ cdef class fmpz_mod_poly(flint_poly): return res - def _div_(self, other): - cdef fmpz_mod_poly res - - other = self.ctx.mod.any_as_fmpz_mod(other) - if other is NotImplemented: - return NotImplemented - - if other == 0: - raise ZeroDivisionError(f"Cannot divide by zero") - - if not other.is_unit(): - raise ZeroDivisionError(f"Cannot divide by {other} modulo {self.ctx.modulus()}") - - res = self.ctx.new_poly() - fmpz_mod_poly_scalar_div_fmpz( - res.val, self.val, (other).val, res.ctx.mod.val - ) - - return res - - def __div__(s, t): - return fmpz_mod_poly._div_(s, t) - - def __truediv__(s, t): - return fmpz_mod_poly._div_(s, t) - @staticmethod def _floordiv_(left, right): cdef fmpz_mod_poly res @@ -487,7 +512,7 @@ cdef class fmpz_mod_poly(flint_poly): if not right.leading_coefficient().is_unit(): raise ZeroDivisionError(f"The leading term of {right} must be a unit modulo N") - res = (left).ctx.new_poly() + res = (left).ctx.new_ctype_poly() fmpz_mod_poly_div( res.val, (left).val, (right).val, res.ctx.mod.val ) @@ -508,7 +533,7 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Exponent must be non-negative") cdef ulong e_ulong = e - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_pow( res.val, self.val, e_ulong, self.ctx.mod.val ) @@ -531,7 +556,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() if n < 0: raise ValueError("Value must be shifted by a non-negative integer") @@ -567,7 +592,7 @@ cdef class fmpz_mod_poly(flint_poly): if n < 0: raise ValueError("Value must be shifted by a non-negative integer") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() if n > 0: fmpz_mod_poly_shift_right( @@ -605,7 +630,7 @@ cdef class fmpz_mod_poly(flint_poly): if right == 0: raise ZeroDivisionError(f"Cannot reduce modulo zero") - res = (left).ctx.new_poly() + res = (left).ctx.new_ctype_poly() fmpz_init(f) fmpz_mod_poly_rem_f( f, res.val, (left).val, (right).val, res.ctx.mod.val @@ -679,7 +704,16 @@ cdef class fmpz_mod_poly(flint_poly): def evaluate(self, input): """ - TODO + Evaluate ``self`` at a point in the base ring. This is + the same as calling the polynomial directly. To evaluate + a list of points, use ``multiploint_evaluate``. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3,4,5,6]) + >>> f.evaluate(-1) + fmpz_mod(160, 163) + >>> f.evaluate(-1) == f(-1) + True """ cdef fmpz_mod res val = self.ctx.mod.any_as_fmpz_mod(input) @@ -740,14 +774,25 @@ cdef class fmpz_mod_poly(flint_poly): def compose(self, input): """ - TODO + Returns the composition of two polynomials + + To be precise about the order of composition, given ``self``, and ``input`` + by `f(x)`, `g(x)`, returns `f(g(x))`. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> g = R([0,0,1]) + >>> f.compose(g) + 3*x^4 + 2*x^2 + 1 + >>> g.compose(f) + 9*x^4 + 12*x^3 + 10*x^2 + 4*x + 1 """ cdef fmpz_mod_poly res val = self.ctx.any_as_fmpz_mod_poly(input) if val is NotImplemented: raise TypeError(f"Cannot compose the polynomial with input: {input}") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_compose(res.val, self.val, (val).val, self.ctx.mod.val) return res @@ -875,7 +920,7 @@ cdef class fmpz_mod_poly(flint_poly): length = d + 1 - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val) return res @@ -905,7 +950,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res length = fmpz_mod_poly_degree(self.val, self.ctx.mod.val) - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() if n <= 0: # return zero return res @@ -950,7 +995,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef fmpz_t f - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() if not check: fmpz_mod_poly_make_monic( res.val, self.val, self.ctx.mod.val @@ -1007,7 +1052,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_sqr( res.val, self.val, self.ctx.mod.val ) @@ -1037,7 +1082,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_mulmod( res.val, self.val, (other).val, (modulus).val, res.ctx.mod.val @@ -1064,7 +1109,7 @@ cdef class fmpz_mod_poly(flint_poly): if modulus is NotImplemented: raise TypeError(f"Cannot interpret {modulus} as a polynomial") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_powmod_ui_binexp( res.val, self.val, e, (modulus).val, res.ctx.mod.val ) @@ -1091,8 +1136,8 @@ cdef class fmpz_mod_poly(flint_poly): if other == 0: raise ZeroDivisionError(f"Cannot compute divmod as {other =}") - Q = self.ctx.new_poly() - R = self.ctx.new_poly() + Q = self.ctx.new_ctype_poly() + R = self.ctx.new_ctype_poly() fmpz_init(f) fmpz_mod_poly_divrem_f( @@ -1137,7 +1182,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_gcd( res.val, self.val, (other).val, self.ctx.mod.val ) @@ -1164,9 +1209,9 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - G = self.ctx.new_poly() - S = self.ctx.new_poly() - T = self.ctx.new_poly() + G = self.ctx.new_ctype_poly() + S = self.ctx.new_ctype_poly() + T = self.ctx.new_ctype_poly() fmpz_init(f) fmpz_mod_poly_xgcd_f( @@ -1191,7 +1236,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_derivative( res.val, self.val, self.ctx.mod.val ) @@ -1271,7 +1316,7 @@ cdef class fmpz_mod_poly(flint_poly): if other is NotImplemented: raise TypeError(f"Cannot interpret {other} as a polynomial") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() res.ctx = self.ctx fmpz_init(f) fmpz_mod_poly_invmod_f( @@ -1300,7 +1345,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_t f cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_init(f) fmpz_mod_poly_inv_series_f( f, res.val, self.val, n, res.ctx.mod.val @@ -1349,7 +1394,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly res cdef int check - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() check = fmpz_mod_poly_sqrt( res.val, self.val, res.ctx.mod.val ) @@ -1375,7 +1420,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_sqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1395,7 +1440,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_invsqrt_series( res.val, self.val, n, res.ctx.mod.val ) @@ -1414,7 +1459,7 @@ cdef class fmpz_mod_poly(flint_poly): """ cdef fmpz_mod_poly res - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_inflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1441,7 +1486,7 @@ cdef class fmpz_mod_poly(flint_poly): if n > n_max: raise ValueError(f"Cannot deflate with {n = }, maximum allowed value is {n_max = }") - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1466,7 +1511,7 @@ cdef class fmpz_mod_poly(flint_poly): n = fmpz_mod_poly_deflation( self.val, self.ctx.mod.val ) - res = self.ctx.new_poly() + res = self.ctx.new_ctype_poly() fmpz_mod_poly_deflate( res.val, self.val, n, res.ctx.mod.val ) @@ -1500,7 +1545,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = self.ctx.new_poly() + u = self.ctx.new_ctype_poly() fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) @@ -1543,7 +1588,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly u for i in range(fac.num): - u = self.ctx.new_poly() + u = self.ctx.new_ctype_poly() fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val) exp = fac.exp[i] res[i] = (u, exp) From 52dee640f24074a185e9c8304bcc4b0377cb861d Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sat, 30 Sep 2023 22:57:26 +0100 Subject: [PATCH 34/35] Forgot to push test commit --- src/flint/test/test.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 2dc6d633..fb2746d7 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2031,13 +2031,23 @@ def test_fmpz_mod_poly(): assert fmpz(2) * f == R_test([-2,-4]) assert F_test(2) * f == R_test([-2,-4]) + # scalar_mul + assert 2 * f == f.scalar_mul(2) + assert raises(lambda: f.scalar_mul("AAA"), TypeError) + # Exact division assert raises(lambda: f.exact_division(f_cmp), ValueError) + assert raises(lambda: f.exact_division("AAA"), TypeError) + assert raises(lambda: f.exact_division(0), ZeroDivisionError) + assert (f * g).exact_division(g) == f assert raises(lambda: f.exact_division(g), ValueError) # true div assert raises(lambda: f / "AAA", TypeError) + assert raises(lambda: f / 0, ZeroDivisionError) + assert raises(lambda: f_cmp / 2, ZeroDivisionError) + assert (f + f) / 2 == f assert (f + f) / fmpz(2) == f assert (f + f) / F_test(2) == f @@ -2085,6 +2095,10 @@ def test_fmpz_mod_poly(): h = R_test([0, 0, 1]) assert h(1) == h(-1) assert raises(lambda: h("AAA"), TypeError) + assert f([-1,-2,-3]) == [f(x) for x in [-1, -2, -3]] + + # compose + assert raises(lambda: h.compose("AAA"), TypeError) # Reverse assert raises(lambda: h.reverse(degree=-100), ValueError) From b2543b40c83094eaf37e29341db64b21aa603a3e Mon Sep 17 00:00:00 2001 From: giacomopope Date: Sun, 1 Oct 2023 01:18:15 +0100 Subject: [PATCH 35/35] Fix docstring types and initalise int type --- src/flint/types/fmpz_mod_poly.pyx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index 57255ab1..5f875f0a 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -834,8 +834,8 @@ cdef class fmpz_mod_poly(flint_poly): def is_one(self): """ - Return ``True`` if the polynomial is the zero polynomial - and `Fal`se` otherwise + Return ``True`` if the polynomial is equal to one + and ``False`` otherwise >>> R = fmpz_mod_poly_ctx(163) >>> f = R(1) @@ -846,8 +846,8 @@ cdef class fmpz_mod_poly(flint_poly): def is_gen(self): """ - Return ``True`` if the polynomial is the zero polynomial - and ``False`` otherwise + Return ``True`` if the polynomial is the generator + of the polynomial, `x`, and ``False`` otherwise >>> R = fmpz_mod_poly_ctx(163) >>> f = R([0,1]) @@ -1610,6 +1610,7 @@ cdef class fmpz_mod_poly(flint_poly): cdef fmpz_mod_poly_factor_t fac cdef int i, with_multiplicity + with_multiplicity = 0 if multiplicities: with_multiplicity = 1