diff --git a/doc/source/fq_default.rst b/doc/source/fq_default.rst new file mode 100644 index 00000000..bea090f9 --- /dev/null +++ b/doc/source/fq_default.rst @@ -0,0 +1,8 @@ +**fq_default** -- finite fields +=============================================================================== + +.. autoclass :: flint.fq_default + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/index.rst b/doc/source/index.rst index 8e95e722..71e50360 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -45,6 +45,7 @@ Scalar types fmpq.rst fmpz_mod.rst nmod.rst + fq_default.rst arb.rst acb.rst dirichlet.rst diff --git a/setup.py b/setup.py index d260834e..93e83c9d 100644 --- a/setup.py +++ b/setup.py @@ -105,6 +105,8 @@ ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), ("flint.types.fmpz_mpoly_q", ["src/flint/types/fmpz_mpoly_q.pyx"]), + ("flint.types.fq_default", ["src/flint/types/fq_default.pyx"]), + ("flint.types.arf", ["src/flint/types/arf.pyx"]), ("flint.types.arb", ["src/flint/types/arb.pyx"]), ("flint.types.arb_poly", ["src/flint/types/arb_poly.pyx"]), diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 77656aca..4db5de5f 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -24,6 +24,8 @@ from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly, fmpq_mpoly_vec +from .types.fq_default import * + from .types.arf import * from .types.arb import * from .types.arb_poly import * diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 481cac7f..e6151845 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -30,7 +30,136 @@ cdef class flint_elem: cdef class flint_scalar(flint_elem): - pass + # ================================================= + # These are the functions a new class should define + # assumes that addition and multiplication are + # commutative + # ================================================= + def is_zero(self): + return False + + def _any_as_self(self): + return NotImplemented + + def _neg_(self): + return NotImplemented + + def _add_(self, other): + return NotImplemented + + def _sub_(self, other): + return NotImplemented + + def _rsub_(self, other): + return NotImplemented + + def _mul_(self, other): + return NotImplemented + + def _div_(self, other): + return NotImplemented + + def _rdiv_(self, other): + return NotImplemented + + def _floordiv_(self, other): + return NotImplemented + + def _rfloordiv_(self, other): + return NotImplemented + + def _invert_(self): + return NotImplemented + + # ================================================= + # Generic arithmetic using the above functions + # ================================================= + + def __pos__(self): + return self + + def __neg__(self): + return self._neg_() + + def __add__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._add_(other) + + def __radd__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._add_(other) + + def __sub__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._sub_(other) + + def __rsub__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._rsub_(other) + + def __mul__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._mul_(other) + + def __rmul__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._mul_(other) + + def __truediv__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + + if other.is_zero(): + raise ZeroDivisionError + + return self._div_(other) + + def __rtruediv__(self, other): + if self.is_zero(): + raise ZeroDivisionError + + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._rdiv_(other) + + def __floordiv__(self, other): + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + + if other.is_zero(): + raise ZeroDivisionError + + return self._floordiv_(other) + + def __rfloordiv__(self, other): + if self.is_zero(): + raise ZeroDivisionError + + other = self._any_as_self(other) + if other is NotImplemented: + return NotImplemented + return self._rfloordiv_(other) + + def __invert__(self): + if self.is_zero(): + raise ZeroDivisionError + return self._invert_() + cdef class flint_poly(flint_elem): @@ -55,7 +184,7 @@ cdef class flint_poly(flint_elem): """ return list(self) - def str(self, bint ascending=False, *args, **kwargs): + def str(self, bint ascending=False, var="x", *args, **kwargs): """ Convert to a human-readable string (generic implementation for all polynomial types). @@ -80,20 +209,20 @@ cdef class flint_poly(flint_elem): s.append("%s" % c) elif i == 1: if c == "1": - s.append("x") + s.append(var) else: - s.append("%s*x" % c) + s.append(f"{c}*{var}") else: if c == "1": - s.append("x^%s" % i) + s.append(f"{var}^{i}") else: - s.append("%s*x^%s" % (c, i)) + s.append(f"{c}*{var}^{i}") return " + ".join(s) def roots(self): """ Computes all the roots in the base ring of the polynomial. - Returns a list of all pairs (*v*, *m*) where *v* is the + Returns a list of all pairs (*v*, *m*) where *v* is the integer root and *m* is the multiplicity of the root. To compute complex roots of a polynomial, instead use diff --git a/src/flint/flintlib/fq.pxd b/src/flint/flintlib/fq.pxd new file mode 100644 index 00000000..b98daa44 --- /dev/null +++ b/src/flint/flintlib/fq.pxd @@ -0,0 +1,109 @@ +from flint.flintlib.flint cimport flint_bitcnt_t, fmpz_struct, slong, flint_rand_t, ulong +from flint.flintlib.fmpz cimport fmpz_t, fmpz_struct +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t +from flint.flintlib.fmpz_mod_mat cimport fmpz_mod_mat_t +from flint.flintlib.fmpz_poly cimport fmpz_poly_t, fmpz_poly_struct +from flint.flintlib.fmpz_mod_poly cimport fmpz_mod_poly_t, fmpz_mod_poly_struct + +cdef extern from "flint/fq.h": + + ctypedef fmpz_poly_t fq_t + ctypedef fmpz_poly_struct fq_struct + + ctypedef struct fq_ctx_struct: + fmpz_mod_ctx_t ctxp + + int sparse_modulus + int is_conway # whether field was initialized with the Flint Conway tables (assures primitivity) + + fmpz_struct * a + slong * j + slong len + + fmpz_mod_poly_t modulus + fmpz_mod_poly_t inv + + char * var + + ctypedef fq_ctx_struct fq_ctx_t[1] + + void fq_ctx_init(fq_ctx_t ctx, const fmpz_t p, slong d, const char *var) + int _fq_ctx_init_conway(fq_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_ctx_init_conway(fq_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_ctx_init_modulus(fq_ctx_t ctx, const fmpz_mod_poly_t modulus, const fmpz_mod_ctx_t ctxp, const char *var) + void fq_ctx_clear(fq_ctx_t ctx) + const fmpz_mod_poly_struct* fq_ctx_modulus(const fq_ctx_t ctx) + long fq_ctx_degree(const fq_ctx_t ctx) + fmpz_struct * fq_ctx_prime(const fq_ctx_t ctx) + void fq_ctx_order(fmpz_t f, const fq_ctx_t ctx) + # int fq_ctx_fprint(FILE * file, const fq_ctx_t ctx) + void fq_ctx_print(const fq_ctx_t ctx) + void fq_ctx_randtest(fq_ctx_t ctx) + void fq_ctx_randtest_reducible(fq_ctx_t ctx) + void fq_init(fq_t rop, const fq_ctx_t ctx) + void fq_init2(fq_t rop, const fq_ctx_t ctx) + void fq_clear(fq_t rop, const fq_ctx_t ctx) + void _fq_sparse_reduce(fmpz_struct *R, slong lenR, const fq_ctx_t ctx) + void _fq_dense_reduce(fmpz_struct *R, slong lenR, const fq_ctx_t ctx) + void _fq_reduce(fmpz_struct *r, slong lenR, const fq_ctx_t ctx) + void fq_reduce(fq_t rop, const fq_ctx_t ctx) + void fq_add(fq_t rop, const fq_t op1, const fq_t op2, const fq_ctx_t ctx) + void fq_sub(fq_t rop, const fq_t op1, const fq_t op2, const fq_ctx_t ctx) + void fq_sub_one(fq_t rop, const fq_t op1, const fq_ctx_t ctx) + void fq_neg(fq_t rop, const fq_t op, const fq_ctx_t ctx) + void fq_mul(fq_t rop, const fq_t op1, const fq_t op2, const fq_ctx_t ctx) + void fq_mul_fmpz(fq_t rop, const fq_t op, const fmpz_t x, const fq_ctx_t ctx) + void fq_mul_si(fq_t rop, const fq_t op, slong x, const fq_ctx_t ctx) + void fq_mul_ui(fq_t rop, const fq_t op, ulong x, const fq_ctx_t ctx) + void fq_sqr(fq_t rop, const fq_t op, const fq_ctx_t ctx) + void fq_div(fq_t rop, const fq_t op1, const fq_t op2, const fq_ctx_t ctx) + void _fq_inv(fmpz_struct *rop, const fmpz_struct *op, slong len, const fq_ctx_t ctx) + void fq_inv(fq_t rop, const fq_t op, const fq_ctx_t ctx) + void fq_gcdinv(fq_t f, fq_t inv, const fq_t op, const fq_ctx_t ctx) + void _fq_pow(fmpz_struct *rop, const fmpz_struct *op, slong len, const fmpz_t e, const fq_ctx_t ctx) + void fq_pow(fq_t rop, const fq_t op, const fmpz_t e, const fq_ctx_t ctx) + void fq_pow_ui(fq_t rop, const fq_t op, const ulong e, const fq_ctx_t ctx) + int fq_sqrt(fq_t rop, const fq_t op1, const fq_ctx_t ctx) + void fq_pth_root(fq_t rop, const fq_t op1, const fq_ctx_t ctx) + int fq_is_square(const fq_t op, const fq_ctx_t ctx) + # int fq_fprint_pretty(FILE *file, const fq_t op, const fq_ctx_t ctx) + int fq_print_pretty(const fq_t op, const fq_ctx_t ctx) + # void fq_fprint(FILE * file, const fq_t op, const fq_ctx_t ctx) + void fq_print(const fq_t op, const fq_ctx_t ctx) + char * fq_get_str(const fq_t op, const fq_ctx_t ctx) + char * fq_get_str_pretty(const fq_t op, const fq_ctx_t ctx) + void fq_randtest(fq_t rop, flint_rand_t state, const fq_ctx_t ctx) + void fq_randtest_not_zero(fq_t rop, flint_rand_t state, const fq_ctx_t ctx) + void fq_randtest_dense(fq_t rop, flint_rand_t state, const fq_ctx_t ctx) + void fq_rand(fq_t rop, flint_rand_t state, const fq_ctx_t ctx) + void fq_rand_not_zero(fq_t rop, flint_rand_t state, const fq_ctx_t ctx) + void fq_set(fq_t rop, const fq_t op, const fq_ctx_t ctx) + void fq_set_si(fq_t rop, const slong x, const fq_ctx_t ctx) + void fq_set_ui(fq_t rop, const ulong x, const fq_ctx_t ctx) + void fq_set_fmpz(fq_t rop, const fmpz_t x, const fq_ctx_t ctx) + void fq_swap(fq_t op1, fq_t op2, const fq_ctx_t ctx) + void fq_zero(fq_t rop, const fq_ctx_t ctx) + void fq_one(fq_t rop, const fq_ctx_t ctx) + void fq_gen(fq_t rop, const fq_ctx_t ctx) + int fq_get_fmpz(fmpz_t rop, const fq_t op, const fq_ctx_t ctx) + void fq_get_fmpz_poly(fmpz_poly_t a, const fq_t b, const fq_ctx_t ctx) + void fq_get_fmpz_mod_poly(fmpz_mod_poly_t a, const fq_t b, const fq_ctx_t ctx) + void fq_set_fmpz_poly(fq_t a, const fmpz_poly_t b, const fq_ctx_t ctx) + void fq_set_fmpz_mod_poly(fq_t a, const fmpz_mod_poly_t b, const fq_ctx_t ctx) + void fq_get_fmpz_mod_mat(fmpz_mod_mat_t col, const fq_t a, const fq_ctx_t ctx) + void fq_set_fmpz_mod_mat(fq_t a, const fmpz_mod_mat_t col, const fq_ctx_t ctx) + int fq_is_zero(const fq_t op, const fq_ctx_t ctx) + int fq_is_one(const fq_t op, const fq_ctx_t ctx) + int fq_equal(const fq_t op1, const fq_t op2, const fq_ctx_t ctx) + int fq_is_invertible(const fq_t op, const fq_ctx_t ctx) + int fq_is_invertible_f(fq_t f, const fq_t op, const fq_ctx_t ctx) + void _fq_trace(fmpz_t rop, const fmpz_struct *op, slong len, const fq_ctx_t ctx) + void fq_trace(fmpz_t rop, const fq_t op, const fq_ctx_t ctx) + void _fq_norm(fmpz_t rop, const fmpz_struct *op, slong len, const fq_ctx_t ctx) + void fq_norm(fmpz_t rop, const fq_t op, const fq_ctx_t ctx) + void _fq_frobenius(fmpz_struct *rop, const fmpz_struct *op, slong len, slong e, const fq_ctx_t ctx) + void fq_frobenius(fq_t rop, const fq_t op, slong e, const fq_ctx_t ctx) + int fq_multiplicative_order(fmpz_t ord, const fq_t op, const fq_ctx_t ctx) + int fq_is_primitive(const fq_t op, const fq_ctx_t ctx) + void fq_bit_pack(fmpz_t f, const fq_t op, flint_bitcnt_t bit_size, const fq_ctx_t ctx) + void fq_bit_unpack(fq_t rop, const fmpz_t f, flint_bitcnt_t bit_size, const fq_ctx_t ctx) diff --git a/src/flint/flintlib/fq_default.pxd b/src/flint/flintlib/fq_default.pxd new file mode 100644 index 00000000..ac298c62 --- /dev/null +++ b/src/flint/flintlib/fq_default.pxd @@ -0,0 +1,108 @@ +from flint.flintlib.fmpz_poly cimport fmpz_poly_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.flint cimport slong, flint_rand_t, ulong, mp_limb_t + +from flint.flintlib.nmod cimport nmod_t +from flint.flintlib.nmod_poly cimport nmod_poly_t +from flint.flintlib.fmpz_mod cimport fmpz_mod_ctx_t +from flint.flintlib.fmpz_mod_poly cimport fmpz_mod_poly_t + +from flint.flintlib.fq cimport fq_t, fq_ctx_t +from flint.flintlib.fq_nmod cimport fq_nmod_t, fq_nmod_ctx_t +from flint.flintlib.fq_zech cimport fq_zech_t, fq_zech_ctx_t + +cdef extern from "flint/fq_default.h": + ctypedef union fq_default_struct: + fq_t fq + fq_nmod_t fq_nmod + fq_zech_t fq_zech + ulong nmod + fmpz_t fmpz_mod + ctypedef fq_default_struct fq_default_t[1] + + ctypedef struct fq_default_fmpz_mod_ctx_struct: + fmpz_mod_ctx_t mod + fmpz_t a # minpoly is x - a + + ctypedef struct fq_default_nmod_ctx_struct: + nmod_t mod + mp_limb_t a # minpoly is x - a + + ctypedef union fq_default_ctx_struct: + fq_ctx_t fq + fq_nmod_ctx_t fq_nmod + fq_zech_ctx_t fq_zech + fq_default_nmod_ctx_struct nmod + fq_default_fmpz_mod_ctx_struct fmpz_mod + ctypedef fq_default_ctx_struct fq_default_ctx_t[1] + + # Parsed from here + void fq_default_ctx_init(fq_default_ctx_t ctx, const fmpz_t p, slong d, const char * var) + void fq_default_ctx_init_type(fq_default_ctx_t ctx, const fmpz_t p, slong d, const char * var, int type) + void fq_default_ctx_init_modulus(fq_default_ctx_t ctx, const fmpz_mod_poly_t modulus, fmpz_mod_ctx_t mod_ctx, const char * var) + void fq_default_ctx_init_modulus_type(fq_default_ctx_t ctx, const fmpz_mod_poly_t modulus, fmpz_mod_ctx_t mod_ctx, const char * var, int type) + void fq_default_ctx_init_modulus_nmod(fq_default_ctx_t ctx, const nmod_poly_t modulus, const char * var) + void fq_default_ctx_init_modulus_nmod_type(fq_default_ctx_t ctx, const nmod_poly_t modulus, const char * var, int type) + void fq_default_ctx_clear(fq_default_ctx_t ctx) + int fq_default_ctx_type(const fq_default_ctx_t ctx) + slong fq_default_ctx_degree(const fq_default_ctx_t ctx) + void fq_default_ctx_prime(fmpz_t prime, const fq_default_ctx_t ctx) + void fq_default_ctx_order(fmpz_t f, const fq_default_ctx_t ctx) + void fq_default_ctx_modulus(fmpz_mod_poly_t p, const fq_default_ctx_t ctx) + # int fq_default_ctx_fprint(FILE * file, const fq_default_ctx_t ctx) + void fq_default_ctx_print(const fq_default_ctx_t ctx) + void fq_default_ctx_randtest(fq_default_ctx_t ctx) + void fq_default_get_coeff_fmpz(fmpz_t c, fq_default_t op, slong n, const fq_default_ctx_t ctx) + void fq_default_init(fq_default_t rop, const fq_default_ctx_t ctx) + void fq_default_init2(fq_default_t rop, const fq_default_ctx_t ctx) + void fq_default_clear(fq_default_t rop, const fq_default_ctx_t ctx) + int fq_default_is_invertible(const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_add(fq_default_t rop, const fq_default_t op1, const fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_sub(fq_default_t rop, const fq_default_t op1, const fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_sub_one(fq_default_t rop, const fq_default_t op1, const fq_default_ctx_t ctx) + void fq_default_neg(fq_default_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_mul(fq_default_t rop, const fq_default_t op1, const fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_mul_fmpz(fq_default_t rop, const fq_default_t op, const fmpz_t x, const fq_default_ctx_t ctx) + void fq_default_mul_si(fq_default_t rop, const fq_default_t op, slong x, const fq_default_ctx_t ctx) + void fq_default_mul_ui(fq_default_t rop, const fq_default_t op, ulong x, const fq_default_ctx_t ctx) + void fq_default_sqr(fq_default_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_div(fq_default_t rop, const fq_default_t op1, const fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_inv(fq_default_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_pow(fq_default_t rop, const fq_default_t op, const fmpz_t e, const fq_default_ctx_t ctx) + void fq_default_pow_ui(fq_default_t rop, const fq_default_t op, const ulong e, const fq_default_ctx_t ctx) + int fq_default_sqrt(fq_default_t rop, const fq_default_t op1, const fq_default_ctx_t ctx) + void fq_default_pth_root(fq_default_t rop, const fq_default_t op1, const fq_default_ctx_t ctx) + int fq_default_is_square(const fq_default_t op, const fq_default_ctx_t ctx) + # int fq_default_fprint_pretty(FILE *file, const fq_default_t op, const fq_default_ctx_t ctx) + int fq_default_print_pretty(const fq_default_t op, const fq_default_ctx_t ctx) + # void fq_default_fprint(FILE * file, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_print(const fq_default_t op, const fq_default_ctx_t ctx) + char * fq_default_get_str(const fq_default_t op, const fq_default_ctx_t ctx) + char * fq_default_get_str_pretty(const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_randtest(fq_default_t rop, flint_rand_t state, const fq_default_ctx_t ctx) + void fq_default_randtest_not_zero(fq_default_t rop, flint_rand_t state, const fq_default_ctx_t ctx) + void fq_default_rand(fq_default_t rop, flint_rand_t state, const fq_default_ctx_t ctx) + void fq_default_rand_not_zero(fq_default_t rop, flint_rand_t state, const fq_default_ctx_t ctx) + void fq_default_set(fq_default_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_set_si(fq_default_t rop, const slong x, const fq_default_ctx_t ctx) + void fq_default_set_ui(fq_default_t rop, const ulong x, const fq_default_ctx_t ctx) + void fq_default_set_fmpz(fq_default_t rop, const fmpz_t x, const fq_default_ctx_t ctx) + void fq_default_swap(fq_default_t op1, fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_zero(fq_default_t rop, const fq_default_ctx_t ctx) + void fq_default_one(fq_default_t rop, const fq_default_ctx_t ctx) + void fq_default_gen(fq_default_t rop, const fq_default_ctx_t ctx) + int fq_default_get_fmpz(fmpz_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_get_nmod_poly(nmod_poly_t poly, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_set_nmod_poly(fq_default_t op, const nmod_poly_t poly, const fq_default_ctx_t ctx) + # void fq_default_get_fmpz_mod_poly(fmpz_mod_poly_t poly, const fq_default_t op, const fmpz_mod_ctx_t mod_ctx, const fq_default_ctx_t ctx) + # void fq_default_set_fmpz_mod_poly(fq_default_t op, const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t mod_ctx, const fq_default_ctx_t ctx) + void fq_default_get_fmpz_mod_poly(fmpz_mod_poly_t poly, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_set_fmpz_mod_poly(fq_default_t op, const fmpz_mod_poly_t poly, const fq_default_ctx_t ctx) + void fq_default_get_fmpz_poly(fmpz_poly_t a, const fq_default_t b, const fq_default_ctx_t ctx) + void fq_default_set_fmpz_poly(fq_default_t a, const fmpz_poly_t b, const fq_default_ctx_t ctx) + int fq_default_is_zero(const fq_default_t op, const fq_default_ctx_t ctx) + int fq_default_is_one(const fq_default_t op, const fq_default_ctx_t ctx) + int fq_default_equal(const fq_default_t op1, const fq_default_t op2, const fq_default_ctx_t ctx) + void fq_default_trace(fmpz_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_norm(fmpz_t rop, const fq_default_t op, const fq_default_ctx_t ctx) + void fq_default_frobenius(fq_default_t rop, const fq_default_t op, slong e, const fq_default_ctx_t ctx) diff --git a/src/flint/flintlib/fq_nmod.pxd b/src/flint/flintlib/fq_nmod.pxd new file mode 100644 index 00000000..c273be9d --- /dev/null +++ b/src/flint/flintlib/fq_nmod.pxd @@ -0,0 +1,109 @@ +# unimported types {'fq_nmod_t', 'fq_nmod_ctx_t'} +from flint.flintlib.fmpz cimport fmpz_t, fmpz_struct +from flint.flintlib.nmod_poly cimport nmod_poly_t +from flint.flintlib.flint cimport mp_ptr, mp_limb_t, slong, mp_srcptr, flint_rand_t, flint_bitcnt_t, ulong +from flint.flintlib.nmod cimport nmod_t +from flint.flintlib.nmod_mat cimport nmod_mat_t +from flint.flintlib.nmod_poly cimport nmod_poly_struct, nmod_poly_t + +cdef extern from "flint/fq_nmod.h": + + ctypedef nmod_poly_t fq_nmod_t + ctypedef nmod_poly_struct fq_nmod_struct + + ctypedef struct fq_nmod_ctx_struct: + fmpz_struct p + nmod_t mod + + int sparse_modulus + int is_conway # whether field was generated using Flint Conway table (assures primitivity + + mp_limb_t *a + slong *j + slong len + + nmod_poly_t modulus + nmod_poly_t inv + + char *var + ctypedef fq_nmod_ctx_struct fq_nmod_ctx_t[1] + + + void fq_nmod_ctx_init(fq_nmod_ctx_t ctx, const fmpz_t p, slong d, const char *var) + int _fq_nmod_ctx_init_conway(fq_nmod_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_nmod_ctx_init_conway(fq_nmod_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_nmod_ctx_init_modulus(fq_nmod_ctx_t ctx, nmod_poly_t modulus, const char *var) + void fq_nmod_ctx_clear(fq_nmod_ctx_t ctx) + const nmod_poly_struct* fq_nmod_ctx_modulus(const fq_nmod_ctx_t ctx) + long fq_nmod_ctx_degree(const fq_nmod_ctx_t ctx) + fmpz_struct * fq_nmod_ctx_prime(const fq_nmod_ctx_t ctx) + void fq_nmod_ctx_order(fmpz_t f, const fq_nmod_ctx_t ctx) + # int fq_nmod_ctx_fprint(FILE * file, const fq_nmod_ctx_t ctx) + void fq_nmod_ctx_print(const fq_nmod_ctx_t ctx) + void fq_nmod_ctx_randtest(fq_nmod_ctx_t ctx) + void fq_nmod_ctx_randtest_reducible(fq_nmod_ctx_t ctx) + void fq_nmod_init(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void fq_nmod_init2(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void fq_nmod_clear(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void _fq_nmod_sparse_reduce(mp_ptr R, slong lenR, const fq_nmod_ctx_t ctx) + void _fq_nmod_dense_reduce(mp_ptr R, slong lenR, const fq_nmod_ctx_t ctx) + void _fq_nmod_reduce(mp_ptr r, slong lenR, const fq_nmod_ctx_t ctx) + void fq_nmod_reduce(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void fq_nmod_add(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_t op2, const fq_nmod_ctx_t ctx) + void fq_nmod_sub(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_t op2, const fq_nmod_ctx_t ctx) + void fq_nmod_sub_one(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_ctx_t ctx) + void fq_nmod_neg(fq_nmod_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_mul(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_t op2, const fq_nmod_ctx_t ctx) + void fq_nmod_mul_fmpz(fq_nmod_t rop, const fq_nmod_t op, const fmpz_t x, const fq_nmod_ctx_t ctx) + void fq_nmod_mul_si(fq_nmod_t rop, const fq_nmod_t op, slong x, const fq_nmod_ctx_t ctx) + void fq_nmod_mul_ui(fq_nmod_t rop, const fq_nmod_t op, ulong x, const fq_nmod_ctx_t ctx) + void fq_nmod_sqr(fq_nmod_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void _fq_nmod_inv(mp_ptr * rop, mp_srcptr * op, slong len, const fq_nmod_ctx_t ctx) + void fq_nmod_inv(fq_nmod_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_gcdinv(fq_nmod_t f, fq_nmod_t inv, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void _fq_nmod_pow(mp_ptr * rop, mp_srcptr * op, slong len, const fmpz_t e, const fq_nmod_ctx_t ctx) + void fq_nmod_pow(fq_nmod_t rop, const fq_nmod_t op, const fmpz_t e, const fq_nmod_ctx_t ctx) + void fq_nmod_pow_ui(fq_nmod_t rop, const fq_nmod_t op, const ulong e, const fq_nmod_ctx_t ctx) + void fq_nmod_sqrt(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_ctx_t ctx) + void fq_nmod_pth_root(fq_nmod_t rop, const fq_nmod_t op1, const fq_nmod_ctx_t ctx) + int fq_nmod_is_square(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + # int fq_nmod_fprint_pretty(FILE * file, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_print_pretty(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + # void fq_nmod_fprint(FILE * file, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_print(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + char * fq_nmod_get_str(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + char * fq_nmod_get_str_pretty(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_randtest(fq_nmod_t rop, flint_rand_t state, const fq_nmod_ctx_t ctx) + void fq_nmod_randtest_not_zero(fq_nmod_t rop, flint_rand_t state, const fq_nmod_ctx_t ctx) + void fq_nmod_randtest_dense(fq_nmod_t rop, flint_rand_t state, const fq_nmod_ctx_t ctx) + void fq_nmod_rand(fq_nmod_t rop, flint_rand_t state, const fq_nmod_ctx_t ctx) + void fq_nmod_rand_not_zero(fq_nmod_t rop, flint_rand_t state, const fq_nmod_ctx_t ctx) + void fq_nmod_set(fq_nmod_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_set_si(fq_nmod_t rop, const slong x, const fq_nmod_ctx_t ctx) + void fq_nmod_set_ui(fq_nmod_t rop, const ulong x, const fq_nmod_ctx_t ctx) + void fq_nmod_set_fmpz(fq_nmod_t rop, const fmpz_t x, const fq_nmod_ctx_t ctx) + void fq_nmod_swap(fq_nmod_t op1, fq_nmod_t op2, const fq_nmod_ctx_t ctx) + void fq_nmod_zero(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void fq_nmod_one(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + void fq_nmod_gen(fq_nmod_t rop, const fq_nmod_ctx_t ctx) + int fq_nmod_get_fmpz(fmpz_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_get_nmod_poly(nmod_poly_t a, const fq_nmod_t b, const fq_nmod_ctx_t ctx); + void fq_nmod_set_nmod_poly(fq_nmod_t a, const nmod_poly_t b, const fq_nmod_ctx_t ctx); + void fq_nmod_get_nmod_mat(nmod_mat_t col, const fq_nmod_t a, const fq_nmod_ctx_t ctx) + void fq_nmod_set_nmod_mat(fq_nmod_t a, const nmod_mat_t col, const fq_nmod_ctx_t ctx) + int fq_nmod_is_zero(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_is_one(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_equal(const fq_nmod_t op1, const fq_nmod_t op2, const fq_nmod_ctx_t ctx) + int fq_nmod_is_invertible(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_is_invertible_f(fq_nmod_t f, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_cmp(const fq_nmod_t a, const fq_nmod_t b, const fq_nmod_ctx_t ctx) + void _fq_nmod_trace(fmpz_t rop, mp_srcptr * op, slong len, const fq_nmod_ctx_t ctx) + void fq_nmod_trace(fmpz_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void _fq_nmod_norm(fmpz_t rop, mp_srcptr * op, slong len, const fq_nmod_ctx_t ctx) + void fq_nmod_norm(fmpz_t rop, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void _fq_nmod_frobenius(mp_ptr * rop, mp_srcptr * op, slong len, slong e, const fq_nmod_ctx_t ctx) + void fq_nmod_frobenius(fq_nmod_t rop, const fq_nmod_t op, slong e, const fq_nmod_ctx_t ctx) + int fq_nmod_multiplicative_order(fmpz_t ord, const fq_nmod_t op, const fq_nmod_ctx_t ctx) + int fq_nmod_is_primitive(const fq_nmod_t op, const fq_nmod_ctx_t ctx) + void fq_nmod_bit_pack(fmpz_t f, const fq_nmod_t op, flint_bitcnt_t bit_size, const fq_nmod_ctx_t ctx) + void fq_nmod_bit_unpack(fq_nmod_t rop, const fmpz_t f, flint_bitcnt_t bit_size, const fq_nmod_ctx_t ctx) diff --git a/src/flint/flintlib/fq_zech.pxd b/src/flint/flintlib/fq_zech.pxd new file mode 100644 index 00000000..531536ee --- /dev/null +++ b/src/flint/flintlib/fq_zech.pxd @@ -0,0 +1,111 @@ +from flint.flintlib.flint cimport ulong, slong, flint_bitcnt_t, mp_ptr, flint_rand_t, mp_srcptr, mp_limb_t +from flint.flintlib.fmpz cimport fmpz_t, fmpz_struct +from flint.flintlib.nmod_mat cimport nmod_mat_t +from flint.flintlib.nmod_poly cimport nmod_poly_t, nmod_poly_struct +from flint.flintlib.fq_nmod cimport fq_nmod_t, fq_nmod_ctx_t, fq_nmod_ctx_struct + + +cdef extern from "flint/fq_zech.h": + ctypedef struct fq_zech_struct: + mp_limb_t value + ctypedef fq_zech_struct fq_zech_t[1] + + ctypedef struct fq_zech_ctx_struct: + mp_limb_t qm1 # q - 1 + mp_limb_t qm1o2 # (q - 1) / 2 or 1 when p == 2 + mp_limb_t qm1opm1 # (q - 1) / (p - 1) + mp_limb_t p + double ppre + mp_limb_t prime_root # primitive root for prime subfield + mp_limb_t * zech_log_table + mp_limb_t * prime_field_table + mp_limb_t * eval_table + + fq_nmod_ctx_struct * fq_nmod_ctx + int owns_fq_nmod_ctx + int is_conway # whether field was generated using Flint Conway tables (assures primitivity) + ctypedef fq_zech_ctx_struct fq_zech_ctx_t[1] + + # PArsed from here + void fq_zech_ctx_init(fq_zech_ctx_t ctx, const fmpz_t p, slong d, const char *var) + int _fq_zech_ctx_init_conway(fq_zech_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_zech_ctx_init_conway(fq_zech_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_zech_ctx_init_random(fq_zech_ctx_t ctx, const fmpz_t p, slong d, const char *var) + void fq_zech_ctx_init_modulus(fq_zech_ctx_t ctx, nmod_poly_t modulus, const char *var) + int fq_zech_ctx_init_modulus_check(fq_zech_ctx_t ctx, nmod_poly_t modulus, const char *var) + void fq_zech_ctx_init_fq_nmod_ctx(fq_zech_ctx_t ctx, fq_nmod_ctx_t ctxn) + int fq_zech_ctx_init_fq_nmod_ctx_check(fq_zech_ctx_t ctx, fq_nmod_ctx_t ctxn) + void fq_zech_ctx_clear(fq_zech_ctx_t ctx) + const nmod_poly_struct* fq_zech_ctx_modulus(const fq_zech_ctx_t ctx) + long fq_zech_ctx_degree(const fq_zech_ctx_t ctx) + fmpz_struct * fq_zech_ctx_prime(const fq_zech_ctx_t ctx) + void fq_zech_ctx_order(fmpz_t f, const fq_zech_ctx_t ctx) + mp_limb_t fq_zech_ctx_order_ui(const fq_zech_ctx_t ctx) + # int fq_zech_ctx_fprint(FILE * file, const fq_zech_ctx_t ctx) + void fq_zech_ctx_print(const fq_zech_ctx_t ctx) + void fq_zech_ctx_randtest(fq_zech_ctx_t ctx) + void fq_zech_ctx_randtest_reducible(fq_zech_ctx_t ctx) + void fq_zech_init(fq_zech_t rop, const fq_zech_ctx_t ctx) + void fq_zech_init2(fq_zech_t rop, const fq_zech_ctx_t ctx) + void fq_zech_clear(fq_zech_t rop, const fq_zech_ctx_t ctx) + void _fq_zech_sparse_reduce(mp_ptr R, slong lenR, const fq_zech_ctx_t ctx) + void _fq_zech_dense_reduce(mp_ptr R, slong lenR, const fq_zech_ctx_t ctx) + void _fq_zech_reduce(mp_ptr r, slong lenR, const fq_zech_ctx_t ctx) + void fq_zech_reduce(fq_zech_t rop, const fq_zech_ctx_t ctx) + void fq_zech_add(fq_zech_t rop, const fq_zech_t op1, const fq_zech_t op2, const fq_zech_ctx_t ctx) + void fq_zech_sub(fq_zech_t rop, const fq_zech_t op1, const fq_zech_t op2, const fq_zech_ctx_t ctx) + void fq_zech_sub_one(fq_zech_t rop, const fq_zech_t op1, const fq_zech_ctx_t ctx) + void fq_zech_neg(fq_zech_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_mul(fq_zech_t rop, const fq_zech_t op1, const fq_zech_t op2, const fq_zech_ctx_t ctx) + void fq_zech_mul_fmpz(fq_zech_t rop, const fq_zech_t op, const fmpz_t x, const fq_zech_ctx_t ctx) + void fq_zech_mul_si(fq_zech_t rop, const fq_zech_t op, slong x, const fq_zech_ctx_t ctx) + void fq_zech_mul_ui(fq_zech_t rop, const fq_zech_t op, ulong x, const fq_zech_ctx_t ctx) + void fq_zech_sqr(fq_zech_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_div(fq_zech_t rop, const fq_zech_t op1, const fq_zech_t op2, const fq_zech_ctx_t ctx) + void _fq_zech_inv(mp_ptr *rop, mp_srcptr *op, slong len, const fq_zech_ctx_t ctx) + void fq_zech_inv(fq_zech_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_gcdinv(fq_zech_t f, fq_zech_t inv, const fq_zech_t op, const fq_zech_ctx_t ctx) + void _fq_zech_pow(mp_ptr *rop, mp_srcptr *op, slong len, const fmpz_t e, const fq_zech_ctx_t ctx) + void fq_zech_pow(fq_zech_t rop, const fq_zech_t op, const fmpz_t e, const fq_zech_ctx_t ctx) + void fq_zech_pow_ui(fq_zech_t rop, const fq_zech_t op, const ulong e, const fq_zech_ctx_t ctx) + void fq_zech_sqrt(fq_zech_t rop, const fq_zech_t op1, const fq_zech_ctx_t ctx) + void fq_zech_pth_root(fq_zech_t rop, const fq_zech_t op1, const fq_zech_ctx_t ctx) + int fq_zech_is_square(const fq_zech_t op, const fq_zech_ctx_t ctx) + # int fq_zech_fprint_pretty(FILE *file, const fq_zech_t op, const fq_zech_ctx_t ctx) + int fq_zech_print_pretty(const fq_zech_t op, const fq_zech_ctx_t ctx) + # void fq_zech_fprint(FILE * file, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_print(const fq_zech_t op, const fq_zech_ctx_t ctx) + char * fq_zech_get_str(const fq_zech_t op, const fq_zech_ctx_t ctx) + char * fq_zech_get_str_pretty(const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_randtest(fq_zech_t rop, flint_rand_t state, const fq_zech_ctx_t ctx) + void fq_zech_randtest_not_zero(fq_zech_t rop, flint_rand_t state, const fq_zech_ctx_t ctx) + void fq_zech_randtest_dense(fq_zech_t rop, flint_rand_t state, const fq_zech_ctx_t ctx) + void fq_zech_rand(fq_zech_t rop, flint_rand_t state, const fq_zech_ctx_t ctx) + void fq_zech_rand_not_zero(fq_zech_t rop, flint_rand_t state, const fq_zech_ctx_t ctx) + void fq_zech_set(fq_zech_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_set_si(fq_zech_t rop, const slong x, const fq_zech_ctx_t ctx) + void fq_zech_set_ui(fq_zech_t rop, const ulong x, const fq_zech_ctx_t ctx) + void fq_zech_set_fmpz(fq_zech_t rop, const fmpz_t x, const fq_zech_ctx_t ctx) + void fq_zech_swap(fq_zech_t op1, fq_zech_t op2, const fq_zech_ctx_t ctx) + void fq_zech_zero(fq_zech_t rop, const fq_zech_ctx_t ctx) + void fq_zech_one(fq_zech_t rop, const fq_zech_ctx_t ctx) + void fq_zech_gen(fq_zech_t rop, const fq_zech_ctx_t ctx) + int fq_zech_get_fmpz(fmpz_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_get_fq_nmod(fq_nmod_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_set_fq_nmod(fq_zech_t rop, const fq_nmod_t op, const fq_zech_ctx_t ctx) + void fq_zech_get_nmod_poly(nmod_poly_t a, const fq_zech_t b, const fq_zech_ctx_t ctx) + void fq_zech_set_nmod_poly(fq_zech_t a, const nmod_poly_t b, const fq_zech_ctx_t ctx) + void fq_zech_get_nmod_mat(nmod_mat_t col, const fq_zech_t a, const fq_zech_ctx_t ctx) + void fq_zech_set_nmod_mat(fq_zech_t a, const nmod_mat_t col, const fq_zech_ctx_t ctx) + int fq_zech_is_zero(const fq_zech_t op, const fq_zech_ctx_t ctx) + int fq_zech_is_one(const fq_zech_t op, const fq_zech_ctx_t ctx) + int fq_zech_equal(const fq_zech_t op1, const fq_zech_t op2, const fq_zech_ctx_t ctx) + int fq_zech_is_invertible(const fq_zech_t op, const fq_zech_ctx_t ctx) + int fq_zech_is_invertible_f(fq_zech_t f, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_trace(fmpz_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_norm(fmpz_t rop, const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_frobenius(fq_zech_t rop, const fq_zech_t op, slong e, const fq_zech_ctx_t ctx) + int fq_zech_multiplicative_order(fmpz_t ord, const fq_zech_t op, const fq_zech_ctx_t ctx) + int fq_zech_is_primitive(const fq_zech_t op, const fq_zech_ctx_t ctx) + void fq_zech_bit_pack(fmpz_t f, const fq_zech_t op, flint_bitcnt_t bit_size, const fq_zech_ctx_t ctx) + void fq_zech_bit_unpack(fq_zech_t rop, const fmpz_t f, flint_bitcnt_t bit_size, const fq_zech_ctx_t ctx) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index f837f63a..580e90c6 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -71,6 +71,7 @@ def run_doctests(verbose=None): flint.types.nmod_poly, flint.types.nmod_mat, flint.types.nmod_series, + flint.types.fq_default, flint.types.arf, flint.types.arb, flint.types.arb_poly, diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index aca7668e..f10ab63c 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3585,6 +3585,163 @@ def test_matrices_transpose(): assert M1234.transpose() == M([[1, 4], [2, 5], [3, 6]]) +def test_fq_default(): + # test fq_default context creation + # TODO + + # GF(5) + gf_5 = flint.fq_default_ctx(5) + gf_5_ = flint.fq_default_ctx(5) + + # GF(5^2) + gf_5_2 = flint.fq_default_ctx(5, 2) + gf_5_2_ = flint.fq_default_ctx(5, 2) + + # GF((2**127 - 1)^2) + gf_127 = flint.fq_default_ctx(2**127 - 1, 2) + gf_127_2 = flint.fq_default_ctx(2**127 - 1, 2) + + assert (gf_5 == gf_5_) is True + assert (gf_5 != gf_5_) is False + assert (gf_5 == gf_5_2) is False + assert (gf_5 != gf_5_2) is True + + assert gf_5.prime() == gf_5_2.prime() == 5 + assert gf_5_2.order() == 5*5 + assert gf_5_2.multiplicative_order() == 5*5 - 1 + assert gf_127_2.prime() == 2**127 - 1 + + assert gf_5_2(0) == gf_5_2.zero() + assert gf_5_2(1) == gf_5_2.one() + assert gf_5_2.gen() == gf_5_2([0, 1]) + + assert str(gf_5) == "Context for fq_default in GF(5)" + assert str(gf_5_2) == "Context for fq_default in GF(5^2)[x]/(x^2 + 4*x + 2)" + + # coercision + assert gf_5(1) == gf_5.one() + assert gf_5(flint.fmpz(1)) == gf_5.one() + assert gf_5(-1) == -gf_5.one() + assert gf_5(flint.fmpz(-1)) == -gf_5.one() + R = flint.fmpz_mod_ctx(5) + assert gf_5(R(1)) == gf_5.one() + assert gf_5(R(-1)) == -gf_5.one() + assert gf_5(flint.nmod(1, 5)) == gf_5.one() + assert gf_5(flint.nmod(-1, 5)) == -gf_5.one() + assert gf_5([0, 1]) == gf_5.gen() + assert gf_5(flint.fmpz_poly([0, 1])) == gf_5.gen() + R = flint.fmpz_mod_poly_ctx(5) + assert gf_5.gen() == gf_5(R.gen()) + assert gf_5.gen() == gf_5(flint.nmod_poly([0, 1], 5)) + + + # testing various equalties between types + + # integers are the same if charactersitic is the same + # even with extensions + assert gf_5.one() == gf_5_.one() + assert gf_5.one() == gf_5_2.one() + assert gf_5.one() != gf_127.one() + + # the generators for different extensions + assert gf_5_2([0, 1]) != gf_5([0, 1]) + assert gf_5_2([0, 1]) == gf_5_2_([0, 1]) + assert gf_5_2([0, 1]) != gf_127_2([0, 1]) + + # integers are reduced modulo p before comparison + for int_type in [int, flint.fmpz]: + assert gf_5(1) == int_type(1) + assert gf_5(-1) == int_type(-1) + assert gf_5(-1) == int_type(4) + assert gf_5(4) == int_type(4) + assert gf_5(4) == int_type(-1) + + # integers modulo n also can be compared when they match + assert gf_5(1) == flint.nmod(1, 5) + assert gf_5(-1) == flint.nmod(-1, 5) + assert gf_5(-1) == flint.nmod(4, 5) + assert gf_5_2(1) == flint.nmod(1, 5) + assert gf_5_2(-1) == flint.nmod(-1, 5) + assert gf_5_2(-1) == flint.nmod(4, 5) + + # when the moduli dont match, comparison is always false + assert gf_5(1) != flint.nmod(1, 7) + assert gf_5(-1) != flint.nmod(-1, 7) + assert gf_5(-1) != flint.nmod(4, 7) + assert gf_5_2(1) != flint.nmod(1, 7) + assert gf_5_2(-1) != flint.nmod(-1, 7) + assert gf_5_2(-1) != flint.nmod(4, 7) + + # integers modulo n also can be compared when they match + R5 = flint.fmpz_mod_ctx(5) + assert gf_5(1) == R5(1) + assert gf_5(-1) == R5(-1) + assert gf_5(-1) == R5(4) + assert gf_5_2(1) == R5(1) + assert gf_5_2(-1) == R5(-1) + assert gf_5_2(-1) == R5(4) + + # when the moduli dont match, comparison is always false + R7 = flint.fmpz_mod_ctx(7) + assert gf_5(1) != R7(1) + assert gf_5(-1) != R7(-1) + assert gf_5(-1) != R7(4) + assert gf_5_2(1) != R7(1) + assert gf_5_2(-1) != R7(-1) + assert gf_5_2(-1) != R7(4) + + # test fq_default element arithemtic + + for gf in [gf_5, gf_5_2, gf_127, gf_127_2]: + + assert (gf(0) == gf.zero()) is True + assert (gf(0) != gf.zero()) is False + assert (gf(1) == gf.zero()) is False + assert (gf(1) != gf.zero()) is True + assert raises(lambda: gf.zero() > gf.zero(), TypeError) + assert raises(lambda: gf.zero() >= gf.zero(), TypeError) + assert raises(lambda: gf.zero() < gf.zero(), TypeError) + assert raises(lambda: gf.zero() <= gf.zero(), TypeError) + + assert gf.zero().is_zero() is True + assert gf.one().is_zero() is False + + assert gf.zero().is_one() is False + assert gf.one().is_one() is True + + a = gf.random_element(not_zero=True) + b = gf.random_element(not_zero=True) + c = gf.random_element(not_zero=True) + + assert a + (-a) == gf.zero() + assert a + a == 2*a + assert a * a == a**2 + assert a * a == a.square() + assert a * a * a == pow(a, 3) + + assert (a + b) + c == a + (b + c) + assert (a - b) - c == a - (b + c) + assert (a * b) * c == a * (b * c) + assert (a / b) / c == a / (b * c) + + assert a + 0 == 0 + a == a + assert a + gf.zero() == a + assert a * 1 == 1 * a == a + assert a * gf.one() == a + assert a * gf.zero() == gf.zero() + assert a / a == gf.one() + + assert raises(lambda: a / 0, ZeroDivisionError) + assert raises(lambda: ~gf.zero(), ZeroDivisionError) + assert raises(lambda: pow(gf.zero(), -1), ZeroDivisionError) + + assert 1/a == pow(a, -1) == ~a + assert gf.one() == pow(a, 0) + assert gf.zero() == pow(gf.zero(), 2**64) + assert a == pow(a, 1) + + assert (a*a).is_square() + def test_all_tests(): test_funcs = {f for name, f in globals().items() if name.startswith("test_")} untested = test_funcs - set(all_tests) @@ -3650,6 +3807,8 @@ def test_all_tests(): test_matrices_rref, test_matrices_solve, + test_fq_default, + test_arb, test_pickling, diff --git a/src/flint/types/fq_default.pxd b/src/flint/types/fq_default.pxd new file mode 100644 index 00000000..265cd962 --- /dev/null +++ b/src/flint/types/fq_default.pxd @@ -0,0 +1,57 @@ +from flint.flintlib.fq_default cimport * +from flint.flintlib.fq_zech cimport fq_zech_is_primitive, fq_zech_multiplicative_order +from flint.flintlib.fq_nmod cimport fq_nmod_is_primitive, fq_nmod_multiplicative_order +from flint.flintlib.fq cimport fq_is_primitive, fq_multiplicative_order +from flint.types.fmpz cimport fmpz +from flint.flint_base.flint_base cimport flint_scalar + +cpdef enum fq_default_type: + """ + Enum for the fq_default context types: + + - 1. `fq_default_ctx.FQ_ZECH`: Use `fq_zech_t`, + - 2. `fq_default_ctx.FQ_NMOD`: Use `fq_nmod_t`, + - 3. `fq_default_ctx.FQ`: Use `fq_t`. + - 4. `fq_default_ctx.NMOD`: Use `nmod` for degree = 1, + - 5. `fq_default_ctx.FMPZ_MOD`: Use `fmpz_mod` for degree = 1. + + These can be manually selected, or type: `fq_default_ctx.DEFAULT` can be used + for the implementation to be automatically decided by Flint (default), + """ + DEFAULT = 0 + FQ_ZECH = 1 + FQ_NMOD = 2 + FQ = 3 + NMOD = 4 + FMPZ_MOD = 5 + +cdef class fq_default_ctx: + cdef fq_default_ctx_t val + cdef readonly char *var + cdef bint _initialized + + cdef new_ctype_fq_default(self) + cdef set_list_as_fq_default(self, fq_default_t val, obj) + cdef set_any_scalar_as_fq_default(self, fq_default_t fq_ele, obj) + cdef set_any_as_fq_default(self, fq_default_t val, obj) + cdef any_as_fq_default(self, obj) + + cdef _set_from_order(self, p, d, var, fq_type=*, check_prime=*) + cdef _set_from_modulus(self, modulus, var, fq_type=*, check_modulus=*) + + cdef _c_set_from_order(self, fmpz p, int d, char *var, fq_default_type fq_type=*) + cdef _c_set_from_modulus(self, modulus, char *var, fq_default_type fq_type=*) + +cdef class fq_default(flint_scalar): + cdef fq_default_ctx ctx + cdef fq_default_t val + + # Arithmetic for flint_scalar base class + cpdef fq_default _neg_(fq_default self) + cpdef fq_default _add_(fq_default self, fq_default other) + cpdef fq_default _mul_(fq_default self, fq_default other) + cpdef fq_default _sub_(fq_default self, fq_default other) + cpdef fq_default _rsub_(fq_default self, fq_default other) + cpdef fq_default _div_(fq_default self, fq_default other) + cpdef fq_default _rdiv_(fq_default self, fq_default other) + cpdef fq_default _invert_(fq_default self) diff --git a/src/flint/types/fq_default.pyx b/src/flint/types/fq_default.pyx new file mode 100644 index 00000000..4b0a128e --- /dev/null +++ b/src/flint/types/fq_default.pyx @@ -0,0 +1,854 @@ +from flint.pyflint cimport global_random_state +from flint.types.fmpz cimport fmpz, any_as_fmpz +from flint.types.nmod cimport nmod +from flint.types.fmpz_mod cimport fmpz_mod +from flint.types.fmpz_poly cimport fmpz_poly, any_as_fmpz_poly, fmpz_poly_set_list +from flint.types.fmpz_mod_poly cimport fmpz_mod_poly, fmpz_mod_poly_ctx +from flint.types.nmod_poly cimport nmod_poly +from flint.utils.typecheck cimport typecheck + +# Allow the type to be denoted by strings or integers +FQ_TYPES = { + "FQ_ZECH" : 1, + "FQ_NMOD" : 2, + "FQ" : 3, + "NMOD" : 4, + "FMPZ_MOD" : 5 +} + +cdef class fq_default_ctx: + r""" + Context object for creating :class:`~.fq_default`. + + Finite fields can be initialized in one of two possible ways. The + first is by providing characteristic and degree: + + >>> fq_default_ctx(5, 2, 'y', fq_type='FQ_ZECH') + fq_default_ctx(5, 2, 'y', x^2 + 4*x + 2, 'FQ_ZECH') + + The second is by giving an irreducible polynomial of type + :class:`~.nmod_poly` or :class:`~.fmpz_mod_poly`: + + >>> from flint import fmpz_mod_poly_ctx + >>> mod = fmpz_mod_poly_ctx(11)([1,0,1]) + >>> fq_default_ctx(modulus=mod, fq_type='FQ_NMOD') + fq_default_ctx(11, 2, 'x', x^2 + 1, 'FQ_NMOD') + + For more details, see the documentation of :method:`~._set_from_order` + and :method:`~._set_from_modulus`. + """ + def __cinit__(self): + pass + + def __dealloc__(self): + if self._initialized: + fq_default_ctx_clear(self.val) + self._initialized = False + + @staticmethod + def _parse_input_fq_type(fq_type): + if typecheck(fq_type, str): + fq_type = FQ_TYPES.get(fq_type, None) + if fq_type is None: + raise ValueError("invalid fq_type string") + + # Now fq_type should be an int between 0, 5 + if not typecheck(fq_type, int): + raise TypeError(f"{fq_type = } is invalid") + if fq_type < 0 or fq_type > 5: + raise ValueError(f"{fq_type = } should be between 0 and 5") + + return fq_type + + @staticmethod + def _parse_input_var(var): + # If no variable is given, use x + if var is None: + var = b"x" + + # Encode to bytes for cython to parse + if isinstance(var, str): + var = var.encode() + + # TODO: Flint only wants one-character inputs + if len(var) > 1: + raise ValueError("variable for GF(p^k) generator can only be one character") + + return var + + def __init__(self, p=None, degree=None, var=None, modulus=None, fq_type=fq_default_type.DEFAULT): + # Ensure the var used for the generator of GF(p^d) is a single byte + # TODO: this var is meaningless for GF(p) -- we could handle this somehow? + var = self._parse_input_var(var) + + # Ensure the fq_type is an integer between 0, 5 -- we allow users to + # input a string which is converted as an enum + fq_type = self._parse_input_fq_type(fq_type) + + # If a modulus is given, attempt to construct from this + if modulus is not None: + # If the polynomial has no known characteristic, we can try and create one + # using the supplied prime + if not (typecheck(modulus, fmpz_mod_poly) or typecheck(modulus, nmod_poly)): + if p is None: + raise ValueError("cannot create from modulus if no characteristic is known") + + ring_ctx = fmpz_mod_poly_ctx(p) + modulus = ring_ctx.any_as_fmpz_mod_poly(modulus) + if modulus is NotImplemented: + raise TypeError("modulus cannot be cast to fmpz_mod_poly") + + self._set_from_modulus(modulus, var, fq_type) + return + + # If there's no modulus and no prime, we can't continue + if p is None: + raise ValueError("either a prime or modulus must be passed for construction") + + # If we're not given a degree, construct GF(p) + if degree is None: + degree = 1 + + # Construct the field from the prime and degree GF(p^d) + self._set_from_order(p, degree, var, fq_type) + + cdef _c_set_from_order(self, fmpz p, int d, char *var, + fq_default_type fq_type=fq_default_type.DEFAULT): + self.var = var + fq_default_ctx_init_type(self.val, p.val, d, self.var, fq_type) + self._initialized = True + + cdef _set_from_order(self, p, d, var, + fq_type=fq_default_type.DEFAULT, check_prime=True): + """ + Construct a context for the finite field GF(p^d). + + `var` is a name for the ring generator of this field over GF(p). + + The optional parameter `type` select the implementation. For more + information about the types available, see :class:`~.fq_default_type` + for possible types. + """ + # c_from_order expects the characteristic to be fmpz type + prime = any_as_fmpz(p) + if prime is NotImplemented: + raise TypeError(f"cannot coerce {p = } to type fmpz") + + if check_prime and not prime.is_prime(): + raise ValueError("characteristic is not prime") + + # the degree must be strictly positive + if d < 1: + raise ValueError(f"the degree must be positive, got {d = }") + + # Cython type conversion and context initalisation + self._c_set_from_order(prime, d, var, fq_type) + + cdef _c_set_from_modulus(self, modulus, char *var, + fq_default_type fq_type=fq_default_type.DEFAULT): + self.var = var + if typecheck(modulus, fmpz_mod_poly): + fq_default_ctx_init_modulus_type(self.val, (modulus).val, + (modulus).ctx.mod.val, self.var, fq_type) + elif typecheck(modulus, nmod_poly): + fq_default_ctx_init_modulus_nmod_type(self.val, (modulus).val, + self.var, fq_type) + else: + raise TypeError(f"modulus must be fmpz_mod_poly or nmod_poly, got {modulus!r}") + self._initialized = True + + cdef _set_from_modulus(self, modulus, var, + fq_type=fq_default_type.DEFAULT, check_modulus=True): + """ + Construct a context for a finite field from an irreducible polynomial. + + `modulus` may be of type :class:`~.fmpz_mod_poly` or :class:`~.nmod_poly`. + + `var` is a name for the ring generator of this field over the prime field. + + The optional parameter `type` select the implementation. For more + information about the types available, see :class:`~.fq_default_type` + for possible types. + """ + if check_modulus and not modulus.is_irreducible(): + raise ValueError("modulus must be irreducible") + + # Cython type conversion and context initalisation + self._c_set_from_modulus(modulus, var, fq_type) + + @property + def fq_type(self): + """ + Return the implementation of this context + """ + return fq_default_type(fq_default_ctx_type(self.val)) + + def degree(self): + """ + The extension degree of the finite field + + >>> gf = fq_default_ctx(5, 2) + >>> gf.degree() + 2 + """ + return fq_default_ctx_degree(self.val) + + def characteristic(self): + """ + Return the characteristic of the finite field + + >>> gf = fq_default_ctx(5, 2) + >>> gf.characteristic() + 5 + """ + cdef fmpz p + p = fmpz.__new__(fmpz) + fq_default_ctx_prime(p.val, self.val) + return p + + prime = characteristic + + def order(self): + """ + Return the order of the finite field + + >>> gf = fq_default_ctx(5, 2) + >>> gf.order() + 25 + """ + cdef fmpz q + q = fmpz.__new__(fmpz) + fq_default_ctx_order(q.val, self.val) + return q + + def multiplicative_order(self): + """" + Return the multiplicative order of the finite field + + >>> gf = fq_default_ctx(5, 2) + >>> gf.multiplicative_order() + 24 + """ + return self.order() - 1 + + def modulus(self): + """ + Return the modulus from the context as an fmpz_mod_poly type + + >>> gf = fq_default_ctx(5, 2) + >>> gf.modulus() + x^2 + 4*x + 2 + """ + cdef fmpz_mod_poly_ctx ctx + cdef fmpz_mod_poly pol + ctx = fmpz_mod_poly_ctx(self.prime()) + pol = ctx.new_ctype_poly() + fq_default_ctx_modulus(pol.val, self.val) + return pol + + def zero(self): + """ + Return the zero element + + >>> gf = fq_default_ctx(5) + >>> gf.zero() + 0 + """ + cdef fq_default res + res = self.new_ctype_fq_default() + res.ctx = self + fq_default_zero(res.val, self.val) + return res + + def one(self): + """ + Return the unit element + + >>> gf = fq_default_ctx(5) + >>> gf.one() + 1 + """ + cdef fq_default res + res = self.new_ctype_fq_default() + res.ctx = self + fq_default_one(res.val, self.val) + return res + + def gen(self): + """ + Return the one element + + >>> gf = fq_default_ctx(5, 2, var="w") + >>> gf.gen() + w + """ + cdef fq_default res + res = self.new_ctype_fq_default() + res.ctx = self + fq_default_gen(res.val, self.val) + return res + + def random_element(self, not_zero=False): + r""" + Return a random element of the finite field + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf.random_element() + >>> type(a) is fq_default + True + >>> a = gf.random_element(not_zero=True) + >>> not a.is_zero() + True + """ + cdef fq_default res + res = self.new_ctype_fq_default() + res.ctx = self + if not_zero: + fq_default_rand_not_zero(res.val, global_random_state, self.val) + else: + fq_default_rand(res.val, global_random_state, self.val) + return res + + cdef new_ctype_fq_default(self): + return fq_default.__new__(fq_default, None, self) + + cdef set_list_as_fq_default(self, fq_default_t fq_ele, obj): + cdef fmpz_poly poly + poly = fmpz_poly.__new__(fmpz_poly) + fmpz_poly_set_list(poly.val, obj) + + # Now set the value from the fmpz_poly + fq_default_set_fmpz_poly(fq_ele, poly.val, self.val) + + return 0 + + cdef set_any_scalar_as_fq_default(self, fq_default_t fq_ele, obj): + cdef slong i + if typecheck(obj, int): + # For small integers we can convert directly + try: + i = obj + fq_default_set_si(fq_ele, i, self.val) + return 0 + # For larger integers fall through to conversion to fmpz + except OverflowError: + pass + + obj_fmpz = any_as_fmpz(obj) + fq_default_set_fmpz(fq_ele, (obj_fmpz).val, self.val) + return 0 + + # For fmpz we can also convert directly + if typecheck(obj, fmpz): + fq_default_set_fmpz(fq_ele, (obj).val, self.val) + return 0 + + # For nmod we can convert by taking it as an int + if typecheck(obj, nmod) and self.prime() == obj.modulus(): + fq_default_set_ui(fq_ele, (obj).val, self.val) + return 0 + + # For fmpz_mod we can also convert directly + if typecheck(obj, fmpz_mod) and self.prime() == (obj).ctx.modulus(): + fq_default_set_fmpz(fq_ele, (obj).val, self.val) + return 0 + + # Otherwise the object wasn't a scalar we convert from + return NotImplemented + + cdef set_any_as_fq_default(self, fq_default_t fq_ele, obj): + # First try and convert from scalars + check = self.set_any_scalar_as_fq_default(fq_ele, obj) + if check is not NotImplemented: + return 0 + + if typecheck(obj, fmpz_mod_poly) and self.prime() == (obj).ctx.mod.modulus(): + fq_default_set_fmpz_mod_poly(fq_ele, (obj).val, self.val) + return 0 + + if typecheck(obj, nmod_poly) and self.prime() == obj.modulus(): + fq_default_set_nmod_poly(fq_ele, (obj).val, self.val) + return 0 + + # If the input is not fmpz_mod_poly or nmod_poly or a list, we cast the + # input to an fmpz_poly and then set from this + poly = any_as_fmpz_poly(obj) + if poly is NotImplemented: + return NotImplemented + + fq_default_set_fmpz_poly(fq_ele, (poly).val, self.val) + return 0 + + cdef any_as_fq_default(self, obj): + # convert from fq_default + if typecheck(obj, fq_default): + if self != (obj).ctx: + raise ValueError("contexts dont match") + return obj + + cdef fq_default res + res = self.new_ctype_fq_default() + check = self.set_any_as_fq_default(res.val, obj) + if check is NotImplemented: + return NotImplemented + return res + + def __eq__(self, other): + """ + Two finite field context compare equal if they have same + characteristic, modulus, type and variable + + >>> from flint import fmpz_mod_poly_ctx + >>> modulus = fmpz_mod_poly_ctx(5)([2,4,1]) + >>> gf = fq_default_ctx(5, 2) + >>> gf2 = fq_default_ctx(modulus=modulus) + >>> gf2 == gf + True + >>> gf3 = fq_default_ctx(modulus=modulus, var="y") + >>> gf3 == gf + False + """ + if self is other: + return True + + if typecheck(other, fq_default_ctx): + return (self.fq_type == other.fq_type + and self.var == other.var + and self.prime() == other.prime() + and self.modulus() == other.modulus()) + return False + + def __hash__(self): + return hash((self.type, self.var, self.prime(), self.modulus())) + + def __str__(self): + if self.degree() == 1: + return f"Context for fq_default in GF({self.prime()})" + return f"Context for fq_default in GF({self.prime()}^{self.degree()})[{self.var.decode()}]/({self.modulus().str(var=self.var.decode())})" + + def __repr__(self): + if self.degree() == 1: + return f"fq_default_ctx({self.prime()}, var='{self.var.decode()}' type='{self.fq_type._name_}')" + return f"fq_default_ctx({self.prime()}, {self.degree()}, '{self.var.decode()}', {self.modulus()!r}, '{self.fq_type._name_}')" + + def __call__(self, val): + return fq_default(val, self) + + +cdef class fq_default(flint_scalar): + def __cinit__(self, val, ctx): + if not typecheck(ctx, fq_default_ctx): + raise TypeError + self.ctx = ctx + fq_default_init(self.val, self.ctx.val) + + def __dealloc__(self): + if self.ctx is not None: + fq_default_clear(self.val, self.ctx.val) + + def __init__(self, val, ctx): + if not typecheck(ctx, fq_default_ctx): + raise TypeError + self.ctx = ctx + + # Converts the list to an fmpz_poly and then sets from this + if typecheck(val, list): + self.ctx.set_list_as_fq_default(self.val, val) + return + + # Otherwise cascades through types to convert + check = self.ctx.set_any_as_fq_default(self.val, val) + if check is NotImplemented: + raise TypeError + + def __int__(self): + """ + Attempts to lift self to an integer of type fmpz in [0, p-1] + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> int(gf(123)) + 123 + """ + cdef fmpz x = fmpz.__new__(fmpz) + res = fq_default_get_fmpz(x.val, self.val, self.ctx.val) + if res == 1: + return int(x) + raise ValueError("fq element has no lift to the integers") + + def polynomial(self): + """ + Returns a representative of ``self`` as a polynomial in `(Z/pZ)[x] / h(x)` + where `h(x)` is the defining polynomial of the finite field. + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.polynomial() + 3*x^2 + 2*x + 1 + >>> gf(123).polynomial() + 123 + """ + cdef fmpz_mod_poly pol + + ring_ctx = fmpz_mod_poly_ctx(self.ctx.prime()) + pol = ring_ctx.new_ctype_poly() + fq_default_get_fmpz_mod_poly((pol).val, self.val, self.ctx.val) + + return pol + + def to_list(self): + """ + Returns self as a list of fmpz types corresponding to a + list of coefficients + + >>> gf = fq_default_ctx(163, 3) + >>> gf([-1,2,1]).to_list() + [162, 2, 1] + >>> gf.one().to_list() + [1, 0, 0] + """ + coeffs = [None for _ in range(self.ctx.degree())] + for i in range(self.ctx.degree()): + c = fmpz.__new__(fmpz) + fq_default_get_coeff_fmpz((c).val, self.val, i, self.ctx.val) + coeffs[i] = c + return coeffs + + def str(self): + return self.polynomial().str(var=self.ctx.var.decode()) + + def repr(self): + return f"fq_default({self.to_list(), self.ctx.__repr__()})" + + def __hash__(self): + return hash((self.polynomial(), hash(self.ctx))) + + # ================================================= + # Comparisons + # ================================================= + def is_zero(self): + """ + Returns true is self is zero and false otherwise + + >>> gf = fq_default_ctx(163, 3) + >>> gf(0).is_zero() + True + >>> gf(-1).is_zero() + False + """ + return 1 == fq_default_is_zero(self.val, self.ctx.val) + + def is_one(self): + """ + Returns true is self is one and false otherwise + + >>> gf = fq_default_ctx(163, 3) + >>> gf(-1).is_one() + False + >>> gf(1).is_one() + True + """ + return 1 == fq_default_is_one(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + cdef bint res + if op != 2 and op != 3: + raise TypeError("fq_default cannot be ordered") + + # If other is not an fq_default element, we attempt to convert to fq_default + if not typecheck(other, fq_default): + # For nmod and fmpz_mod if the modulus does not match the characteristic + # then we return false. + if typecheck(other, nmod) and self.ctx.characteristic() != (other).modulus(): + res = False + + elif typecheck(other, fmpz_mod) and self.ctx.characteristic() != (other).ctx.modulus(): + res = False + + else: + # Convert from int, fmpz, fmpz_mod and nmod to fq_default + cmp = self.ctx.new_ctype_fq_default() + check = self.ctx.set_any_scalar_as_fq_default((cmp).val, other) + + # Conversion failed, element was not a compatible scalar + if check is NotImplemented: + res = False + else: + # We now have an fq_default element with the same context, so compare value only + res = fq_default_equal(self.val, (cmp).val, self.ctx.val) + + # Flip the result of res if we're doing not equals + if op == 2: + return res + return not res + + # Otherwise we're in the case where other is also an fq_default but may have a + # different context. + + # If the contexts match exactly, only check values + if self.ctx == (other).ctx: + res = fq_default_equal(self.val, (other).val, self.ctx.val) + + # Otherwise if both contexts have the same characteristic check + # if both fq_default lift to the same integer + elif self.ctx.characteristic() == (other).ctx.characteristic(): + try: + res = int(self) == int(other) + # One or both lifts failed, so values are not equal + except ValueError: + res = False + + # Otherwise, values are considered not equal + else: + res = False + + # Flip the result of res if we're doing not equals + if op == 2: + return res + return not res + + # ================================================= + # Generic arithmetic required by flint_scalar + # ================================================= + + def _any_as_self(self, other): + return self.ctx.any_as_fq_default(other) + + cpdef fq_default _neg_(fq_default self): + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_neg(res.val, self.val, self.ctx.val) + return res + + cpdef fq_default _add_(fq_default self, fq_default other): + """ + Assumes that __add__() has ensured other is of type self + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_add(res.val, self.val, other.val, self.ctx.val) + return res + + cpdef fq_default _sub_(fq_default self, fq_default other): + """ + Assumes that __sub__() has ensured other is of type self + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_sub(res.val, self.val, other.val, res.ctx.val) + return res + + cpdef fq_default _rsub_(fq_default self, fq_default other): + """ + Assumes that __rsub__() has ensured other is of type self + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_sub(res.val, other.val, self.val, res.ctx.val) + return res + + cpdef fq_default _mul_(fq_default self, fq_default other): + """ + Assumes that __mul__() has ensured other is of type self + + TODO: this could be optimised by using mul_si and others + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_mul(res.val, self.val, other.val, self.ctx.val) + return res + + cpdef fq_default _div_(fq_default self, fq_default other): + """ + Assumes that __div__() has ensured other is of type self + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_div(res.val, self.val, other.val, res.ctx.val) + return res + + cpdef fq_default _rdiv_(fq_default self, fq_default other): + """ + Assumes that __div__() has ensured other is of type self + """ + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_div(res.val, other.val, self.val, res.ctx.val) + return res + + cpdef fq_default _invert_(fq_default self): + cdef fq_default res = self.ctx.new_ctype_fq_default() + fq_default_inv(res.val, self.val, self.ctx.val) + return res + + # ================================================= + # Additional arithmetic + # ================================================= + + def inverse(self): + """ + Computes the inverse of self + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> b = a.inverse() + >>> b + 68*x^2 + 17*x + 116 + >>> a*b == gf.one() + True + """ + return self._invert_() + + def square(self): + """ + Computes the square of ``self`` + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.square() == a*a + True + >>> a.square() + 110*x^2 + 101*x + 25 + """ + cdef fq_default res + res = self.ctx.new_ctype_fq_default() + fq_default_sqr(res.val, self.val, self.ctx.val) + return res + + def __pow__(self, e): + """ + Compute `a^e` for `a` equal to ``self``. + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> pow(a, -1) == 1/a + True + >>> pow(a, 2) == a * a + True + >>> pow(a, 2**128) == pow(a, 2**128 % (163**3 - 1)) + True + >>> pow(a, 123) + 46*x^2 + 110*x + 155 + + """ + cdef fq_default res + res = self.ctx.new_ctype_fq_default() + + # If e is negative, we invert first + if e < 0: + if self.is_zero(): + raise ZeroDivisionError + e = -e + fq_default_inv(res.val, self.val, self.ctx.val) + else: + fq_default_set(res.val, self.val, self.ctx.val) + + # For small e we dont need to make an fmpz + if e.bit_length() < 32: + fq_default_pow_ui(res.val, res.val, e, self.ctx.val) + return res + + # Attempt to cast the exponent to an fmpz type then exponentiate + e_fmpz = any_as_fmpz(e) + if e_fmpz is NotImplemented: + raise TypeError(f"exponent {e = } cannot be cast to fmpz") + + fq_default_pow(res.val, res.val, (e_fmpz).val, self.ctx.val) + + return res + + def sqrt(self): + """ + Returns the square root of the element, if not square root exists, + throws a ValueError. + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([3,2,1]) + >>> a.is_square() + True + >>> b = a.sqrt() + >>> b + 95*x^2 + 36*x + 34 + >>> b**2 in [a, -a] + True + """ + cdef fq_default res + res = self.ctx.new_ctype_fq_default() + check = fq_default_sqrt(res.val, self.val, self.ctx.val) + if check: + return res + raise ValueError("element is not a square") + + def is_square(self): + """ + Returns if the element is a square in the field + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.is_square() + False + >>> (a*a).is_square() + True + """ + return 1 == fq_default_is_square(self.val, self.ctx.val) + + def pth_root(self): + """ + Returns the pth root of the element. + + This is computed by raising ``self`` to the `p^(d-1)` power, + `p` is the characteristic of the field and `d` is the degree + of the extension. + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.pth_root() + 5*x^2 + 152*x + 119 + """ + cdef fq_default res + res = self.ctx.new_ctype_fq_default() + fq_default_pth_root(res.val, self.val, self.ctx.val) + return res + + # ================================================= + # Special functions + # ================================================= + + def trace(self): + """ + Returns the trace of self + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.trace() + 124 + """ + cdef fmpz tr = fmpz.__new__(fmpz) + fq_default_trace(tr.val, self.val, self.ctx.val) + return tr + + def norm(self): + """ + Returns the norm of self + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.norm() + 116 + """ + cdef fmpz nrm = fmpz.__new__(fmpz) + fq_default_norm(nrm.val, self.val, self.ctx.val) + return nrm + + def frobenius(self, e=1): + r""" + Evaluates the homomorphism `\Sigma^e` on ``self``. + + >>> gf = fq_default_ctx(163, 3) + >>> a = gf([1,2,3]) + >>> a.frobenius() + 155*x^2 + 9*x + 4 + >>> a.frobenius(2) + 5*x^2 + 152*x + 119 + >>> a == a.frobenius(3) + True + >>> a.frobenius(2) == a.frobenius(-1) + True + """ + cdef fq_default res + res = self.ctx.new_ctype_fq_default() + fq_default_frobenius(res.val, self.val, e, self.ctx.val) + return res diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index 4dbfcd35..d256d389 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -26,6 +26,8 @@ exts = [ 'fmpz_mod_poly', 'fmpz_mod_mat', + 'fq_default', + 'arf', 'arb',