From 5a84520dd08e275b39d35235d1d5c5feaf01c72a Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 16:37:51 +0100 Subject: [PATCH 01/33] Add new type to package --- setup.py | 1 + src/flint/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/setup.py b/setup.py index d7df0a0b..1972be10 100644 --- a/setup.py +++ b/setup.py @@ -94,6 +94,7 @@ ("flint.types.acb_mat", ["src/flint/types/acb_mat.pyx"]), ("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.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 74b306a0..52dc79a9 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -21,6 +21,7 @@ from .types.acb_mat import * from .types.acb_series import * from .types.fmpz_mpoly import * +from .types.fmpz_mod import * from .functions.showgood import showgood __version__ = '0.4.4' From 9fbe93a60238debece6164767b8429ce906b0f6c Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 16:38:07 +0100 Subject: [PATCH 02/33] Include additional struct --- src/flint/flintlib/fmpz.pxd | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/flint/flintlib/fmpz.pxd b/src/flint/flintlib/fmpz.pxd index 4779537d..4decaaa9 100644 --- a/src/flint/flintlib/fmpz.pxd +++ b/src/flint/flintlib/fmpz.pxd @@ -1,11 +1,14 @@ -from flint.flintlib.flint cimport fmpz_struct, ulong, mp_limb_t +from flint.flintlib.flint cimport fmpz_struct, ulong, mp_limb_t, mp_ptr from flint.flintlib.flint cimport mp_size_t, mp_bitcnt_t, slong, flint_rand_t, flint_bitcnt_t -# from flint.flintlib.nmod cimport nmod_t -# from flint.flintlib.fmpz_factor cimport fmpz_factor_t cdef extern from "flint/fmpz.h": ctypedef fmpz_struct fmpz_t[1] + ctypedef struct fmpz_preinvn_struct: + mp_ptr dinv + slong n + flint_bitcnt_t norm + ctypedef fmpz_preinvn_struct fmpz_preinvn_t[1] # from here on is parsed # fmpz_struct PTR_TO_COEFF(__mpz_struct * ptr) From 12a87eab40af4fc359926d0d952b33e771a83da7 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 16:38:15 +0100 Subject: [PATCH 03/33] Tidy up --- src/flint/types/nmod.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/flint/types/nmod.pyx b/src/flint/types/nmod.pyx index c0a01bb8..53bee07b 100644 --- a/src/flint/types/nmod.pyx +++ b/src/flint/types/nmod.pyx @@ -47,9 +47,6 @@ cdef class nmod(flint_scalar): """ - # cdef mp_limb_t val - # cdef nmod_t mod - def __init__(self, val, mod): cdef mp_limb_t m m = mod From b7b3df319f39d81814f8da0ab70ac1b0ea09c88b Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 16:38:33 +0100 Subject: [PATCH 04/33] Level 0 functions for fmpz_mod --- src/flint/flintlib/fmpz_mod.pxd | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/flint/flintlib/fmpz_mod.pxd diff --git a/src/flint/flintlib/fmpz_mod.pxd b/src/flint/flintlib/fmpz_mod.pxd new file mode 100644 index 00000000..a96a0e3a --- /dev/null +++ b/src/flint/flintlib/fmpz_mod.pxd @@ -0,0 +1,45 @@ +from flint.flintlib.flint cimport ulong, slong +from flint.flintlib.fmpz cimport fmpz_t, fmpz_preinvn_struct +from flint.flintlib.nmod cimport nmod_t + +# unimported types {'fmpz_mod_discrete_log_pohlig_hellman_t'} + +cdef extern from "flint/fmpz_mod.h": + ctypedef struct fmpz_mod_ctx_struct: + fmpz_t n + nmod_t mod + ulong n_limbs[3] + ulong ninv_limbs[3] + fmpz_preinvn_struct * ninv_huge + ctypedef fmpz_mod_ctx_struct fmpz_mod_ctx_t[1] + + # Parsed from here + void fmpz_mod_ctx_init(fmpz_mod_ctx_t ctx, const fmpz_t n) + void fmpz_mod_ctx_clear(fmpz_mod_ctx_t ctx) + void fmpz_mod_ctx_set_modulus(fmpz_mod_ctx_t ctx, const fmpz_t n) + void fmpz_mod_set_fmpz(fmpz_t a, const fmpz_t b, const fmpz_mod_ctx_t ctx) + int fmpz_mod_is_canonical(const fmpz_t a, const fmpz_mod_ctx_t ctx) + int fmpz_mod_is_one(const fmpz_t a, const fmpz_mod_ctx_t ctx) + void fmpz_mod_add(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_add_fmpz(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_add_ui(fmpz_t a, const fmpz_t b, ulong c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_add_si(fmpz_t a, const fmpz_t b, slong c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_sub(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_sub_fmpz(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_sub_ui(fmpz_t a, const fmpz_t b, ulong c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_sub_si(fmpz_t a, const fmpz_t b, slong c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_fmpz_sub(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_ui_sub(fmpz_t a, ulong b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_si_sub(fmpz_t a, slong b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_neg(fmpz_t a, const fmpz_t b, const fmpz_mod_ctx_t ctx) + void fmpz_mod_mul(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_inv(fmpz_t a, const fmpz_t b, const fmpz_mod_ctx_t ctx) + int fmpz_mod_divides(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_pow_ui(fmpz_t a, const fmpz_t b, ulong e, const fmpz_mod_ctx_t ctx) + int fmpz_mod_pow_fmpz(fmpz_t a, const fmpz_t b, const fmpz_t e, const fmpz_mod_ctx_t ctx) + # void fmpz_mod_discrete_log_pohlig_hellman_init(fmpz_mod_discrete_log_pohlig_hellman_t L) + # void fmpz_mod_discrete_log_pohlig_hellman_clear(fmpz_mod_discrete_log_pohlig_hellman_t L) + # double fmpz_mod_discrete_log_pohlig_hellman_precompute_prime(fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t p) + # const fmpz_struct * fmpz_mod_discrete_log_pohlig_hellman_primitive_root(const fmpz_mod_discrete_log_pohlig_hellman_t L) + # void fmpz_mod_discrete_log_pohlig_hellman_run(fmpz_t x, const fmpz_mod_discrete_log_pohlig_hellman_t L, const fmpz_t y) + int fmpz_next_smooth_prime(fmpz_t a, const fmpz_t b) From 548107aa66325991bbb115d34ca9a48392011b4f Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 16:38:50 +0100 Subject: [PATCH 05/33] Start working on context for fmpz_mod --- src/flint/types/fmpz_mod.pxd | 11 ++++++++ src/flint/types/fmpz_mod.pyx | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/flint/types/fmpz_mod.pxd create mode 100644 src/flint/types/fmpz_mod.pyx diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd new file mode 100644 index 00000000..1e07a318 --- /dev/null +++ b/src/flint/types/fmpz_mod.pxd @@ -0,0 +1,11 @@ +from flint.flint_base.flint_base cimport flint_scalar +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t + +cdef class fmpz_mod_ctx: + cdef fmpz_mod_ctx_t val + +cdef class fmpz_mod(flint_scalar): + cdef fmpz_t val + cdef fmpz_mod_ctx ctx + diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx new file mode 100644 index 00000000..91567df5 --- /dev/null +++ b/src/flint/types/fmpz_mod.pyx @@ -0,0 +1,49 @@ +from flint.flint_base.flint_base cimport flint_scalar +from flint.flintlib.fmpz_mod cimport ( + fmpz_mod_ctx_init, + fmpz_mod_ctx_set_modulus, +) + +from flint.types.fmpz cimport fmpz, any_as_fmpz + +cdef class fmpz_mod_ctx: + """ + """ + def __init__(self, n): + """ + """ + # Ensure modulus is fmpz type + mod = any_as_fmpz(n) + if mod is NotImplemented: + raise NotImplementedError("TODO") + + # Ensure modulus is positive + if mod < 1: + raise ValueError("Modulus is expected to be positive") + + # Init the context + fmpz_mod_ctx_init(self.val, (mod).val) + + def __repr__(self): + # return "Context for fmpz_mod with modulus: %s" % self.ctx.n + return "Stuff: (%s, %s, %s)" % (self.val.n, self.val.mod, self.val.n_limbs) + + def __str__(self): + return "fmpz_mod_ctx(%s)" % self.val.mod + + def set_modulus(self, n): + """ + """ + # Ensure modulus is fmpz type + mod = any_as_fmpz(n) + if mod is NotImplemented: + raise NotImplementedError("TODO") + + # Ensure modulus is positive + if mod < 1: + raise ValueError("Modulus is expected to be positive") + + fmpz_mod_ctx_set_modulus(self.val, (mod).val) + +cdef class fmpz_mod(flint_scalar): + pass From 7e7dc8744915b989441f30b3a641bf579a7797d5 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 18:39:13 +0100 Subject: [PATCH 06/33] Small progress --- src/flint/types/fmpz_mod.pxd | 3 +- src/flint/types/fmpz_mod.pyx | 58 ++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd index 1e07a318..36892243 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -2,10 +2,11 @@ from flint.flint_base.flint_base cimport flint_scalar from flint.flintlib.fmpz cimport fmpz_t from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t + cdef class fmpz_mod_ctx: cdef fmpz_mod_ctx_t val cdef class fmpz_mod(flint_scalar): - cdef fmpz_t val cdef fmpz_mod_ctx ctx + cdef fmpz_t val diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 91567df5..1c70e3e3 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -1,10 +1,38 @@ -from flint.flint_base.flint_base cimport flint_scalar +from flint.flintlib.flint cimport slong +from flint.flintlib.fmpz cimport ( + fmpz_t, + COEFF_IS_MPZ, + fmpz_get_str +) from flint.flintlib.fmpz_mod cimport ( + fmpz_mod_ctx_t, fmpz_mod_ctx_init, fmpz_mod_ctx_set_modulus, + fmpz_mod_set_fmpz +) +from flint.utils.typecheck cimport typecheck +from flint.utils.conversion cimport chars_from_str, str_from_chars, _str_trunc +from flint.flint_base.flint_base cimport flint_scalar +from flint.types.fmpz cimport ( + fmpz, + any_as_fmpz, ) -from flint.types.fmpz cimport fmpz, any_as_fmpz +cimport libc.stdlib + +# TODO: import this from types.fmpz somehow +cdef fmpz_get_intlong(fmpz_t x): + """ + Convert fmpz_t to a Python int or long. + """ + cdef char * s + if COEFF_IS_MPZ(x[0]): + s = fmpz_get_str(NULL, 16, x) + v = int(str_from_chars(s), 16) + libc.stdlib.free(s) + return v + else: + return x[0] cdef class fmpz_mod_ctx: """ @@ -25,11 +53,14 @@ cdef class fmpz_mod_ctx: fmpz_mod_ctx_init(self.val, (mod).val) def __repr__(self): - # return "Context for fmpz_mod with modulus: %s" % self.ctx.n - return "Stuff: (%s, %s, %s)" % (self.val.n, self.val.mod, self.val.n_limbs) + return "Context for fmpz_mod with modulus: {}".format( + fmpz_get_intlong(self.val.n) + ) def __str__(self): - return "fmpz_mod_ctx(%s)" % self.val.mod + return "fmpz_mod_ctx({})".format( + fmpz_get_intlong(self.val.n) + ) def set_modulus(self, n): """ @@ -45,5 +76,20 @@ cdef class fmpz_mod_ctx: fmpz_mod_ctx_set_modulus(self.val, (mod).val) +# This is buggy and broken cdef class fmpz_mod(flint_scalar): - pass + def __init__(self, val, ctx): + self.ctx = ctx + + val = any_as_fmpz(val) + assert typecheck(val, fmpz) + fmpz_mod_set_fmpz(self.val, (val).val, (self.ctx)) + + def repr(self): + return "fmpz_mod(%s, %s)" % (self.val, self.mod.n) + + def str(self): + return str(fmpz_get_intlong(self.val)) + + def __int__(self): + return fmpz_get_intlong(self.val) From 59cfb05faa6af81255ba6926b9eb94d833bc741f Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 19:02:41 +0100 Subject: [PATCH 07/33] clean up context --- src/flint/types/fmpz_mod.pyx | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 1c70e3e3..721f6b23 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -1,6 +1,7 @@ from flint.flintlib.flint cimport slong from flint.flintlib.fmpz cimport ( fmpz_t, + fmpz_set, COEFF_IS_MPZ, fmpz_get_str ) @@ -51,15 +52,23 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) + + def modulus(self): + """ + Return the modulus from the context as an fmpz + """ + n = fmpz() + fmpz_set(n.val, (self.val.n)) + return n def __repr__(self): return "Context for fmpz_mod with modulus: {}".format( - fmpz_get_intlong(self.val.n) + self.modulus() ) def __str__(self): return "fmpz_mod_ctx({})".format( - fmpz_get_intlong(self.val.n) + self.modulus() ) def set_modulus(self, n): @@ -81,14 +90,17 @@ cdef class fmpz_mod(flint_scalar): def __init__(self, val, ctx): self.ctx = ctx - val = any_as_fmpz(val) - assert typecheck(val, fmpz) - fmpz_mod_set_fmpz(self.val, (val).val, (self.ctx)) + val_fmpz = any_as_fmpz(val) + assert typecheck(val_fmpz, fmpz) + fmpz_mod_set_fmpz(self.val, (val_fmpz).val, (self.ctx)) - def repr(self): - return "fmpz_mod(%s, %s)" % (self.val, self.mod.n) + def __repr__(self): + return "fmpz_mod({}, {})".format( + fmpz_get_intlong(self.val), + self.ctx.modulus() + ) - def str(self): + def __str__(self): return str(fmpz_get_intlong(self.val)) def __int__(self): From 0ecf9a002c66f9f14fe369888c3773f3bd97dbc2 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 19:26:07 +0100 Subject: [PATCH 08/33] Got initalisation somewhat working --- src/flint/types/fmpz_mod.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 721f6b23..8ba0172a 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -92,7 +92,7 @@ cdef class fmpz_mod(flint_scalar): val_fmpz = any_as_fmpz(val) assert typecheck(val_fmpz, fmpz) - fmpz_mod_set_fmpz(self.val, (val_fmpz).val, (self.ctx)) + fmpz_mod_set_fmpz(self.val, (val_fmpz).val, (self.ctx.val)) def __repr__(self): return "fmpz_mod({}, {})".format( From bd09992e8cffb70c4ee2d805f1d4bc185f310f80 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 21:57:46 +0100 Subject: [PATCH 09/33] Add ability to create type from context --- src/flint/types/fmpz_mod.pyx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 8ba0172a..c7c35260 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -71,6 +71,16 @@ cdef class fmpz_mod_ctx: self.modulus() ) + def __call__(self, val): + if not typecheck(val, fmpz): + val = any_as_fmpz(val) + if val is NotImplemented: + raise NotImplementedError("TODO") + + return fmpz_mod(val, self) + + # TODO: should this be allowed, or should + # we make a ctx immutatble? def set_modulus(self, n): """ """ @@ -85,14 +95,17 @@ cdef class fmpz_mod_ctx: fmpz_mod_ctx_set_modulus(self.val, (mod).val) -# This is buggy and broken + cdef class fmpz_mod(flint_scalar): def __init__(self, val, ctx): self.ctx = ctx - val_fmpz = any_as_fmpz(val) - assert typecheck(val_fmpz, fmpz) - fmpz_mod_set_fmpz(self.val, (val_fmpz).val, (self.ctx.val)) + if not typecheck(val, fmpz): + val = any_as_fmpz(val) + if val is NotImplemented: + raise NotImplementedError("TODO") + + fmpz_mod_set_fmpz(self.val, (val).val, (self.ctx.val)) def __repr__(self): return "fmpz_mod({}, {})".format( From 06f00d1200b606a0ce564b674a5705ef1fb1d8d4 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 23:29:54 +0100 Subject: [PATCH 10/33] Add fmpz_mod tests --- src/flint/test/test.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 9c9dc8fe..56e60767 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1584,6 +1584,52 @@ def test_pickling(): obj2 = pickle.loads(s) assert obj == obj2 +def test_fmpz_mod(): + from flint import fmpz_mod_ctx + + # Context tests + F163 = fmpz_mod_ctx(163) + assert F163.modulus() == 163 + big = 2**1024 - 1 + Fbig = fmpz_mod_ctx(2**1024 - 1) + assert Fbig.modulus() == big + + # Rich comparisons + assert raises(lambda: F163(123) > 0, TypeError) + assert raises(lambda: F163(123) >= 0, TypeError) + assert raises(lambda: F163(123) < 0, TypeError) + assert raises(lambda: F163(123) <= 0, TypeError) + + assert F163(123) == 123 + assert F163(123) == 123 + 163 + assert F163(123) != 1 + assert F163(123) == F163(123) + assert F163(123) == F163(123 + 163) + assert F163(123) != F163(1) + + # Is one, zero, canoncial + assert F163(0) == 0 + assert F163(0).is_zero() + assert not F163(0) + assert not F163(163) + assert F163(1).is_one() + assert F163(164).is_one() + assert not F163(2).is_one() + assert F163(123).is_canonical() + + # Arithmetic tests + + # Negation + assert -F163(123) == F163(-123) == (-123 % 163) + assert -F163(1) == F163(-1) == F163(162) + + # Addition + assert F163(123) + F163(456) == F163(123 + 456) + assert F163(123) + F163(456) == F163(456) + F163(123) + test_inplace = F163(123) + test_inplace += F163(456) + assert test_inplace == F163(123 + 456) + all_tests = [ test_pyflint, @@ -1603,4 +1649,5 @@ def test_pickling(): test_nmod_poly, test_nmod_mat, test_arb, + test_fmpz_mod, ] From 9f010933fe82acbc0de6ea8b30baba98fd41170d Mon Sep 17 00:00:00 2001 From: giacomopope Date: Thu, 14 Sep 2023 23:30:06 +0100 Subject: [PATCH 11/33] Add comparisons and start arithmetic --- src/flint/types/fmpz_mod.pyx | 125 +++++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 14 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index c7c35260..0138061c 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -5,14 +5,10 @@ from flint.flintlib.fmpz cimport ( COEFF_IS_MPZ, fmpz_get_str ) -from flint.flintlib.fmpz_mod cimport ( - fmpz_mod_ctx_t, - fmpz_mod_ctx_init, - fmpz_mod_ctx_set_modulus, - fmpz_mod_set_fmpz -) +from flint.flintlib.fmpz_mod cimport * + from flint.utils.typecheck cimport typecheck -from flint.utils.conversion cimport chars_from_str, str_from_chars, _str_trunc +from flint.utils.conversion cimport str_from_chars from flint.flint_base.flint_base cimport flint_scalar from flint.types.fmpz cimport ( fmpz, @@ -21,7 +17,8 @@ from flint.types.fmpz cimport ( cimport libc.stdlib -# TODO: import this from types.fmpz somehow +# TODO: import this from types.fmpz somehow, it's not good +# to have the function repeated here. cdef fmpz_get_intlong(fmpz_t x): """ Convert fmpz_t to a Python int or long. @@ -38,13 +35,14 @@ cdef fmpz_get_intlong(fmpz_t x): cdef class fmpz_mod_ctx: """ """ - def __init__(self, n): + def __init__(self, mod): """ """ # Ensure modulus is fmpz type - mod = any_as_fmpz(n) - if mod is NotImplemented: - raise NotImplementedError("TODO") + if not typecheck(mod, fmpz): + mod = any_as_fmpz(mod) + if mod is NotImplemented: + raise NotImplementedError("TODO") # Ensure modulus is positive if mod < 1: @@ -56,6 +54,7 @@ cdef class fmpz_mod_ctx: def modulus(self): """ Return the modulus from the context as an fmpz + type """ n = fmpz() fmpz_set(n.val, (self.val.n)) @@ -107,14 +106,112 @@ cdef class fmpz_mod(flint_scalar): fmpz_mod_set_fmpz(self.val, (val).val, (self.ctx.val)) + def is_zero(self): + return self == 0 + + # TODO: kind of pointless, as we always ensure canonical on init? + def is_canonical(self): + cdef bint res + res = fmpz_mod_is_canonical(self.val, (self.ctx.val)) + return res == 1 + + def is_one(self): + cdef bint res + res = fmpz_mod_is_one(self.val, (self.ctx.val)) + return res == 1 + + def __richcmp__(s, t, int op): + cdef bint res + if op != 2 and op != 3: + raise TypeError("fmpz_mod cannot be ordered") + # TODO: is this the best method for comparison? + if typecheck(s, fmpz_mod) and typecheck(t, fmpz_mod): + res = ((s).val[0] == (t).val[0]) and \ + ((s).ctx.modulus() == (t).ctx.modulus()) + if op == 2: + return res + else: + return not res + # TODO: is this the best method for comparison? + elif typecheck(s, fmpz_mod) and typecheck(t, int): + res = int(s) == t % (s).ctx.modulus() + if op == 2: + return res + else: + return not res + return NotImplemented + + def __bool__(self): + return not self.is_zero() + def __repr__(self): return "fmpz_mod({}, {})".format( fmpz_get_intlong(self.val), self.ctx.modulus() ) - def __str__(self): - return str(fmpz_get_intlong(self.val)) + # TODO: seems ugly... + def __hash__(self): + return hash( + (int(self), int(self.ctx.modulus())) + ) def __int__(self): return fmpz_get_intlong(self.val) + + def __str__(self): + return str(int(self)) + + # ---------------- # + # Arithmetic # + # ---------------- # + + def __neg__(self): + res = fmpz() + fmpz_mod_neg( + res.val, self.val, + (self.ctx.val)) + return self.ctx(res) + + # TODO: proper type handing for the other... + def __add__(self, other): + res = fmpz() + fmpz_mod_add( + res.val, self.val, (other).val, + (self.ctx.val) + ) + return self.ctx(res) + + def __radd__(self, other): + return other + self + + def __iadd__(self, other): + fmpz_mod_add( + self.val, self.val, (other).val, + (self.ctx.val) + ) + return self + + def __sub__(self, other): + pass + + def __rsub__(self, other): + pass + + def __isub__(self, other): + pass + + def __mul__(self, other): + pass + + def __rmul__(self, other): + pass + + def __imul__(self, other): + pass + + def __truediv__(self, other): + pass + + def __floordiv__(self, other): + raise NotImplemented From 6f58268c15fa1c44a1dfa063f63e461a78d3d630 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 00:12:16 +0100 Subject: [PATCH 12/33] Modify comparison tests following feedback --- src/flint/test/test.py | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 56e60767..c2bae1c4 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1595,27 +1595,35 @@ def test_fmpz_mod(): assert Fbig.modulus() == big # Rich comparisons - assert raises(lambda: F163(123) > 0, TypeError) - assert raises(lambda: F163(123) >= 0, TypeError) - assert raises(lambda: F163(123) < 0, TypeError) - assert raises(lambda: F163(123) <= 0, TypeError) - - assert F163(123) == 123 - assert F163(123) == 123 + 163 - assert F163(123) != 1 - assert F163(123) == F163(123) - assert F163(123) == F163(123 + 163) - assert F163(123) != F163(1) + assert (F163(123) == 123) is True + assert (F163(123) == 123 + 163) is True + assert (F163(123) == 1) is False + assert (F163(123) != 1) is True + + assert (F163(123) == F163(123)) is True + assert (F163(123) == F163(123 + 163)) is True + assert (F163(123) == F163(1)) is False + assert (F163(123) != F163(1)) is True + + assert (Fbig(123) == 123) is True + assert (Fbig(123) == 123 + big) is True + assert (Fbig(123) == 1) is False + assert (Fbig(123) != 1) is True + assert (Fbig(123) == Fbig(123)) is True + assert (Fbig(123) == Fbig(123 + big)) is True + assert (Fbig(123) == Fbig(1)) is False + assert (Fbig(123) != Fbig(1)) is True # Is one, zero, canoncial - assert F163(0) == 0 - assert F163(0).is_zero() + assert (F163(0) == 0) is True + assert F163(0).is_zero() is True assert not F163(0) assert not F163(163) - assert F163(1).is_one() - assert F163(164).is_one() - assert not F163(2).is_one() - assert F163(123).is_canonical() + assert F163(1).is_one() is True + assert F163(164).is_one() is True + assert F163(1).is_one() is True + assert F163(2).is_one() is False + assert F163(123).is_canonical() is True # Arithmetic tests From f32fdfaa496e5799a24ddd215baa434da7e140a2 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 00:20:55 +0100 Subject: [PATCH 13/33] allow other types in addition --- src/flint/types/fmpz_mod.pyx | 42 ++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 0138061c..dc87bbc5 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -176,18 +176,52 @@ cdef class fmpz_mod(flint_scalar): # TODO: proper type handing for the other... def __add__(self, other): res = fmpz() - fmpz_mod_add( - res.val, self.val, (other).val, + + # Add two fmpz_mod if moduli match + if typecheck(other, fmpz_mod): + if not (self).ctx.modulus() == (other).ctx.modulus(): + raise ValueError("moduli must match") + + fmpz_mod_add( + res.val, self.val, (other).val, + (self.ctx.val) + ) + return self.ctx(res) + + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + # Add an fmpz to an fmpz_mod + fmpz_mod_add_fmpz( + res.val, self.val, (other).val, (self.ctx.val) ) + return self.ctx(res) def __radd__(self, other): return other + self def __iadd__(self, other): - fmpz_mod_add( - self.val, self.val, (other).val, + # Add two fmpz_mod if moduli match + if typecheck(other, fmpz_mod): + if not (self).ctx.modulus() == (other).ctx.modulus(): + raise ValueError("moduli must match") + + fmpz_mod_add( + self.val, self.val, (other).val, + (self.ctx.val) + ) + return self + + # Add an fmpz to an fmpz_mod + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + fmpz_mod_add_fmpz( + self.val, self.val, (other).val, (self.ctx.val) ) return self From 79db695c2ff390c36750d1a57a93e796a772f00c Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 00:21:06 +0100 Subject: [PATCH 14/33] update tests --- src/flint/test/test.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index c2bae1c4..d50e1db9 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1585,7 +1585,7 @@ def test_pickling(): assert obj == obj2 def test_fmpz_mod(): - from flint import fmpz_mod_ctx + from flint import fmpz_mod_ctx, fmpz # Context tests F163 = fmpz_mod_ctx(163) @@ -1634,9 +1634,20 @@ def test_fmpz_mod(): # Addition assert F163(123) + F163(456) == F163(123 + 456) assert F163(123) + F163(456) == F163(456) + F163(123) + assert F163(123) + 456 == F163(123 + 456) + assert F163(123) + fmpz(456) == F163(456) + F163(123) + test_inplace = F163(123) test_inplace += F163(456) assert test_inplace == F163(123 + 456) + + test_inplace = F163(123) + test_inplace += 456 + assert test_inplace == F163(123 + 456) + + test_inplace = F163(123) + test_inplace += fmpz(456) + assert test_inplace == F163(123 + 456) all_tests = [ From 717acf481c1073b28cac62a973664f15bfed2eff Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 00:23:23 +0100 Subject: [PATCH 15/33] add test to ensure correct error for mismatched moduli --- src/flint/test/test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index d50e1db9..07b24ed2 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1636,6 +1636,7 @@ def test_fmpz_mod(): assert F163(123) + F163(456) == F163(456) + F163(123) assert F163(123) + 456 == F163(123 + 456) assert F163(123) + fmpz(456) == F163(456) + F163(123) + assert raises(lambda: F163(123) + Fbig(456), ValueError) test_inplace = F163(123) test_inplace += F163(456) From 7a8f7e5b23597b265d23b369e499e41ab717d4d3 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 11:42:45 +0100 Subject: [PATCH 16/33] First draft of fmpz_mod --- src/flint/types/fmpz_mod.pyx | 266 +++++++++++++++++++++++++++++------ 1 file changed, 221 insertions(+), 45 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index dc87bbc5..90c5c716 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -1,6 +1,7 @@ from flint.flintlib.flint cimport slong from flint.flintlib.fmpz cimport ( fmpz_t, + fmpz_one, fmpz_set, COEFF_IS_MPZ, fmpz_get_str @@ -51,6 +52,7 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) + # TODO: should this be cached if we make immutable? def modulus(self): """ Return the modulus from the context as an fmpz @@ -78,22 +80,6 @@ cdef class fmpz_mod_ctx: return fmpz_mod(val, self) - # TODO: should this be allowed, or should - # we make a ctx immutatble? - def set_modulus(self, n): - """ - """ - # Ensure modulus is fmpz type - mod = any_as_fmpz(n) - if mod is NotImplemented: - raise NotImplementedError("TODO") - - # Ensure modulus is positive - if mod < 1: - raise ValueError("Modulus is expected to be positive") - - fmpz_mod_ctx_set_modulus(self.val, (mod).val) - cdef class fmpz_mod(flint_scalar): def __init__(self, val, ctx): @@ -104,7 +90,7 @@ cdef class fmpz_mod(flint_scalar): if val is NotImplemented: raise NotImplementedError("TODO") - fmpz_mod_set_fmpz(self.val, (val).val, (self.ctx.val)) + fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) def is_zero(self): return self == 0 @@ -112,12 +98,12 @@ cdef class fmpz_mod(flint_scalar): # TODO: kind of pointless, as we always ensure canonical on init? def is_canonical(self): cdef bint res - res = fmpz_mod_is_canonical(self.val, (self.ctx.val)) + res = fmpz_mod_is_canonical(self.val, self.ctx.val) return res == 1 def is_one(self): cdef bint res - res = fmpz_mod_is_one(self.val, (self.ctx.val)) + res = fmpz_mod_is_one(self.val, self.ctx.val) return res == 1 def __richcmp__(s, t, int op): @@ -166,52 +152,56 @@ cdef class fmpz_mod(flint_scalar): # Arithmetic # # ---------------- # + def __pos__(self): + return self + def __neg__(self): - res = fmpz() + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx + fmpz_mod_neg( res.val, self.val, (self.ctx.val)) - return self.ctx(res) + return res - # TODO: proper type handing for the other... def __add__(self, other): - res = fmpz() + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx # Add two fmpz_mod if moduli match if typecheck(other, fmpz_mod): - if not (self).ctx.modulus() == (other).ctx.modulus(): + if not self.ctx.val.n == (other).ctx.val.n: raise ValueError("moduli must match") fmpz_mod_add( - res.val, self.val, (other).val, - (self.ctx.val) + res.val, self.val, (other).val, self.ctx.val ) - return self.ctx(res) + return res + # Attempt to add an fmpz to an fmpz_mod other = any_as_fmpz(other) if other is NotImplemented: raise NotImplementedError - # Add an fmpz to an fmpz_mod fmpz_mod_add_fmpz( - res.val, self.val, (other).val, - (self.ctx.val) + res.val, self.val, (other).val, self.ctx.val ) - return self.ctx(res) + return res def __radd__(self, other): - return other + self + return self.__add__(other) def __iadd__(self, other): # Add two fmpz_mod if moduli match if typecheck(other, fmpz_mod): - if not (self).ctx.modulus() == (other).ctx.modulus(): + if not self.ctx.val.n == (other).ctx.val.n: raise ValueError("moduli must match") fmpz_mod_add( - self.val, self.val, (other).val, - (self.ctx.val) + self.val, self.val, (other).val, self.ctx.val ) return self @@ -221,31 +211,217 @@ cdef class fmpz_mod(flint_scalar): raise NotImplementedError fmpz_mod_add_fmpz( - self.val, self.val, (other).val, - (self.ctx.val) + self.val, self.val, (other).val, self.ctx.val ) return self def __sub__(self, other): - pass + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx + + # Sub two fmpz_mod if moduli match + if typecheck(other, fmpz_mod): + if not self.ctx.val.n == (other).ctx.val.n: + raise ValueError("moduli must match") + + fmpz_mod_sub( + res.val, self.val, (other).val, self.ctx.val + ) + return res + + # Attempt to sub an fmpz to an fmpz_mod + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + fmpz_mod_fmpz_sub( + res.val, self.val, (other).val, self.ctx.val + ) + return res + + # TODO: is this bad? Should I just copy paste logic + # above? def __rsub__(self, other): - pass + return self.__sub__(other).__neg__() def __isub__(self, other): - pass + if typecheck(other, fmpz_mod): + if not self.ctx.val.n == (other).ctx.val.n: + raise ValueError("moduli must match") + + fmpz_mod_sub( + self.val, self.val, (other).val, self.ctx.val + ) + return self + + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + fmpz_mod_sub_fmpz( + self.val, self.val, (other).val, self.ctx.val + ) + return self def __mul__(self, other): - pass + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx + + # Add two fmpz_mod if moduli match + if typecheck(other, fmpz_mod): + if not self.ctx.val.n == (other).ctx.val.n: + raise ValueError("moduli must match") + + fmpz_mod_mul( + res.val, self.val, (other).val, self.ctx.val + ) + return res + + # Attempt to add an fmpz to an fmpz_mod + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + # There's no `fmpz_mod_mul_fmpz` + # So we need to make sure that other is canonical + # TODO: should we check with `fmpz_mod_is_canoncial` + # or just always reduce? + fmpz_mod_set_fmpz( + (other).val, (other).val, self.ctx.val + ) + + fmpz_mod_mul( + res.val, self.val, (other).val, self.ctx.val + ) + + return res def __rmul__(self, other): - pass + return self.__mul__(other) def __imul__(self, other): - pass + # Add two fmpz_mod if moduli match + if typecheck(other, fmpz_mod): + if not self.ctx.val.n == (other).ctx.val.n: + raise ValueError("moduli must match") + + fmpz_mod_mul( + self.val, self.val, (other).val, self.ctx.val + ) + return self - def __truediv__(self, other): - pass + # Attempt to add an fmpz to an fmpz_mod + other = any_as_fmpz(other) + if other is NotImplemented: + raise NotImplementedError + + # There's no `fmpz_mod_mul_fmpz` + # So we need to make sure that other is canonical + # TODO: should we check with `fmpz_mod_is_canoncial` + # or just always reduce? + fmpz_mod_set_fmpz( + (other).val, (other).val, self.ctx.val + ) + + fmpz_mod_mul( + self.val, self.val, (other).val, self.ctx.val + ) + + return self + + @staticmethod + def _div_(left, right): + cdef bint check + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + + # Division when left and right are fmpz_mod + if typecheck(left, fmpz_mod) and typecheck(right, fmpz_mod): + res.ctx = (left).ctx + if not (left).ctx.val.n == (right).ctx.val.n: + raise ValueError("moduli must match") + check = fmpz_mod_divides( + res.val, (left).val, (right).val, res.ctx.val + ) + + # Case when only left is fmpz_mod + elif typecheck(left, fmpz_mod): + res.ctx = (left).ctx + right = any_as_fmpz(right) + if right is NotImplemented: + raise NotImplementedError + check = fmpz_mod_divides( + res.val, (left).val, (right).val, res.ctx.val + ) + + # Case when right is an fmpz_mod + else: + res.ctx = (right).ctx + left = any_as_fmpz(left) + if left is NotImplemented: + raise NotImplementedError + check = fmpz_mod_divides( + res.val, (left).val, (right).val, res.ctx.val + ) + + if check == 0: + raise ZeroDivisionError(f"{right} is not invertible modulo {res.ctx.modulus()}") + + return res + + def __truediv__(s, t): + return fmpz_mod._div_(s, t) + + def __rtruediv__(s, t): + return fmpz_mod._div_(t, s) def __floordiv__(self, other): raise NotImplemented + + def inverse(self, check=True): + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx + + # Warning! This will crash if there's no solution + if check is False: + fmpz_mod_inv(res.val, self.val, self.ctx.val) + return res + + cdef bint r + cdef fmpz one = fmpz.__new__(fmpz) + fmpz_one(one.val) + + r = fmpz_mod_divides( + res.val, one.val, self.val, self.ctx.val + ) + if r == 0: + raise ZeroDivisionError(f"{self} is not invertible modulo {self.ctx.modulus()}") + + return res + + def __invert__(self): + return self.inverse() + + def __pow__(self, e): + cdef bint check + cdef fmpz_mod res + res = fmpz_mod.__new__(fmpz_mod) + res.ctx = self.ctx + + # Attempt to convert exponent to fmpz + e = any_as_fmpz(e) + if e is NotImplemented: + raise NotImplementedError + + check = fmpz_mod_pow_fmpz( + res.val, self.val, (e).val, self.ctx.val + ) + + if check == 0: + raise ZeroDivisionError(f"{self} is not invertible modulo {self.ctx.modulus()}") + + return res From 514f3b0bd38ccb03d105613d971f78b63a8e965b Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 11:42:54 +0100 Subject: [PATCH 17/33] Update tests --- src/flint/test/test.py | 115 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 10 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 07b24ed2..0ebae4b7 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1586,15 +1586,21 @@ def test_pickling(): def test_fmpz_mod(): from flint import fmpz_mod_ctx, fmpz - + # Context tests F163 = fmpz_mod_ctx(163) assert F163.modulus() == 163 + big = 2**1024 - 1 Fbig = fmpz_mod_ctx(2**1024 - 1) assert Fbig.modulus() == big # Rich comparisons + assert raises(lambda: F163(123) > 0, TypeError) + assert raises(lambda: F163(123) >= 0, TypeError) + assert raises(lambda: F163(123) < 0, TypeError) + assert raises(lambda: F163(123) <= 0, TypeError) + assert (F163(123) == 123) is True assert (F163(123) == 123 + 163) is True assert (F163(123) == 1) is False @@ -1605,15 +1611,6 @@ def test_fmpz_mod(): assert (F163(123) == F163(1)) is False assert (F163(123) != F163(1)) is True - assert (Fbig(123) == 123) is True - assert (Fbig(123) == 123 + big) is True - assert (Fbig(123) == 1) is False - assert (Fbig(123) != 1) is True - assert (Fbig(123) == Fbig(123)) is True - assert (Fbig(123) == Fbig(123 + big)) is True - assert (Fbig(123) == Fbig(1)) is False - assert (Fbig(123) != Fbig(1)) is True - # Is one, zero, canoncial assert (F163(0) == 0) is True assert F163(0).is_zero() is True @@ -1625,6 +1622,7 @@ def test_fmpz_mod(): assert F163(2).is_one() is False assert F163(123).is_canonical() is True + # Arithmetic tests # Negation @@ -1635,6 +1633,7 @@ def test_fmpz_mod(): assert F163(123) + F163(456) == F163(123 + 456) assert F163(123) + F163(456) == F163(456) + F163(123) assert F163(123) + 456 == F163(123 + 456) + assert 456 + F163(123) == F163(123 + 456) assert F163(123) + fmpz(456) == F163(456) + F163(123) assert raises(lambda: F163(123) + Fbig(456), ValueError) @@ -1649,6 +1648,102 @@ def test_fmpz_mod(): test_inplace = F163(123) test_inplace += fmpz(456) assert test_inplace == F163(123 + 456) + + # Subtraction + + assert F163(123) - F163(456) == F163(123 - 456) + assert F163(123) - 456 == F163(123 - 456) + assert F163(123) - 456 == F163(123) - F163(456) + assert F163(456) - 123 == F163(456) - F163(123) + assert 123 - F163(456) == F163(123) - F163(456) + # assert 456 - F163(123) == F163(456) - F163(123) + assert F163(123) - fmpz(456) == F163(123) - F163(456) + assert raises(lambda: F163(123) - Fbig(456), ValueError) + + test_inplace = F163(123) + test_inplace -= F163(456) + assert test_inplace == F163(123 - 456) + + test_inplace = F163(123) + test_inplace -= 456 + assert test_inplace == F163(123 - 456) + + test_inplace = F163(123) + test_inplace -= fmpz(456) + assert test_inplace == F163(123 - 456) + + # Multiplication + + assert F163(123) * F163(456) == (123 * 456) % 163 + assert F163(123) * 456 == (123 * 456) % 163 + assert 456 * F163(123) == (123 * 456) % 163 + + assert F163(1) * F163(123) == F163(1 * 123) + assert F163(2) * F163(123) == F163(2 * 123) + assert F163(3) * F163(123) == F163(3 * 123) + assert 1 * F163(123) == F163(1 * 123) + assert 2 * F163(123) == F163(2 * 123) + assert 3 * F163(123) == F163(3 * 123) + assert F163(123) * 1 == F163(1 * 123) + assert F163(123) * 2 == F163(2 * 123) + assert F163(123) * 3 == F163(3 * 123) + assert fmpz(1) * F163(123) == F163(1 * 123) + assert fmpz(2) * F163(123) == F163(2 * 123) + assert fmpz(3) * F163(123) == F163(3 * 123) + + test_inplace = F163(123) + test_inplace *= F163(456) + assert test_inplace == F163(123 * 456) + + test_inplace = F163(123) + test_inplace *= 456 + assert test_inplace == F163(123 * 456) + + test_inplace = F163(123) + test_inplace *= fmpz(456) + assert test_inplace == F163(123 * 456) + + # Exponentiation + + assert F163(0)**0 == pow(0, 0, 163) + assert F163(0)**1 == pow(0, 1, 163) + assert F163(0)**2 == pow(0, 2, 163) + assert raises(lambda: F163(0)**(-1), ZeroDivisionError) + + assert F163(123)**0 == pow(123, 0, 163) + assert F163(123)**1 == pow(123, 1, 163) + assert F163(123)**2 == pow(123, 2, 163) + assert F163(123)**3 == pow(123, 3, 163) + assert F163(123)**100 == pow(123, 100, 163) + assert F163(123)**(-1) == pow(123, -1, 163) + assert F163(123)**(-2) == pow(123, -2, 163) + assert F163(123)**(-3) == pow(123, -3, 163) + assert F163(123)**(-4) == pow(123, -4, 163) + + # Inversion + + assert raises(lambda: F163(0).__invert__(), ZeroDivisionError) + assert F163(123).__invert__() == pow(123, -1, 163) + assert F163(1).__invert__() == pow(1, -1, 163) + assert F163(2).__invert__() == pow(2, -1, 163) + + assert F163(1).inverse(check=False) == pow(1, -1, 163) + assert F163(2).inverse(check=False) == pow(2, -1, 163) + assert F163(123).inverse(check=False) == pow(123, -1, 163) + + # Division + + assert raises(lambda: F163(1) / F163(0), ZeroDivisionError) + assert F163(123) / F163(456) == (123 * pow(456, -1, 163)) % 163 + assert F163(123) / fmpz(456) == (123 * pow(456, -1, 163)) % 163 + assert F163(123) / 456 == (123 * pow(456, -1, 163)) % 163 + + assert 1 / F163(2) == pow(2, -1, 163) + assert 1 / F163(123) == pow(123, -1, 163) + assert 1 / F163(456) == pow(456, -1, 163) + + assert fmpz(456) / F163(123) == (456 * pow(123, -1, 163)) % 163 + assert 456 / F163(123) == (456 * pow(123, -1, 163)) % 163 all_tests = [ From 91615a2c84dcb3d7dfc7f2faf5ed861d2a9c860a Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 11:54:47 +0100 Subject: [PATCH 18/33] Avoid call to modulus --- src/flint/types/fmpz_mod.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 90c5c716..5cda98c4 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -113,12 +113,13 @@ cdef class fmpz_mod(flint_scalar): # TODO: is this the best method for comparison? if typecheck(s, fmpz_mod) and typecheck(t, fmpz_mod): res = ((s).val[0] == (t).val[0]) and \ - ((s).ctx.modulus() == (t).ctx.modulus()) + ((s).ctx.val.n == (t).ctx.val.n) if op == 2: return res else: return not res # TODO: is this the best method for comparison? + # Seems like I'm doing too many type conversions? elif typecheck(s, fmpz_mod) and typecheck(t, int): res = int(s) == t % (s).ctx.modulus() if op == 2: From 356ec25574c03d2b90d6946604c37f9d410e558d Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 15:07:56 +0100 Subject: [PATCH 19/33] Include additional tests --- src/flint/test/test.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 0ebae4b7..ffb0a0a1 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1595,6 +1595,14 @@ def test_fmpz_mod(): Fbig = fmpz_mod_ctx(2**1024 - 1) assert Fbig.modulus() == big + F163_copy = fmpz_mod_ctx(163) + assert F163_copy == F163 + assert F163_copy != Fbig + assert str(F163) == "fmpz_mod_ctx(163)" + assert repr(F163) == "Context for fmpz_mod with modulus: 163" + + # Type tests + # Rich comparisons assert raises(lambda: F163(123) > 0, TypeError) assert raises(lambda: F163(123) >= 0, TypeError) @@ -1605,12 +1613,20 @@ def test_fmpz_mod(): assert (F163(123) == 123 + 163) is True assert (F163(123) == 1) is False assert (F163(123) != 1) is True - assert (F163(123) == F163(123)) is True assert (F163(123) == F163(123 + 163)) is True assert (F163(123) == F163(1)) is False assert (F163(123) != F163(1)) is True + assert (hash(F163(123)) == hash(123)) is True + assert (hash(F163(F163(123))) == hash(123)) is True + assert (hash(F163(123)) == hash(1)) is False + assert (hash(F163(123)) != hash(1)) is True + assert (hash(F163(123)) == hash(F163(123))) is True + assert (hash(F163(123)) == hash(F163(123 + 163))) is True + assert (hash(F163(123)) == hash(F163(1))) is False + assert (hash(F163(123)) != hash(F163(1))) is True + # Is one, zero, canoncial assert (F163(0) == 0) is True assert F163(0).is_zero() is True @@ -1620,8 +1636,14 @@ def test_fmpz_mod(): assert F163(164).is_one() is True assert F163(1).is_one() is True assert F163(2).is_one() is False - assert F163(123).is_canonical() is True + # int, str, repr + assert str(F163(11)) == "11" + assert repr(F163(-1)) == "162" + assert repr(F163(11)) == "fmpz_mod(11, 163)" + assert repr(F163(-1)) == "fmpz_mod(162, 163)" + + assert F163(5).__pos__() == F163(5) # Arithmetic tests @@ -1631,6 +1653,8 @@ def test_fmpz_mod(): # Addition assert F163(123) + F163(456) == F163(123 + 456) + assert raises(lambda: F163(123) + "AAA", TypeError) + assert F163(123) + F163(456) == F163(456) + F163(123) assert F163(123) + 456 == F163(123 + 456) assert 456 + F163(123) == F163(123 + 456) From 70a1f57aa40df95790ff212e105381eaee6856cd Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 15:08:14 +0100 Subject: [PATCH 20/33] Modify code based on feedback and attempt to simplify code --- src/flint/types/fmpz_mod.pyx | 225 +++++++++++------------------------ 1 file changed, 67 insertions(+), 158 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 5cda98c4..56c93790 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -4,7 +4,9 @@ from flint.flintlib.fmpz cimport ( fmpz_one, fmpz_set, COEFF_IS_MPZ, - fmpz_get_str + fmpz_get_str, + fmpz_init, + fmpz_clear ) from flint.flintlib.fmpz_mod cimport * @@ -36,6 +38,12 @@ cdef fmpz_get_intlong(fmpz_t x): cdef class fmpz_mod_ctx: """ """ + def __cinit__(self, mod): + self.__init__(mod) + + def __dealloc__(self): + fmpz_mod_ctx_clear(self.val) + def __init__(self, mod): """ """ @@ -51,8 +59,12 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) + + def __eq__(self, other): + if typecheck(other, fmpz_mod_ctx): + return self.val.n == (other).val.n + return False - # TODO: should this be cached if we make immutable? def modulus(self): """ Return the modulus from the context as an fmpz @@ -73,34 +85,47 @@ cdef class fmpz_mod_ctx: ) def __call__(self, val): - if not typecheck(val, fmpz): - val = any_as_fmpz(val) - if val is NotImplemented: - raise NotImplementedError("TODO") - return fmpz_mod(val, self) +# TODO: this seems stupid +def any_as_fmpz_mod(obj, ctx): + try: + return ctx(obj) + except NotImplementedError: + return NotImplemented cdef class fmpz_mod(flint_scalar): + def __cinit__(self): + fmpz_init(self.val) + + def __dealloc__(self): + fmpz_clear(self.val) + def __init__(self, val, ctx): + if not typecheck(ctx, fmpz_mod_ctx): + raise ValueError 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("TODO") - + raise NotImplementedError fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) def is_zero(self): return self == 0 - # TODO: kind of pointless, as we always ensure canonical on init? - def is_canonical(self): - cdef bint res - res = fmpz_mod_is_canonical(self.val, self.ctx.val) - return res == 1 - def is_one(self): cdef bint res res = fmpz_mod_is_one(self.val, self.ctx.val) @@ -113,7 +138,7 @@ cdef class fmpz_mod(flint_scalar): # TODO: is this the best method for comparison? if typecheck(s, fmpz_mod) and typecheck(t, fmpz_mod): res = ((s).val[0] == (t).val[0]) and \ - ((s).ctx.val.n == (t).ctx.val.n) + ((s).ctx == (t).ctx) if op == 2: return res else: @@ -137,11 +162,8 @@ cdef class fmpz_mod(flint_scalar): self.ctx.modulus() ) - # TODO: seems ugly... def __hash__(self): - return hash( - (int(self), int(self.ctx.modulus())) - ) + return hash((int(self))) def __int__(self): return fmpz_get_intlong(self.val) @@ -160,86 +182,37 @@ cdef class fmpz_mod(flint_scalar): cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx - - fmpz_mod_neg( - res.val, self.val, - (self.ctx.val)) + fmpz_mod_neg(res.val, self.val, self.ctx.val) return res def __add__(self, other): + other = any_as_fmpz_mod(other, self.ctx) + if other is NotImplemented: + return NotImplemented + cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx - - # Add two fmpz_mod if moduli match - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_add( - res.val, self.val, (other).val, self.ctx.val - ) - return res - - # Attempt to add an fmpz to an fmpz_mod - other = any_as_fmpz(other) - if other is NotImplemented: - raise NotImplementedError - - fmpz_mod_add_fmpz( - res.val, self.val, (other).val, self.ctx.val + fmpz_mod_add( + res.val, self.val, (other).val, self.ctx.val ) - return res def __radd__(self, other): return self.__add__(other) - def __iadd__(self, other): - # Add two fmpz_mod if moduli match - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_add( - self.val, self.val, (other).val, self.ctx.val - ) - return self - - # Add an fmpz to an fmpz_mod - other = any_as_fmpz(other) + def __sub__(self, other): + other = any_as_fmpz_mod(other, self.ctx) if other is NotImplemented: - raise NotImplementedError - - fmpz_mod_add_fmpz( - self.val, self.val, (other).val, self.ctx.val - ) - return self + return NotImplemented - def __sub__(self, other): cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx - # Sub two fmpz_mod if moduli match - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_sub( - res.val, self.val, (other).val, self.ctx.val - ) - return res - - # Attempt to sub an fmpz to an fmpz_mod - other = any_as_fmpz(other) - if other is NotImplemented: - raise NotImplementedError - - fmpz_mod_fmpz_sub( - res.val, self.val, (other).val, self.ctx.val + fmpz_mod_sub( + res.val, self.val, (other).val, self.ctx.val ) - return res # TODO: is this bad? Should I just copy paste logic @@ -247,92 +220,23 @@ cdef class fmpz_mod(flint_scalar): def __rsub__(self, other): return self.__sub__(other).__neg__() - def __isub__(self, other): - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_sub( - self.val, self.val, (other).val, self.ctx.val - ) - return self - - other = any_as_fmpz(other) + def __mul__(self, other): + other = any_as_fmpz_mod(other, self.ctx) if other is NotImplemented: - raise NotImplementedError + return NotImplemented - fmpz_mod_sub_fmpz( - self.val, self.val, (other).val, self.ctx.val - ) - return self - - def __mul__(self, other): cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx - # Add two fmpz_mod if moduli match - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_mul( - res.val, self.val, (other).val, self.ctx.val - ) - return res - - # Attempt to add an fmpz to an fmpz_mod - other = any_as_fmpz(other) - if other is NotImplemented: - raise NotImplementedError - - # There's no `fmpz_mod_mul_fmpz` - # So we need to make sure that other is canonical - # TODO: should we check with `fmpz_mod_is_canoncial` - # or just always reduce? - fmpz_mod_set_fmpz( - (other).val, (other).val, self.ctx.val - ) - fmpz_mod_mul( - res.val, self.val, (other).val, self.ctx.val + res.val, self.val, (other).val, self.ctx.val ) - return res def __rmul__(self, other): return self.__mul__(other) - def __imul__(self, other): - # Add two fmpz_mod if moduli match - if typecheck(other, fmpz_mod): - if not self.ctx.val.n == (other).ctx.val.n: - raise ValueError("moduli must match") - - fmpz_mod_mul( - self.val, self.val, (other).val, self.ctx.val - ) - return self - - # Attempt to add an fmpz to an fmpz_mod - other = any_as_fmpz(other) - if other is NotImplemented: - raise NotImplementedError - - # There's no `fmpz_mod_mul_fmpz` - # So we need to make sure that other is canonical - # TODO: should we check with `fmpz_mod_is_canoncial` - # or just always reduce? - fmpz_mod_set_fmpz( - (other).val, (other).val, self.ctx.val - ) - - fmpz_mod_mul( - self.val, self.val, (other).val, self.ctx.val - ) - - return self - @staticmethod def _div_(left, right): cdef bint check @@ -342,7 +246,7 @@ cdef class fmpz_mod(flint_scalar): # Division when left and right are fmpz_mod if typecheck(left, fmpz_mod) and typecheck(right, fmpz_mod): res.ctx = (left).ctx - if not (left).ctx.val.n == (right).ctx.val.n: + if not (left).ctx == (right).ctx: raise ValueError("moduli must match") check = fmpz_mod_divides( res.val, (left).val, (right).val, res.ctx.val @@ -353,7 +257,7 @@ cdef class fmpz_mod(flint_scalar): res.ctx = (left).ctx right = any_as_fmpz(right) if right is NotImplemented: - raise NotImplementedError + return NotImplemented check = fmpz_mod_divides( res.val, (left).val, (right).val, res.ctx.val ) @@ -363,7 +267,7 @@ cdef class fmpz_mod(flint_scalar): res.ctx = (right).ctx left = any_as_fmpz(left) if left is NotImplemented: - raise NotImplementedError + return NotImplemented check = fmpz_mod_divides( res.val, (left).val, (right).val, res.ctx.val ) @@ -383,11 +287,16 @@ cdef class fmpz_mod(flint_scalar): raise NotImplemented def inverse(self, check=True): + """ + Computes a^-1 mod N + + When check=False, the solutions is assumed to exist and Flint will abort on + failure. + """ cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) res.ctx = self.ctx - # Warning! This will crash if there's no solution if check is False: fmpz_mod_inv(res.val, self.val, self.ctx.val) return res From cd73cf81253556ba4ed9fc74150bd5d2627c76db Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 17:10:52 +0100 Subject: [PATCH 21/33] increase coverage of fmpz_mod tests --- src/flint/test/test.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index ffb0a0a1..75e7a876 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1585,10 +1585,12 @@ def test_pickling(): assert obj == obj2 def test_fmpz_mod(): - from flint import fmpz_mod_ctx, fmpz + from flint import fmpz_mod_ctx, fmpz, fmpz_mod # Context tests F163 = fmpz_mod_ctx(163) + assert raises(lambda: fmpz_mod_ctx("AAA"), NotImplementedError) + assert raises(lambda: fmpz_mod_ctx(-1), ValueError) assert F163.modulus() == 163 big = 2**1024 - 1 @@ -1597,12 +1599,20 @@ def test_fmpz_mod(): F163_copy = fmpz_mod_ctx(163) assert F163_copy == F163 + assert Fbig != F163 + assert hash(F163_copy) == hash(F163) + assert hash(Fbig) != hash(F163), f"{hash(Fbig)}, {hash(F163)}" assert F163_copy != Fbig + assert F163_copy != "A" + assert str(F163) == "fmpz_mod_ctx(163)" assert repr(F163) == "Context for fmpz_mod with modulus: 163" # Type tests + assert raises(lambda: fmpz_mod(1, "AAA"), ValueError) + + # Rich comparisons assert raises(lambda: F163(123) > 0, TypeError) assert raises(lambda: F163(123) >= 0, TypeError) @@ -1639,7 +1649,7 @@ def test_fmpz_mod(): # int, str, repr assert str(F163(11)) == "11" - assert repr(F163(-1)) == "162" + assert str(F163(-1)) == "162" assert repr(F163(11)) == "fmpz_mod(11, 163)" assert repr(F163(-1)) == "fmpz_mod(162, 163)" @@ -1653,6 +1663,8 @@ def test_fmpz_mod(): # Addition assert F163(123) + F163(456) == F163(123 + 456) + assert F163(123) + F163_copy(456) == F163(123 + 456) + assert F163(123) + F163(456) == F163_copy(123 + 456) assert raises(lambda: F163(123) + "AAA", TypeError) assert F163(123) + F163(456) == F163(456) + F163(123) @@ -1680,9 +1692,11 @@ def test_fmpz_mod(): assert F163(123) - 456 == F163(123) - F163(456) assert F163(456) - 123 == F163(456) - F163(123) assert 123 - F163(456) == F163(123) - F163(456) - # assert 456 - F163(123) == F163(456) - F163(123) + assert 456 - F163(123) == F163(456) - F163(123) assert F163(123) - fmpz(456) == F163(123) - F163(456) assert raises(lambda: F163(123) - Fbig(456), ValueError) + assert raises(lambda: F163(123) - "AAA", TypeError) + test_inplace = F163(123) test_inplace -= F163(456) @@ -1714,6 +1728,8 @@ def test_fmpz_mod(): assert fmpz(1) * F163(123) == F163(1 * 123) assert fmpz(2) * F163(123) == F163(2 * 123) assert fmpz(3) * F163(123) == F163(3 * 123) + assert raises(lambda: F163(123) * "AAA", TypeError) + test_inplace = F163(123) test_inplace *= F163(456) @@ -1733,6 +1749,8 @@ def test_fmpz_mod(): assert F163(0)**1 == pow(0, 1, 163) assert F163(0)**2 == pow(0, 2, 163) assert raises(lambda: F163(0)**(-1), ZeroDivisionError) + assert raises(lambda: F163(0)**("AA"), NotImplementedError) + assert F163(123)**0 == pow(123, 0, 163) assert F163(123)**1 == pow(123, 1, 163) @@ -1761,7 +1779,10 @@ def test_fmpz_mod(): assert F163(123) / F163(456) == (123 * pow(456, -1, 163)) % 163 assert F163(123) / fmpz(456) == (123 * pow(456, -1, 163)) % 163 assert F163(123) / 456 == (123 * pow(456, -1, 163)) % 163 - + assert raises(lambda: F163(123) / "AAA", TypeError) + assert raises(lambda: "AAA" / F163(123), TypeError) + assert raises(lambda: Fbig(123) / F163(123), ValueError) + assert raises(lambda: F163(123) // F163(123), TypeError) assert 1 / F163(2) == pow(2, -1, 163) assert 1 / F163(123) == pow(123, -1, 163) assert 1 / F163(456) == pow(456, -1, 163) From 3e79dc7fb59660cbb757dc673a1f28120314ca25 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 17:11:05 +0100 Subject: [PATCH 22/33] Small changes based off feedback from PR --- src/flint/types/fmpz_mod.pyx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 56c93790..3c924088 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -6,7 +6,8 @@ from flint.flintlib.fmpz cimport ( COEFF_IS_MPZ, fmpz_get_str, fmpz_init, - fmpz_clear + fmpz_clear, + fmpz_equal ) from flint.flintlib.fmpz_mod cimport * @@ -59,11 +60,6 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) - - def __eq__(self, other): - if typecheck(other, fmpz_mod_ctx): - return self.val.n == (other).val.n - return False def modulus(self): """ @@ -74,6 +70,14 @@ cdef class fmpz_mod_ctx: fmpz_set(n.val, (self.val.n)) return n + def __eq__(self, other): + if typecheck(other, fmpz_mod_ctx): + return fmpz_equal(self.val.n, (other).val.n) + return False + + def __hash__(self): + return hash(self.modulus()) + def __repr__(self): return "Context for fmpz_mod with modulus: {}".format( self.modulus() @@ -151,7 +155,6 @@ cdef class fmpz_mod(flint_scalar): return res else: return not res - return NotImplemented def __bool__(self): return not self.is_zero() @@ -284,7 +287,7 @@ cdef class fmpz_mod(flint_scalar): return fmpz_mod._div_(t, s) def __floordiv__(self, other): - raise NotImplemented + return NotImplemented def inverse(self, check=True): """ From 2e433ca9b58b3012c8d9c21660d561104eb46956 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 18:03:26 +0100 Subject: [PATCH 23/33] Remove function in favour for import --- src/flint/types/fmpz.pxd | 1 + src/flint/types/fmpz_mod.pyx | 21 +-------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/flint/types/fmpz.pxd b/src/flint/types/fmpz.pxd index 4317c89f..074aa411 100644 --- a/src/flint/types/fmpz.pxd +++ b/src/flint/types/fmpz.pxd @@ -10,6 +10,7 @@ from flint.flintlib.fmpz cimport fmpz_t, fmpz_set_str, fmpz_set_si from cpython.version cimport PY_MAJOR_VERSION cdef int fmpz_set_any_ref(fmpz_t x, obj) +cdef fmpz_get_intlong(fmpz_t x) cdef inline int fmpz_set_pylong(fmpz_t x, obj): cdef int overflow diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 3c924088..b327047c 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -1,10 +1,7 @@ -from flint.flintlib.flint cimport slong from flint.flintlib.fmpz cimport ( fmpz_t, fmpz_one, fmpz_set, - COEFF_IS_MPZ, - fmpz_get_str, fmpz_init, fmpz_clear, fmpz_equal @@ -12,29 +9,13 @@ from flint.flintlib.fmpz cimport ( from flint.flintlib.fmpz_mod cimport * from flint.utils.typecheck cimport typecheck -from flint.utils.conversion cimport str_from_chars from flint.flint_base.flint_base cimport flint_scalar from flint.types.fmpz cimport ( fmpz, any_as_fmpz, + fmpz_get_intlong ) -cimport libc.stdlib - -# TODO: import this from types.fmpz somehow, it's not good -# to have the function repeated here. -cdef fmpz_get_intlong(fmpz_t x): - """ - Convert fmpz_t to a Python int or long. - """ - cdef char * s - if COEFF_IS_MPZ(x[0]): - s = fmpz_get_str(NULL, 16, x) - v = int(str_from_chars(s), 16) - libc.stdlib.free(s) - return v - else: - return x[0] cdef class fmpz_mod_ctx: """ From f96f3c31ad6eeedd30c1fba6ffd12b3adf6946f1 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 18:48:31 +0100 Subject: [PATCH 24/33] Get to 99% coverage --- src/flint/test/test.py | 374 +++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 197 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 75e7a876..8121d1ed 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1587,209 +1587,189 @@ def test_pickling(): def test_fmpz_mod(): from flint import fmpz_mod_ctx, fmpz, fmpz_mod + 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) + # Context tests - F163 = fmpz_mod_ctx(163) - assert raises(lambda: fmpz_mod_ctx("AAA"), NotImplementedError) + assert raises(lambda: fmpz_mod_ctx("AAA"), TypeError) assert raises(lambda: fmpz_mod_ctx(-1), ValueError) - assert F163.modulus() == 163 - - big = 2**1024 - 1 - Fbig = fmpz_mod_ctx(2**1024 - 1) - assert Fbig.modulus() == big + assert F_sml.modulus() == p_sml + assert F_med.modulus() == p_med + assert F_big.modulus() == p_big - F163_copy = fmpz_mod_ctx(163) - assert F163_copy == F163 - assert Fbig != F163 - assert hash(F163_copy) == hash(F163) - assert hash(Fbig) != hash(F163), f"{hash(Fbig)}, {hash(F163)}" - assert F163_copy != Fbig - assert F163_copy != "A" + F_big_copy = fmpz_mod_ctx(p_big) + assert F_big_copy == F_big + assert F_big != F_sml + assert hash(F_big_copy) == hash(F_big) + assert hash(F_big) != hash(F_sml) + assert F_big_copy != F_sml + assert F_big_copy != "A" - assert str(F163) == "fmpz_mod_ctx(163)" - assert repr(F163) == "Context for fmpz_mod with modulus: 163" + assert repr(F_sml) == "fmpz_mod_ctx(163)" + assert str(F_sml) == "Context for fmpz_mod with modulus: 163" # Type tests - - assert raises(lambda: fmpz_mod(1, "AAA"), ValueError) - - - # Rich comparisons - assert raises(lambda: F163(123) > 0, TypeError) - assert raises(lambda: F163(123) >= 0, TypeError) - assert raises(lambda: F163(123) < 0, TypeError) - assert raises(lambda: F163(123) <= 0, TypeError) - - assert (F163(123) == 123) is True - assert (F163(123) == 123 + 163) is True - assert (F163(123) == 1) is False - assert (F163(123) != 1) is True - assert (F163(123) == F163(123)) is True - assert (F163(123) == F163(123 + 163)) is True - assert (F163(123) == F163(1)) is False - assert (F163(123) != F163(1)) is True - - assert (hash(F163(123)) == hash(123)) is True - assert (hash(F163(F163(123))) == hash(123)) is True - assert (hash(F163(123)) == hash(1)) is False - assert (hash(F163(123)) != hash(1)) is True - assert (hash(F163(123)) == hash(F163(123))) is True - assert (hash(F163(123)) == hash(F163(123 + 163))) is True - assert (hash(F163(123)) == hash(F163(1))) is False - assert (hash(F163(123)) != hash(F163(1))) is True - - # Is one, zero, canoncial - assert (F163(0) == 0) is True - assert F163(0).is_zero() is True - assert not F163(0) - assert not F163(163) - assert F163(1).is_one() is True - assert F163(164).is_one() is True - assert F163(1).is_one() is True - assert F163(2).is_one() is False - - # int, str, repr - assert str(F163(11)) == "11" - assert str(F163(-1)) == "162" - assert repr(F163(11)) == "fmpz_mod(11, 163)" - assert repr(F163(-1)) == "fmpz_mod(162, 163)" - - assert F163(5).__pos__() == F163(5) - - # Arithmetic tests - - # Negation - assert -F163(123) == F163(-123) == (-123 % 163) - assert -F163(1) == F163(-1) == F163(162) - - # Addition - assert F163(123) + F163(456) == F163(123 + 456) - assert F163(123) + F163_copy(456) == F163(123 + 456) - assert F163(123) + F163(456) == F163_copy(123 + 456) - assert raises(lambda: F163(123) + "AAA", TypeError) - - assert F163(123) + F163(456) == F163(456) + F163(123) - assert F163(123) + 456 == F163(123 + 456) - assert 456 + F163(123) == F163(123 + 456) - assert F163(123) + fmpz(456) == F163(456) + F163(123) - assert raises(lambda: F163(123) + Fbig(456), ValueError) - - test_inplace = F163(123) - test_inplace += F163(456) - assert test_inplace == F163(123 + 456) - - test_inplace = F163(123) - test_inplace += 456 - assert test_inplace == F163(123 + 456) - - test_inplace = F163(123) - test_inplace += fmpz(456) - assert test_inplace == F163(123 + 456) - - # Subtraction - - assert F163(123) - F163(456) == F163(123 - 456) - assert F163(123) - 456 == F163(123 - 456) - assert F163(123) - 456 == F163(123) - F163(456) - assert F163(456) - 123 == F163(456) - F163(123) - assert 123 - F163(456) == F163(123) - F163(456) - assert 456 - F163(123) == F163(456) - F163(123) - assert F163(123) - fmpz(456) == F163(123) - F163(456) - assert raises(lambda: F163(123) - Fbig(456), ValueError) - assert raises(lambda: F163(123) - "AAA", TypeError) - - - test_inplace = F163(123) - test_inplace -= F163(456) - assert test_inplace == F163(123 - 456) - - test_inplace = F163(123) - test_inplace -= 456 - assert test_inplace == F163(123 - 456) - - test_inplace = F163(123) - test_inplace -= fmpz(456) - assert test_inplace == F163(123 - 456) - - # Multiplication - - assert F163(123) * F163(456) == (123 * 456) % 163 - assert F163(123) * 456 == (123 * 456) % 163 - assert 456 * F163(123) == (123 * 456) % 163 - - assert F163(1) * F163(123) == F163(1 * 123) - assert F163(2) * F163(123) == F163(2 * 123) - assert F163(3) * F163(123) == F163(3 * 123) - assert 1 * F163(123) == F163(1 * 123) - assert 2 * F163(123) == F163(2 * 123) - assert 3 * F163(123) == F163(3 * 123) - assert F163(123) * 1 == F163(1 * 123) - assert F163(123) * 2 == F163(2 * 123) - assert F163(123) * 3 == F163(3 * 123) - assert fmpz(1) * F163(123) == F163(1 * 123) - assert fmpz(2) * F163(123) == F163(2 * 123) - assert fmpz(3) * F163(123) == F163(3 * 123) - assert raises(lambda: F163(123) * "AAA", TypeError) - - - test_inplace = F163(123) - test_inplace *= F163(456) - assert test_inplace == F163(123 * 456) - - test_inplace = F163(123) - test_inplace *= 456 - assert test_inplace == F163(123 * 456) - - test_inplace = F163(123) - test_inplace *= fmpz(456) - assert test_inplace == F163(123 * 456) - - # Exponentiation - - assert F163(0)**0 == pow(0, 0, 163) - assert F163(0)**1 == pow(0, 1, 163) - assert F163(0)**2 == pow(0, 2, 163) - assert raises(lambda: F163(0)**(-1), ZeroDivisionError) - assert raises(lambda: F163(0)**("AA"), NotImplementedError) - - - assert F163(123)**0 == pow(123, 0, 163) - assert F163(123)**1 == pow(123, 1, 163) - assert F163(123)**2 == pow(123, 2, 163) - assert F163(123)**3 == pow(123, 3, 163) - assert F163(123)**100 == pow(123, 100, 163) - assert F163(123)**(-1) == pow(123, -1, 163) - assert F163(123)**(-2) == pow(123, -2, 163) - assert F163(123)**(-3) == pow(123, -3, 163) - assert F163(123)**(-4) == pow(123, -4, 163) - - # Inversion - - assert raises(lambda: F163(0).__invert__(), ZeroDivisionError) - assert F163(123).__invert__() == pow(123, -1, 163) - assert F163(1).__invert__() == pow(1, -1, 163) - assert F163(2).__invert__() == pow(2, -1, 163) - - assert F163(1).inverse(check=False) == pow(1, -1, 163) - assert F163(2).inverse(check=False) == pow(2, -1, 163) - assert F163(123).inverse(check=False) == pow(123, -1, 163) - - # Division - - assert raises(lambda: F163(1) / F163(0), ZeroDivisionError) - assert F163(123) / F163(456) == (123 * pow(456, -1, 163)) % 163 - assert F163(123) / fmpz(456) == (123 * pow(456, -1, 163)) % 163 - assert F163(123) / 456 == (123 * pow(456, -1, 163)) % 163 - assert raises(lambda: F163(123) / "AAA", TypeError) - assert raises(lambda: "AAA" / F163(123), TypeError) - assert raises(lambda: Fbig(123) / F163(123), ValueError) - assert raises(lambda: F163(123) // F163(123), TypeError) - assert 1 / F163(2) == pow(2, -1, 163) - assert 1 / F163(123) == pow(123, -1, 163) - assert 1 / F163(456) == pow(456, -1, 163) - - assert fmpz(456) / F163(123) == (456 * pow(123, -1, 163)) % 163 - assert 456 / F163(123) == (456 * pow(123, -1, 163)) % 163 - + assert raises(lambda: fmpz_mod(1, "AAA"), TypeError) + + # Test for small, medium and large char. + for F_test in [F_sml, F_med, F_big]: + test_mod = int(F_test.modulus()) + test_x = (-123) % test_mod # canonical value + test_y = ((-456) % test_mod)**2 # non-canoncial value + + F_test_copy = fmpz_mod_ctx(test_mod) + F_other = fmpz_mod_ctx(11) + + assert raises(lambda: F_test(test_x) > 0, TypeError) + assert raises(lambda: F_test(test_x) >= 0, TypeError) + assert raises(lambda: F_test(test_x) < 0, TypeError) + assert raises(lambda: F_test(test_x) <= 0, TypeError) + + assert (test_x == F_test(test_x)) is True, f"{test_x}, {F_test(test_x)}" + assert (124 != F_test(test_x)) is True + assert (F_test(test_x) == test_x) is True + assert (F_test(test_x) == test_x + test_mod) is True + assert (F_test(test_x) == 1) is False + assert (F_test(test_x) != 1) is True + assert (F_test(test_x) == F_test(test_x)) is True + 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 (hash(F_test(test_x)) == hash(test_x)) is True + assert (hash(F_test(F_test(test_x))) == hash(test_x)) is True + assert (hash(F_test(test_x)) == hash(1)) is False + assert (hash(F_test(test_x)) != hash(1)) is True + assert (hash(F_test(test_x)) == hash(F_test(test_x))) is True + assert (hash(F_test(test_x)) == hash(F_test(test_x + test_mod))) is True + assert (hash(F_test(test_x)) == hash(F_test(1))) is False + assert (hash(F_test(test_x)) != hash(F_test(1))) is True + + # Is one, zero + assert (F_test(0) == 0) is True + assert F_test(0).is_zero() is True + assert not F_test(0) + assert not F_test(test_mod) + assert F_test(1).is_one() is True + assert F_test(test_mod + 1).is_one() is True + assert F_test(1).is_one() is True + assert F_test(2).is_one() is False + + # int, str, repr + assert str(F_test(11)) == "11" + assert str(F_test(-1)) == str(test_mod - 1) + assert repr(F_test(11)) == f"fmpz_mod(11, {test_mod})" + assert repr(F_test(-1)) == f"fmpz_mod({test_mod - 1}, {test_mod})" + + assert +F_test(5) == F_test(5) + + # Arithmetic tests + + # Negation + assert -F_test(test_x) == F_test(-test_x) == (-test_x % test_mod) + assert -F_test(1) == F_test(-1) == F_test(test_mod - 1) + + # Addition + assert F_test(test_x) + F_test(test_y) == F_test(test_x + test_y) + assert F_test(test_x) + F_test_copy(test_y) == F_test(test_x + test_y) + assert F_test(test_x) + F_test(test_y) == F_test_copy(test_x + test_y) + assert raises(lambda: F_test(test_x) + "AAA", TypeError) + + assert F_test(test_x) + F_test(test_y) == F_test(test_y) + F_test(test_x) + assert F_test(test_x) + test_y == F_test(test_x + test_y) + assert test_y + F_test(test_x) == F_test(test_x + test_y) + assert F_test(test_x) + fmpz(test_y) == F_test(test_y) + F_test(test_x) + assert raises(lambda: F_test(test_x) + F_other(test_y), ValueError) + + # Subtraction + + assert F_test(test_x) - F_test(test_y) == F_test(test_x - test_y) + assert F_test(test_x) - test_y == F_test(test_x - test_y) + assert F_test(test_x) - test_y == F_test(test_x) - F_test(test_y) + assert F_test(test_y) - test_x == F_test(test_y) - F_test(test_x) + assert test_x - F_test(test_y) == F_test(test_x) - F_test(test_y) + assert test_y - F_test(test_x) == F_test(test_y) - F_test(test_x) + 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) + + # Multiplication + + assert F_test(test_x) * F_test(test_y) == (test_x * test_y) % test_mod + assert F_test(test_x) * test_y == (test_x * test_y) % test_mod + assert test_y * F_test(test_x) == (test_x * test_y) % test_mod + + assert F_test(1) * F_test(test_x) == F_test(1 * test_x) + assert F_test(2) * F_test(test_x) == F_test(2 * test_x) + assert F_test(3) * F_test(test_x) == F_test(3 * test_x) + assert 1 * F_test(test_x) == F_test(1 * test_x) + assert 2 * F_test(test_x) == F_test(2 * test_x) + assert 3 * F_test(test_x) == F_test(3 * test_x) + assert F_test(test_x) * 1 == F_test(1 * test_x) + assert F_test(test_x) * 2 == F_test(2 * test_x) + assert F_test(test_x) * 3 == F_test(3 * test_x) + assert fmpz(1) * F_test(test_x) == F_test(1 * test_x) + assert fmpz(2) * F_test(test_x) == F_test(2 * test_x) + assert fmpz(3) * F_test(test_x) == F_test(3 * test_x) + assert raises(lambda: F_test(test_x) * "AAA", TypeError) + assert raises(lambda: F_test(test_x) * F_other(test_x), ValueError) + + # Exponentiation + + assert F_test(0)**0 == pow(0, 0, test_mod) + assert F_test(0)**1 == pow(0, 1, test_mod) + assert F_test(0)**2 == pow(0, 2, test_mod) + assert raises(lambda: F_test(0)**(-1), ZeroDivisionError) + assert raises(lambda: F_test(0)**("AA"), NotImplementedError) + + assert F_test(test_x)**fmpz(0) == pow(test_x, 0, test_mod) + assert F_test(test_x)**fmpz(1) == pow(test_x, 1, test_mod) + assert F_test(test_x)**fmpz(2) == pow(test_x, 2, test_mod) + assert F_test(test_x)**fmpz(3) == pow(test_x, 3, test_mod) + + assert F_test(test_x)**0 == pow(test_x, 0, test_mod) + assert F_test(test_x)**1 == pow(test_x, 1, test_mod) + assert F_test(test_x)**2 == pow(test_x, 2, test_mod) + assert F_test(test_x)**3 == pow(test_x, 3, test_mod) + assert F_test(test_x)**100 == pow(test_x, 100, test_mod) + + assert F_test(test_x)**(-1) == pow(test_x, -1, test_mod) + assert F_test(test_x)**(-2) == pow(test_x, -2, test_mod) + assert F_test(test_x)**(-3) == pow(test_x, -3, test_mod) + assert F_test(test_x)**(-4) == pow(test_x, -4, test_mod) + + # Inversion + + assert raises(lambda: ~F_test(0), ZeroDivisionError) + assert ~F_test(test_x) == pow(test_x, -1, test_mod) + assert ~F_test(1) == pow(1, -1, test_mod) + assert ~F_test(2) == pow(2, -1, test_mod), f"Broken!! {~F_test(2)}, {pow(2, -1, test_mod)}" + + assert F_test(1).inverse(check=False) == pow(1, -1, test_mod) + assert F_test(2).inverse(check=False) == pow(2, -1, test_mod) + assert F_test(test_x).inverse(check=False) == pow(test_x, -1, test_mod) + + # Division + assert raises(lambda: F_test(1) / F_test(0), ZeroDivisionError) + assert F_test(test_x) / F_test(test_y) == (test_x * pow(test_y, -1, test_mod)) % test_mod + assert F_test(test_x) / fmpz(test_y) == (test_x * pow(test_y, -1, test_mod)) % test_mod + assert F_test(test_x) / test_y == (test_x * pow(test_y, -1, test_mod)) % test_mod + assert raises(lambda: F_test(test_x) / "AAA", TypeError) + assert raises(lambda: "AAA" / F_test(test_x), TypeError) + assert raises(lambda: F_other(test_x) / F_test(test_x), ValueError) + assert raises(lambda: F_test(test_x) // F_test(test_x), TypeError) + assert 1 / F_test(2) == pow(2, -1, test_mod) + assert 1 / F_test(test_x) == pow(test_x, -1, test_mod) + assert 1 / F_test(test_y) == pow(test_y, -1, test_mod) + + assert fmpz(test_y) / F_test(test_x) == (test_y * pow(test_x, -1, test_mod)) % test_mod + assert test_y / F_test(test_x) == (test_y * pow(test_x, -1, test_mod)) % test_mod + all_tests = [ test_pyflint, From 952159f7ed7558fdfde21c2195fbd939330ff9a0 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 18:48:44 +0100 Subject: [PATCH 25/33] Various changes thanks to PR feedback --- src/flint/types/fmpz_mod.pyx | 72 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index b327047c..a29b68a5 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -21,19 +21,13 @@ cdef class fmpz_mod_ctx: """ """ def __cinit__(self, mod): - self.__init__(mod) - - def __dealloc__(self): - fmpz_mod_ctx_clear(self.val) - - def __init__(self, mod): """ """ # Ensure modulus is fmpz type if not typecheck(mod, fmpz): mod = any_as_fmpz(mod) if mod is NotImplemented: - raise NotImplementedError("TODO") + raise TypeError("Context modulus must be able to be case to an `fmpz` type") # Ensure modulus is positive if mod < 1: @@ -41,6 +35,12 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) + + def __dealloc__(self): + fmpz_mod_ctx_clear(self.val) + + def __init__(self, mod): + pass def modulus(self): """ @@ -59,26 +59,15 @@ cdef class fmpz_mod_ctx: def __hash__(self): return hash(self.modulus()) - def __repr__(self): - return "Context for fmpz_mod with modulus: {}".format( - self.modulus() - ) - def __str__(self): - return "fmpz_mod_ctx({})".format( - self.modulus() - ) + return f"Context for fmpz_mod with modulus: {self.modulus()}" + + def __repr__(self): + return f"fmpz_mod_ctx({self.modulus()})" def __call__(self, val): return fmpz_mod(val, self) -# TODO: this seems stupid -def any_as_fmpz_mod(obj, ctx): - try: - return ctx(obj) - except NotImplementedError: - return NotImplemented - cdef class fmpz_mod(flint_scalar): def __cinit__(self): fmpz_init(self.val) @@ -88,7 +77,7 @@ cdef class fmpz_mod(flint_scalar): def __init__(self, val, ctx): if not typecheck(ctx, fmpz_mod_ctx): - raise ValueError + raise TypeError self.ctx = ctx # When the input is also an fmpz_mod we just need @@ -108,6 +97,12 @@ cdef class fmpz_mod(flint_scalar): raise NotImplementedError fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) + def any_as_fmpz_mod(self, obj): + try: + return self.ctx(obj) + except NotImplementedError: + return NotImplemented + def is_zero(self): return self == 0 @@ -116,26 +111,23 @@ cdef class fmpz_mod(flint_scalar): res = fmpz_mod_is_one(self.val, self.ctx.val) return res == 1 - def __richcmp__(s, t, int op): + def __richcmp__(self, other, int op): cdef bint res if op != 2 and op != 3: raise TypeError("fmpz_mod cannot be ordered") - # TODO: is this the best method for comparison? - if typecheck(s, fmpz_mod) and typecheck(t, fmpz_mod): - res = ((s).val[0] == (t).val[0]) and \ - ((s).ctx == (t).ctx) - if op == 2: - return res - else: - return not res - # TODO: is this the best method for comparison? - # Seems like I'm doing too many type conversions? - elif typecheck(s, fmpz_mod) and typecheck(t, int): - res = int(s) == t % (s).ctx.modulus() + + if not typecheck(other, fmpz_mod): + other = self.any_as_fmpz_mod(other) + + 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 + else: + return NotImplemented def __bool__(self): return not self.is_zero() @@ -170,7 +162,7 @@ cdef class fmpz_mod(flint_scalar): return res def __add__(self, other): - other = any_as_fmpz_mod(other, self.ctx) + other = self.any_as_fmpz_mod(other) if other is NotImplemented: return NotImplemented @@ -186,7 +178,7 @@ cdef class fmpz_mod(flint_scalar): return self.__add__(other) def __sub__(self, other): - other = any_as_fmpz_mod(other, self.ctx) + other = self.any_as_fmpz_mod(other) if other is NotImplemented: return NotImplemented @@ -199,13 +191,11 @@ cdef class fmpz_mod(flint_scalar): ) return res - # TODO: is this bad? Should I just copy paste logic - # above? def __rsub__(self, other): return self.__sub__(other).__neg__() def __mul__(self, other): - other = any_as_fmpz_mod(other, self.ctx) + other = self.any_as_fmpz_mod(other) if other is NotImplemented: return NotImplemented From 6244dc25bfbf3e9970cc9d401ecdad189aef22d3 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 19:28:59 +0100 Subject: [PATCH 26/33] Modify cinit and init --- src/flint/types/fmpz_mod.pyx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index a29b68a5..d55f8df9 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -20,9 +20,16 @@ from flint.types.fmpz cimport ( cdef class fmpz_mod_ctx: """ """ - def __cinit__(self, mod): - """ - """ + def __cinit__(self): + # TODO: is this the best method? + cdef fmpz one = fmpz.__new__(fmpz) + fmpz_one(one.val) + fmpz_mod_ctx_init(self.val, one.val) + + def __dealloc__(self): + fmpz_mod_ctx_clear(self.val) + + def __init__(self, mod): # Ensure modulus is fmpz type if not typecheck(mod, fmpz): mod = any_as_fmpz(mod) @@ -35,12 +42,6 @@ cdef class fmpz_mod_ctx: # Init the context fmpz_mod_ctx_init(self.val, (mod).val) - - def __dealloc__(self): - fmpz_mod_ctx_clear(self.val) - - def __init__(self, mod): - pass def modulus(self): """ From 42d44adadc7ebcabfcc22ff98cee3b63a463bb9f Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 20:49:48 +0100 Subject: [PATCH 27/33] Add doctests and rst for fmpz_mod --- doc/source/fmpz_mod.rst | 8 +++++++ doc/source/nmod.rst | 2 +- src/flint/types/fmpz_mod.pyx | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 doc/source/fmpz_mod.rst diff --git a/doc/source/fmpz_mod.rst b/doc/source/fmpz_mod.rst new file mode 100644 index 00000000..2e760181 --- /dev/null +++ b/doc/source/fmpz_mod.rst @@ -0,0 +1,8 @@ +**fmpz** -- integers mod n +=============================================================================== + +.. autoclass :: flint.fmpz_mod + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/nmod.rst b/doc/source/nmod.rst index 4f8bda48..febc625a 100644 --- a/doc/source/nmod.rst +++ b/doc/source/nmod.rst @@ -1,4 +1,4 @@ -**nmod** -- integers mod n +**nmod** -- integers mod wordsize n =============================================================================== .. autoclass :: flint.nmod diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index d55f8df9..3bc1b762 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -19,6 +19,11 @@ from flint.types.fmpz cimport ( cdef class fmpz_mod_ctx: """ + Context object for *fmpz_mod* initalised with a modulus `n` + + >>> fmpz_mod_ctx(2**127 - 1) + fmpz_mod_ctx(170141183460469231731687303715884105727) + """ def __cinit__(self): # TODO: is this the best method? @@ -47,6 +52,11 @@ cdef class fmpz_mod_ctx: """ Return the modulus from the context as an fmpz type + + >>> mod_ctx = fmpz_mod_ctx(2**127 - 1) + >>> mod_ctx.modulus() + 170141183460469231731687303715884105727 + """ n = fmpz() fmpz_set(n.val, (self.val.n)) @@ -70,6 +80,16 @@ cdef class fmpz_mod_ctx: return fmpz_mod(val, self) cdef class fmpz_mod(flint_scalar): + """ + The *fmpz_mod* type represents integer modulo an + arbitrary-size modulus. For wordsize modulus, see + *nmod*. + + >>> fmpz_mod(-1, fmpz_mod_ctx(2**127 - 1)) + fmpz_mod(170141183460469231731687303715884105726, 170141183460469231731687303715884105727) + + """ + def __cinit__(self): fmpz_init(self.val) @@ -105,9 +125,28 @@ cdef class fmpz_mod(flint_scalar): return NotImplemented def is_zero(self): + """ + Return whether an element is equal to zero + + >>> mod_ctx = fmpz_mod_ctx(163) + >>> mod_ctx(0).is_zero() + True + >>> mod_ctx(1).is_zero() + False + """ return self == 0 def is_one(self): + """ + Return whether an element is equal to one + + >>> mod_ctx = fmpz_mod_ctx(163) + >>> mod_ctx(0).is_one() + False + >>> mod_ctx(1).is_zero() + True + """ + cdef bint res res = fmpz_mod_is_one(self.val, self.ctx.val) return res == 1 @@ -267,6 +306,12 @@ cdef class fmpz_mod(flint_scalar): When check=False, the solutions is assumed to exist and Flint will abort on failure. + + >>> mod_ctx = fmpz_mod_ctx(163) + >>> mod_ctx(2).inverse() + fmpz_mod(82, 163) + >>> mod_ctx(2).inverse(check=False) + fmpz_mod(82, 163) """ cdef fmpz_mod res res = fmpz_mod.__new__(fmpz_mod) From 1e7332fb796ba4d84d6e3bf0101de21f4dd3e305 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:05:53 +0100 Subject: [PATCH 28/33] fix doc issues --- doc/source/fmpz_mod.rst | 7 ++++++- doc/source/index.rst | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/source/fmpz_mod.rst b/doc/source/fmpz_mod.rst index 2e760181..67e3206d 100644 --- a/doc/source/fmpz_mod.rst +++ b/doc/source/fmpz_mod.rst @@ -1,6 +1,11 @@ -**fmpz** -- integers mod n +**fmpz_mod** -- integers mod n =============================================================================== +.. autoclass :: flint.fmpz_mod_ctx + :members: + :inherited-members: + :undoc-members: + .. autoclass :: flint.fmpz_mod :members: :inherited-members: diff --git a/doc/source/index.rst b/doc/source/index.rst index bc7c7ae1..193e5701 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -43,6 +43,7 @@ Scalar types fmpz.rst fmpq.rst + fmpz_mod.rst nmod.rst arb.rst acb.rst From 5e09ff19e2254d2bcef7cee839aa0fbff36c2b0d Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:06:07 +0100 Subject: [PATCH 29/33] add sphinx links to docstrings --- src/flint/types/fmpz_mod.pyx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 3bc1b762..1f6e33d0 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -19,14 +19,14 @@ from flint.types.fmpz cimport ( cdef class fmpz_mod_ctx: """ - Context object for *fmpz_mod* initalised with a modulus `n` + Context object for creating :class:`~.fmpz_mod` initalised + with a modulus `n` >>> fmpz_mod_ctx(2**127 - 1) fmpz_mod_ctx(170141183460469231731687303715884105727) """ def __cinit__(self): - # TODO: is this the best method? cdef fmpz one = fmpz.__new__(fmpz) fmpz_one(one.val) fmpz_mod_ctx_init(self.val, one.val) @@ -83,10 +83,17 @@ cdef class fmpz_mod(flint_scalar): """ The *fmpz_mod* type represents integer modulo an arbitrary-size modulus. For wordsize modulus, see - *nmod*. + :class:`~.nmod`. + + An *fmpz_mod* element is constructed from an :class:`~.fmpz_mod_ctx` + either by passing it as an argument to the type, or + by directly calling the context >>> fmpz_mod(-1, fmpz_mod_ctx(2**127 - 1)) fmpz_mod(170141183460469231731687303715884105726, 170141183460469231731687303715884105727) + >>> ZmodN = fmpz_mod_ctx(2**127 - 1) + >>> ZmodN(-2) + fmpz_mod(170141183460469231731687303715884105725, 170141183460469231731687303715884105727) """ From db0c86becbb780581b4a86dd61db1c0bb071f210 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:21:19 +0100 Subject: [PATCH 30/33] Add missing export --- src/flint/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 52dc79a9..ef3f78e9 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.dirichlet import * from .functions.showgood import showgood __version__ = '0.4.4' From b2dfc431ee3aa704d0a8b307240956c65c431afc Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:21:32 +0100 Subject: [PATCH 31/33] Fix latex in docstring --- src/flint/types/fmpz_mod.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 1f6e33d0..1c6fbf25 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -18,9 +18,9 @@ from flint.types.fmpz cimport ( cdef class fmpz_mod_ctx: - """ + r""" Context object for creating :class:`~.fmpz_mod` initalised - with a modulus `n` + with a modulus :math:`N`. >>> fmpz_mod_ctx(2**127 - 1) fmpz_mod_ctx(170141183460469231731687303715884105727) @@ -308,8 +308,8 @@ cdef class fmpz_mod(flint_scalar): return NotImplemented def inverse(self, check=True): - """ - Computes a^-1 mod N + r""" + Computes :math:`a^{-1} \pmod N` When check=False, the solutions is assumed to exist and Flint will abort on failure. From ea30b64ea193ecf81825e7bda5e7936ce4843117 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:21:50 +0100 Subject: [PATCH 32/33] Remove unneeded docstring --- doc/source/general.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/source/general.rst b/doc/source/general.rst index 6462df8a..52156f0f 100644 --- a/doc/source/general.rst +++ b/doc/source/general.rst @@ -161,8 +161,6 @@ determined from the available data. The following convenience functions are provided for numerical evaluation with adaptive working precision. -.. autofunction :: flint.good - .. autofunction :: flint.showgood Power series From 5dead0cb2e344315742dcf9466525dacad8eae81 Mon Sep 17 00:00:00 2001 From: giacomopope Date: Fri, 15 Sep 2023 23:33:03 +0100 Subject: [PATCH 33/33] Make function cdef --- src/flint/types/fmpz_mod.pxd | 2 ++ src/flint/types/fmpz_mod.pyx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mod.pxd b/src/flint/types/fmpz_mod.pxd index 36892243..264d35d7 100644 --- a/src/flint/types/fmpz_mod.pxd +++ b/src/flint/types/fmpz_mod.pxd @@ -10,3 +10,5 @@ cdef class fmpz_mod(flint_scalar): cdef fmpz_mod_ctx ctx cdef fmpz_t val + cdef any_as_fmpz_mod(self, obj) + diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index 1c6fbf25..362a9161 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -125,7 +125,7 @@ cdef class fmpz_mod(flint_scalar): raise NotImplementedError fmpz_mod_set_fmpz(self.val, (val).val, self.ctx.val) - def any_as_fmpz_mod(self, obj): + cdef any_as_fmpz_mod(self, obj): try: return self.ctx(obj) except NotImplementedError: