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 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/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 4e33ae6e..fbb61478 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): diff --git a/src/flint/flintlib/fmpz_mod_poly.pxd b/src/flint/flintlib/fmpz_mod_poly.pxd new file mode 100644 index 00000000..d241e0df --- /dev/null +++ b/src/flint/flintlib/fmpz_mod_poly.pxd @@ -0,0 +1,339 @@ +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 + # 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) + 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) + + # 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) + 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) + + # 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) + 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) + + # 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) + 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) + + # 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) + 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) + + # 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) + 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_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 + 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) + 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) + + # 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) + 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/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) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 542d6739..c22e5f48 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -52,10 +52,14 @@ 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, flint.types.fmpz_series, + flint.types.fmpz_mod, + flint.types.fmpz_mod_poly, flint.types.fmpq, flint.types.fmpq_poly, flint.types.fmpq_mat, diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 71bcea10..fb2746d7 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: @@ -435,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) @@ -1591,10 +1593,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) @@ -1622,6 +1631,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) @@ -1640,6 +1656,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 @@ -1697,6 +1714,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 @@ -1776,17 +1795,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) @@ -1813,6 +1832,376 @@ 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, fmpz_mod_poly_ctx, fmpz_mod_ctx, fmpz + + # 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 != 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 + 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 + + 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)) + 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" + + # 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) + + + # 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 + 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]) + f3 = R([0, 1]) + + assert f1.is_zero() is True + 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]) + + # 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 + + # floor div + 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) + 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 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) + 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) + 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) + 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() + + # 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) + + # powmod + assert f.powmod(2, g) == (f*f) % g + assert raises(lambda: f.powmod(2, "AAA"), TypeError) + + # divmod + S, T = f.divmod(g) + assert S*g + T == f + 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) + 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) + + # 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] + + # 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]) + + # 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(), 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(), 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_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_evaluate(l) + def _all_polys(): return [ @@ -1820,6 +2209,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), ] @@ -1829,7 +2227,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) @@ -1887,7 +2285,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' @@ -1981,7 +2379,8 @@ def setbad(obj, i, val): 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: + + # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P([1, 1]), 2, 3), NotImplementedError) assert P([1, 2, 1]).gcd(P([1, 1])) == P([1, 1]) @@ -1997,9 +2396,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]) - 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) @@ -2033,5 +2433,6 @@ def setbad(obj, i, val): test_arb, test_fmpz_mod, test_fmpz_mod_dlog, + test_fmpz_mod_poly, test_polys, ] 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.pxd b/src/flint/types/fmpz_mod.pxd index 11b68928..81938672 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -1,14 +1,15 @@ 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 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 686d21e4..687f0eee 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 * @@ -39,6 +42,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): @@ -52,7 +56,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 @@ -62,6 +66,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 +84,59 @@ 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 + + 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 @@ -89,28 +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 - # TODO: we could allow conversion from one modulus to another? + 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): @@ -126,7 +200,7 @@ 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): @@ -167,28 +241,15 @@ 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): 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): """ @@ -209,7 +270,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 """ @@ -217,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` @@ -252,7 +329,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,19 +346,13 @@ 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): - 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 @@ -342,18 +413,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(self, fmpz_mod) and 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.pxd b/src/flint/types/fmpz_mod_poly.pxd new file mode 100644 index 00000000..6b79278f --- /dev/null +++ b/src/flint/types/fmpz_mod_poly.pxd @@ -0,0 +1,18 @@ +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 new_ctype_poly(self) + +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..5f875f0a --- /dev/null +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -0,0 +1,1646 @@ +from cpython.list cimport PyList_GET_SIZE + +from flint.pyflint cimport global_random_state +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.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 +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""" + 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) + + """ + 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 + + >>> R = fmpz_mod_poly_ctx(2**127 - 1) + >>> R.modulus() + 170141183460469231731687303715884105727 + + """ + 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): + """ + Return the zero element of this polynomial ring + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.zero() + 0 + """ + cdef fmpz_mod_poly res + res = self.new_ctype_poly() + fmpz_mod_poly_zero(res.val, self.mod.val) + return res + + def one(self): + """ + Return the one element of this polynomial ring + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.one() + 1 + """ + cdef fmpz_mod_poly res + res = self.new_ctype_poly() + fmpz_mod_poly_one(res.val, self.mod.val) + return res + + def gen(self): + """ + Return the generator of the polynomial: `x` + + >>> R = fmpz_mod_poly_ctx(163) + >>> R.gen() + x + """ + cdef fmpz_mod_poly res + res = self.new_ctype_poly() + 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``. 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() + >>> 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 = self.new_ctype_poly() + 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 + + 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(f"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) + + # 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: + 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 + + # Lastly try and convert to an fmpz + 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 != (obj).ctx: + raise ValueError("moduli must match") + return obj + + cdef fmpz_mod_poly res + 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_ctype_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 + 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 self.mod == (other).mod + return False + + def __hash__(self): + return hash(self.modulus()) + + 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) + + 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 = _fmpz_vec_init(n) + for i in range(n): + 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`") + + res = self.new_ctype_poly() + fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) + + _fmpz_vec_clear(xs, n) + 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_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: + + >>> 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, 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.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) + if check is NotImplemented: + raise TypeError + + def __pos__(self): + return self + + def __neg__(self): + cdef fmpz_mod_poly res + res = self.ctx.new_ctype_poly() + 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 = self.ctx.new_ctype_poly() + fmpz_mod_poly_add( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res + + def __radd__(self, other): + return self.__add__(other) + + @staticmethod + def _sub_(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 + + res = (left).ctx.new_ctype_poly() + 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 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): + 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_ctype_poly() + fmpz_mod_poly_mul( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res + + 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): + """ + 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 + + # 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: + 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_ctype_poly() + check = fmpz_mod_poly_divides( + res.val, self.val, (right).val, res.ctx.mod.val + ) + if check == 0: + raise ValueError( + f"{right} does not divide {self}" + ) + + return res + + @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 ZeroDivisionError(f"The leading term of {right} must be a unit modulo N") + + res = (left).ctx.new_ctype_poly() + fmpz_mod_poly_div( + res.val, (left).val, (right).val, res.ctx.mod.val + ) + return res + + def __floordiv__(self, other): + return fmpz_mod_poly._floordiv_(self, other) + + def __rfloordiv__(self, other): + return fmpz_mod_poly._floordiv_(other, self) + + 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") + + cdef ulong e_ulong = e + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_pow( + res.val, self.val, e_ulong, self.ctx.mod.val + ) + return res + + def left_shift(self, slong n): + """ + 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.left_shift(0) + 3*x^2 + 2*x + 1 + >>> f.left_shift(1) + 3*x^3 + 2*x^2 + x + >>> f.left_shift(4) + 3*x^6 + 2*x^5 + x^4 + + """ + cdef fmpz_mod_poly res + res = self.ctx.new_ctype_poly() + + 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 + ) + else: # do nothing, just copy self + fmpz_mod_poly_set( + res.val, self.val, self.ctx.mod.val + ) + + return res + + 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 + + >>> 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 = self.ctx.new_ctype_poly() + + 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): + 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 + + if right == 0: + raise ZeroDivisionError(f"Cannot reduce modulo zero") + + 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 + ) + if not fmpz_is_one(f): + fmpz_clear(f) + raise ValueError( + f"Cannot compute remainder of {left} modulo {right}" + ) + + return res + + def __mod__(s, t): + return fmpz_mod_poly._mod_(s, t) + + def __rmod__(s, t): + return fmpz_mod_poly._mod_(t, s) + + 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(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.evaluate(input) + + def evaluate(self, input): + """ + 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) + if val is NotImplemented: + raise TypeError(f"Cannot evaluate the polynomial with input: {input}") + + 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 + + def multipoint_evaluate(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 + + >>> 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_evaluate([-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 = _fmpz_vec_init(n) + for i in range(n): + 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 = _fmpz_vec_init(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) + + _fmpz_vec_clear(xs, n) + _fmpz_vec_clear(ys, n) + + return evaluations + + def compose(self, input): + """ + 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_ctype_poly() + 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 + + >>> 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 + + >>> 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) + + def is_one(self): + """ + Return ``True`` if the polynomial is equal to one + 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) + + def is_gen(self): + """ + 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]) + >>> f.is_gen() + True + """ + return 0 != fmpz_mod_poly_is_gen(self.val, self.ctx.mod.val) + + def is_constant(self): + """ + Return ``True`` if this is a constant polynomial. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> x.is_constant() + False + >>> R(123).is_constant() + True + """ + return self.degree() <= 0 + + def constant_coefficient(self): + """ + Return the constant coefficient of this polynomial. + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,2,3]) + >>> f.constant_coefficient() + fmpz_mod(1, 163) + """ + return self[0] + + 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 reverse(self, degree=None): + """ + 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 + + 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 + + res = self.ctx.new_ctype_poly() + 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) == 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) + res = self.ctx.new_ctype_poly() + + if n <= 0: # return zero + return res + 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): + """ + 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): + """ + 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 + coefficient is not invertible, the output is undefined. + + >>> 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 = self.ctx.new_ctype_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): + fmpz_clear(f) + raise ValueError(f"Leading coefficient is not invertible") + return res + + def is_irreducible(self): + """ + Return whether this polynomial is irreducible. + + >>> R = fmpz_mod_poly_ctx(163) + >>> x = R.gen() + >>> 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 square(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 + + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_sqr( + res.val, self.val, self.ctx.mod.val + ) + return res + + def mulmod(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.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(f"Cannot interpret {other} as a polynomial") + + modulus = self.ctx.any_as_fmpz_mod_poly(modulus) + if modulus is NotImplemented: + raise TypeError(f"Cannot interpret {modulus} as a polynomial") + + res = self.ctx.new_ctype_poly() + + fmpz_mod_poly_mulmod( + res.val, self.val, (other).val, (modulus).val, res.ctx.mod.val + ) + return res + + def powmod(self, e, modulus): + """ + Returns ``self`` raised to the power ``e`` modulo ``modulus``: + :math:`f^e \mod g` + + >>> 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.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(f"Cannot interpret {modulus} as a polynomial") + + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_powmod_ui_binexp( + res.val, self.val, e, (modulus).val, res.ctx.mod.val + ) + return res + + def divmod(self, other): + """ + 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]) + >>> g = R([106, 134, 32, 41, 158, 115, 115]) + >>> f.divmod(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: + raise TypeError(f"Cannot interpret {other} as a polynomial") + + if other == 0: + raise ZeroDivisionError(f"Cannot compute divmod as {other =}") + + Q = self.ctx.new_ctype_poly() + R = self.ctx.new_ctype_poly() + + 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): + fmpz_clear(f) + raise ValueError( + 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. + + >>> 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: + raise TypeError(f"Cannot interpret {other} as a polynomial") + + res = self.ctx.new_ctype_poly() + fmpz_mod_poly_gcd( + res.val, self.val, (other).val, self.ctx.mod.val + ) + return res + + def xgcd(self, other): + 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: + + :math:`G = \textrm{self}*S + \textrm{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) + (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 + cdef fmpz_t f + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + raise TypeError(f"Cannot interpret {other} as a polynomial") + + 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( + 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}" + ) + return (G, S, T) + + 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 = self.ctx.new_ctype_poly() + fmpz_mod_poly_derivative( + res.val, self.val, self.ctx.mod.val + ) + 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``. + + >>> 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 + + """ + if not self.ctx.is_prime(): + raise NotImplementedError("radical algorithm assumes that the base is a field") + + return self.exact_division(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: + raise TypeError(f"Cannot interpret {other} as a polynomial") + + res = self.ctx.new_ctype_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): + fmpz_clear(f) + raise ValueError( + f"Cannot compute inverse 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_trunc(3) + 159*x^2 + 151*x + 110 + >>> f.inverse_series_trunc(4) + 23*x^3 + 159*x^2 + 151*x + 110 + >>> f.inverse_series_trunc(5) + 45*x^4 + 23*x^3 + 159*x^2 + 151*x + 110 + """ + cdef fmpz_t f + cdef fmpz_mod_poly res + + 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 + ) + 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, other): + """ + Returns the resultant of ``self`` with ``other``. + + >>> 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) + + """ + cdef fmpz_mod res + + other = self.ctx.any_as_fmpz_mod_poly(other) + if other is NotImplemented: + raise TypeError(f"Cannot interpret {other} as a polynomial") + + 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 sqrt(self): + """ + If ``self`` is a perfect square, compute the square root + + >>> R = fmpz_mod_poly_ctx(163) + >>> f = R([1,1]) + >>> (f*f).sqrt() + x + 1 + + """ + cdef fmpz_mod_poly res + cdef int check + + res = self.ctx.new_ctype_poly() + 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 = self.ctx.new_ctype_poly() + 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 = self.ctx.new_ctype_poly() + 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 = self.ctx.new_ctype_poly() + 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 = self.ctx.new_ctype_poly() + fmpz_mod_poly_deflate( + res.val, self.val, n, res.ctx.mod.val + ) + 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 = self.ctx.new_ctype_poly() + 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 + ``(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() + >>> 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) + + res = [0] * fac.num + + cdef fmpz_mod_poly u + for i in range(fac.num): + 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) + return self.leading_coefficient(), res + + 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. + + >>> 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 + + 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": + 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 = 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) + return self.leading_coefficient(), res + + def roots(self, multiplicities=True): + 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() + >>> 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 + + with_multiplicity = 0 + 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): + """ + This method is not implemented for polynomials in + :math:`(\mathbb{Z}/N\mathbb{Z})[X]` + """ + raise DomainError("Cannot compute compex roots for polynomials over integers modulo N") 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 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