diff --git a/src/flint/flintlib/types/gr.pxd b/src/flint/flintlib/types/gr.pxd index b62cc01e..3db145ba 100644 --- a/src/flint/flintlib/types/gr.pxd +++ b/src/flint/flintlib/types/gr.pxd @@ -5,6 +5,26 @@ from flint.flintlib.types.flint cimport ( flint_bitcnt_t, ) +cdef extern from *: + """ + /* + * The following functions were introduced in FLINT 3.2.0 + */ + + #if __FLINT_RELEASE < 30200 + #define gr_min(res, x, y, ctx) GR_UNABLE + #define gr_max(res, x, y, ctx) GR_UNABLE + #define gr_le(x, y, ctx) T_UNKNOWN + #define gr_lt(x, y, ctx) T_UNKNOWN + #define gr_ge(x, y, ctx) T_UNKNOWN + #define gr_gt(x, y, ctx) T_UNKNOWN + #define gr_abs_le(x, y, ctx) T_UNKNOWN + #define gr_abs_lt(x, y, ctx) T_UNKNOWN + #define gr_abs_ge(x, y, ctx) T_UNKNOWN + #define gr_abs_gt(x, y, ctx) T_UNKNOWN + #endif + """ + cdef extern from "flint/gr.h": diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index 7a4c0287..88785f8b 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -5,7 +5,12 @@ import flint -dunder_test_regex = re.compile(r'^(.*?)__test__\..*?\.(.*) \(line (\d+)\)$') +dunder_test_regex = re.compile(r'^(.*?)__test__\.(.*?\.)(.*) \(line (\d+)\)$') + +test_flint_at_least = { + "flint.types._gr.gr_ctx.gens": 30100, + "flint.types._gr.gr_ctx.neg": 30100, +} def find_doctests(module): @@ -20,9 +25,14 @@ def find_doctests(module): m = dunder_test_regex.match(test.name) if m is not None: groups = m.groups() - test.name = groups[0] + groups[1] - test.lineno = int(groups[2]) - res.append(test) + test.name = groups[0] + groups[2] + test.lineno = int(groups[3]) + + if ( + test_flint_at_least.get("".join(groups[:3]), flint.__FLINT_RELEASE__) + <= flint.__FLINT_RELEASE__ + ): + res.append(test) tests.append((module_info.name, res)) diff --git a/src/flint/types/_gr.pxd b/src/flint/types/_gr.pxd index e02738c5..3de00a5e 100644 --- a/src/flint/types/_gr.pxd +++ b/src/flint/types/_gr.pxd @@ -111,22 +111,43 @@ from flint.flintlib.functions.gr cimport ( gr_is_zero, gr_is_one, gr_is_neg_one, - # gr_is_integer, - # gr_is_rational, + gr_is_integer, + gr_is_rational, gr_equal, gr_neg, gr_add, gr_add_si, + gr_add_other, + gr_other_add, gr_sub, gr_sub_si, + gr_sub_other, + gr_other_sub, gr_mul, gr_mul_si, + gr_mul_other, + gr_other_mul, + gr_is_invertible, gr_inv, gr_div, gr_div_si, + gr_div_other, + gr_div_nonunique, + gr_divides, + gr_other_div, + gr_divexact, + gr_divexact_si, + gr_divexact_other, + gr_other_divexact, + gr_euclidean_div, + gr_euclidean_rem, + gr_euclidean_divrem, + gr_pow, gr_pow_si, + gr_pow_other, + gr_other_pow, gr_is_square, gr_sqrt, @@ -149,10 +170,20 @@ from flint.flintlib.functions.gr cimport ( gr_csgn, gr_arg, - # gr_le, - # gr_lt, - # gr_ge, - # gr_gt, + gr_cmp, + gr_cmp_other, + gr_cmpabs, + gr_cmpabs_other, + gr_le, + gr_lt, + gr_ge, + gr_gt, + gr_abs_le, + gr_abs_lt, + gr_abs_ge, + gr_abs_gt, + gr_min, + gr_max, gr_numerator, gr_denominator, @@ -231,7 +262,7 @@ cdef class gr_ctx(flint_ctx): py_val = self.new_gr() err = gr_set_other(py_val.pval, x.pval, x.ctx.ctx_t, self.ctx_t) if err != GR_SUCCESS: - raise self._error(err, "Incorrect conversion") + raise self._error(err, "Cannot convert x to the current context") return py_val @cython.final @@ -378,6 +409,608 @@ cdef class gr_ctx(flint_ctx): gr_vec_clear(gens, self.ctx_t) return py_gens + @cython.final + cdef inline truth_t _is_zero(self, gr x): + return gr_is_zero(x.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _is_one(self, gr x): + return gr_is_one(x.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _is_neg_one(self, gr x): + return gr_is_neg_one(x.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _equal(self, gr x, gr y): + return gr_equal(x.pval, y.pval, self.ctx_t) + + # @cython.final + # cdef inline truth_t _is_integer(self, gr x): + # return gr_is_integer(x.pval, self.ctx_t) + # + # @cython.final + # cdef inline truth_t _is_rational(self, gr x): + # return gr_is_rational(x.pval, self.ctx_t) + + @cython.final + cdef inline gr _neg(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_neg(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot negate x in this context") + return res + + @cython.final + cdef inline gr _add(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_add(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot add x and y in this context") + return res + + @cython.final + cdef inline gr _add_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_add_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot add x and y in this context") + return res + + @cython.final + cdef inline gr _other_add(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_add(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot add x and y in this context") + return res + + @cython.final + cdef inline gr _add_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_add_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot add x and y in this context") + return res + + @cython.final + cdef inline gr _sub(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_sub(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot sub x and y in this context") + return res + + @cython.final + cdef inline gr _sub_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_sub_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot sub x and y in this context") + return res + + @cython.final + cdef inline gr _other_sub(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_sub(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot sub x and y in this context") + return res + + @cython.final + cdef inline gr _sub_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_sub_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot sub x and y in this context") + return res + + @cython.final + cdef inline gr _mul(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_mul(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot mul x and y in this context") + return res + + @cython.final + cdef inline gr _mul_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_mul_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot mul x and y in this context") + return res + + @cython.final + cdef inline gr _other_mul(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_mul(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot mul x and y in this context") + return res + + @cython.final + cdef inline gr _mul_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_mul_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot mul x and y in this context") + return res + + ### + # Division + + @cython.final + cdef inline truth_t _is_invertible(self, gr x): + return gr_is_invertible(x.pval, self.ctx_t) + + @cython.final + cdef inline gr _inv(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_inv(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "x in not invertible in this context") + return res + + @cython.final + cdef inline gr _div(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_div(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot div x and y in this context") + return res + + @cython.final + cdef inline gr _div_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_div_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot div x and y in this context") + return res + + @cython.final + cdef inline gr _other_div(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_div(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot div x and y in this context") + return res + + @cython.final + cdef inline gr _div_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_div_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot div x and y in this context") + return res + + @cython.final + cdef inline gr _divexact(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_divexact(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot divexact x and y in this context") + return res + + @cython.final + cdef inline gr _divexact_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_divexact_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot divexact x and y in this context") + return res + + @cython.final + cdef inline gr _other_divexact(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_divexact(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot divexact x and y in this context") + return res + + @cython.final + cdef inline gr _divexact_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_divexact_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot divexact x and y in this context") + return res + + @cython.final + cdef inline gr _div_nonunique(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_div_nonunique(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "No solution q to x = qy has been found") + return res + + @cython.final + cdef inline truth_t _divides(self, gr x, gr d): + return gr_divides(x.pval, d.pval, self.ctx_t) + + @cython.final + cdef inline gr _euclidean_div(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_euclidean_div(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "No solution q to x = qy has been found") + return res + + @cython.final + cdef inline gr _euclidean_rem(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_euclidean_rem(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "No solution q to x = qy has been found") + return res + + @cython.final + cdef inline tuple[gr, gr] _euclidean_divrem(self, gr x, gr y): + cdef int err + cdef gr rem = self.new_gr() + cdef gr div = self.new_gr() + err = gr_euclidean_divrem(div.pval, rem.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "No solution q to x = qy has been found") + return (div, rem) + + ### + # Powering + + @cython.final + cdef inline gr _pow(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_pow(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _pow_other(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_pow_other(res.pval, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _other_pow(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_other_pow(res.pval, x.pval, x.ctx.ctx_t, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _pow_si(self, gr x, slong y): + cdef int err + cdef gr res = self.new_gr() + err = gr_pow_si(res.pval, x.pval, y, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + ### + # Square roots + + @cython.final + cdef inline truth_t _is_square(self, gr x): + return gr_is_square(x.pval, self.ctx_t) + + @cython.final + cdef inline gr _sqrt(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_sqrt(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _rsqrt(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_rsqrt(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + ### + # Greates Common Divisors + + @cython.final + cdef inline gr _gcd(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_gcd(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _lcm(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_lcm(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + ### + # Factorization + + @cython.final + cdef inline tuple[gr, list[tuple[gr, int]]] _factor(self, gr x): + cdef int err, i + cdef slong length, exp_s + cdef gr c, f + cdef gr_ptr fac_i, exp_i + cdef gr_vec_t factors, exponents + cdef int flags = 0 # XXX: What is flags? + c = self.new_gr() + gr_vec_init(factors, 0, self.ctx_t) + gr_vec_init(exponents, 0, gr_fmpz_ctx_c.ctx_t) + err = gr_factor(c.pval, factors, exponents, x.pval, flags, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Failed to factor gr object") + length = gr_vec_length(factors, self.ctx_t) + py_factors = [None] * length + for 0 <= i < length: + f = self.new_gr() + fac_i = gr_vec_entry_ptr(factors, i, self.ctx_t) + err = gr_set(f.pval, fac_i, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Failed to copy factor.") + exp_i = gr_vec_entry_ptr(exponents, i, gr_fmpz_ctx_c.ctx_t) + err = gr_get_si(&exp_s, exp_i, gr_fmpz_ctx_c.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Failed to get integer value of exponent.") + exp = exp_s + py_factors[i] = (f, exp) + gr_vec_clear(factors, self.ctx_t) + gr_vec_clear(exponents, gr_fmpz_ctx_c.ctx_t) + return c, py_factors + + ### + # Fractions + + @cython.final + cdef inline gr _numerator(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_numerator(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + @cython.final + cdef inline gr _denominator(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_denominator(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot pow x and y in this context") + return res + + ### + # Integer and Complex parts + + @cython.final + cdef inline gr _floor(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_floor(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute floor(x) in this context") + return res + + @cython.final + cdef inline gr _ceil(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_ceil(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute ceil(x) in this context") + return res + + @cython.final + cdef inline gr _trunc(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_trunc(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute trunc(x) in this context") + return res + + @cython.final + cdef inline gr _nint(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_nint(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute nint(x) in this context") + return res + + @cython.final + cdef inline gr _abs(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_abs(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute abs(x) in this context") + return res + + @cython.final + cdef inline gr _conj(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_conj(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute conj(x) in this context") + return res + + @cython.final + cdef inline gr _re(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_re(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute re(x) in this context") + return res + + @cython.final + cdef inline gr _im(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_im(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute im(x) in this context") + return res + + @cython.final + cdef inline gr _sgn(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_sgn(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute sgn(x) in this context") + return res + + @cython.final + cdef inline gr _csgn(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_csgn(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute csgn(x) in this context") + return res + + @cython.final + cdef inline gr _arg(self, gr x): + cdef int err + cdef gr res = self.new_gr() + err = gr_arg(res.pval, x.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute arg(x) in this context") + return res + + ### + # Ordering methods + + @cython.final + cdef inline int _cmp(self, gr x, gr y): + cdef int err + cdef int res + err = gr_cmp(&res, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compare x and y") + return res + + @cython.final + cdef inline int _cmp_other(self, gr x, gr y): + cdef int err + cdef int res + err = gr_cmp_other(&res, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compare x and y") + return res + + @cython.final + cdef inline int _cmpabs(self, gr x, gr y): + cdef int err + cdef int res + err = gr_cmpabs(&res, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compare x and y") + return res + + @cython.final + cdef inline int _cmpabs_other(self, gr x, gr y): + cdef int err + cdef int res + err = gr_cmpabs_other(&res, x.pval, y.pval, y.ctx.ctx_t, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compare x and y") + return res + + @cython.final + cdef inline truth_t _le(self, gr x, gr y): + return gr_le(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _abs_le(self, gr x, gr y): + return gr_abs_le(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _lt(self, gr x, gr y): + return gr_lt(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _abs_lt(self, gr x, gr y): + return gr_abs_lt(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _ge(self, gr x, gr y): + return gr_ge(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _abs_ge(self, gr x, gr y): + return gr_abs_ge(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _gt(self, gr x, gr y): + return gr_gt(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline truth_t _abs_gt(self, gr x, gr y): + return gr_abs_gt(x.pval, y.pval, self.ctx_t) + + @cython.final + cdef inline gr _min(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_min(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute min(x) in this context") + return res + + @cython.final + cdef inline gr _max(self, gr x, gr y): + cdef int err + cdef gr res = self.new_gr() + err = gr_max(res.pval, x.pval, y.pval, self.ctx_t) + if err != GR_SUCCESS: + raise self._error(err, "Cannot compute min(x) in this context") + return res + # @cython.final # cdef inline list _gens_recursive(self): # cdef int err @@ -907,7 +1540,7 @@ cdef class gr_series_ctx(gr_ctx): @cython.no_gc cdef class gr(flint_scalar): cdef gr_ptr pval - cdef gr_ctx ctx + cdef public gr_ctx ctx cdef bint _init @cython.final @@ -930,14 +1563,6 @@ cdef class gr(flint_scalar): cdef inline truth_t _is_neg_one(self): return gr_is_neg_one(self.pval, self.ctx.ctx_t) - # @cython.final - # cdef inline truth_t _is_integer(self): - # return gr_is_integer(self.pval, self.ctx.ctx_t) - - # @cython.final - # cdef inline truth_t _is_rational(self): - # return gr_is_rational(self.pval, self.ctx.ctx_t) - @cython.final cdef inline gr _neg(self): cdef int err diff --git a/src/flint/types/_gr.pyx b/src/flint/types/_gr.pyx index f8e98432..fa9f0f3a 100644 --- a/src/flint/types/_gr.pyx +++ b/src/flint/types/_gr.pyx @@ -232,6 +232,8 @@ cdef class gr_ctx(flint_ctx): 18446744073709551615 """ if isinstance(arg, gr): + if arg.ctx == self: + return arg return self.from_other(arg) if type(arg) is int: try: @@ -355,12 +357,427 @@ cdef class gr_ctx(flint_ctx): """ return self._gens() - # def gens_recursive(self) -> list[gr]: - # """Return all generators of the domain + def is_zero(self, x) -> bool | None: + """ + Returns whether x is equal to the ring element 0. + + >>> from flint.types._gr import gr_real_arb_ctx + >>> ctx = gr_real_arb_ctx.new(10) + >>> ctx.is_zero(ctx(0)) + True + >>> ctx.is_zero(ctx(1)) + False + >>> ctx.is_zero(ctx("[0 +/- 0.1]")) + """ + return truth_to_py(self._is_zero(x)) + + def is_one(self, x) -> bool | None: + """ + Returns whether x is equal to the ring element 1. - # See :meth:`gens` for an example. + >>> from flint.types._gr import gr_real_arb_ctx + >>> ctx = gr_real_arb_ctx.new(10) + >>> ctx.is_one(ctx(0)) + False + >>> ctx.is_one(ctx(1)) + True + >>> ctx.is_one(ctx("[1 +/- 0.1]")) + """ + return truth_to_py(self._is_one(x)) + + def is_neg_one(self, x) -> bool | None: + """ + Returns whether x is equal to the ring element -1. + + >>> from flint.types._gr import gr_real_arb_ctx + >>> ctx = gr_real_arb_ctx.new(10) + >>> ctx.is_neg_one(ctx(1)) + False + >>> ctx.is_neg_one(ctx(-1)) + True + >>> ctx.is_neg_one(ctx("[-1 +/- 0.1]")) + """ + return truth_to_py(self._is_neg_one(x)) + + def equal(self, x, y) -> bool | None: + """ + Returns whether the elements `x` and `y` are equal. + """ + return self._equal(self(x), self(y)) + + # def is_integer(self, x): + # """ + # Returns whether `x` represents an integer. # """ - # return self._gens_recursive() + # return self._is_integer(self(x)) + # + # def is_rational(self, x): + # """ + # Returns whether x represents a rational number. + # """ + # return self._is_rational(self(x)) + + def neg(self, x) -> gr: + """ + Returns `-x`. + + >>> from flint.types._gr import gr_complex_acb_ctx, gr_real_arb_ctx + >>> arb = gr_real_arb_ctx.new(53); acb = gr_complex_acb_ctx.new(106) + >>> c = acb("2 + I").sqrt(); c + ([1.4553466902253548081226618397097 +/- 3.48e-32] + [0.3435607497225124641385657439146 +/- 5.23e-32]*I) + >>> acb.neg(c) + ([-1.4553466902253548081226618397097 +/- 3.48e-32] + [-0.3435607497225124641385657439146 +/- 5.23e-32]*I) + """ + return self._neg(self(x)) + + ### + # Arithmetic methods + + def add(self, x, y) -> gr: + """ + Returns `x + y` + + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._add(x, y) + if x.ctx == self: + return self._add_other(x, y) + if y.ctx == self: + return self._other_add(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._add_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._add(self(x), self(y)) + + def sub(self, x, y) -> gr: + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._sub(x, y) + if x.ctx == self: + return self._sub_other(x, y) + if y.ctx == self: + return self._other_sub(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._sub_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._sub(self(x), self(y)) + + def mul(self, x, y) -> gr: + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._mul(x, y) + if x.ctx == self: + return self._mul_other(x, y) + if y.ctx == self: + return self._other_mul(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._mul_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._mul(self(x), self(y)) + + ### + # Division + + def is_invertible(self, x) -> bool | None: + """ + Returns whether `x` has a multiplicative inverse in the present ring, i.e. whether `x` is a unit. + """ + return self._is_invertible(self(x)) + + def inv(self, x) -> gr: + """ + Returns the multiplicative inverse of `x` in the present ring, if such an element exists. + """ + return self._inv(self(x)) + + def div(self, x, y) -> gr: + """ + Returns the quotient `x/y` + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._div(x, y) + if x.ctx == self: + return self._div_other(x, y) + if y.ctx == self: + return self._other_div(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._div_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._div(self(x), self(y)) + + def divexact(self, x, y) -> gr: + """ + Returns the quotient `x/y`, assuming that the quotient is exact in the current context. + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._divexact(x, y) + if x.ctx == self: + return self._divexact_other(x, y) + if y.ctx == self: + return self._other_divexact(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._divexact_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._divexact(self(x), self(y)) + + def div_nonunique(self, x, y) -> gr: + """ + Returns an arbitrary solution `q` of the equation `x = qy`. + """ + return self._div_nonunique(self(x), self(y)) + + def divides(self, d, x) -> bool | None: + """ + Returns whether `d | x`; that is, whether there is an element `q` such that `x = qd`. + """ + return self._divides(self(d), self(x)) + + def euclidean_div(self, x, y) -> gr: + return self._euclidean_div(self(x), self(y)) + + def euclidean_rem(self, x, y) -> gr: + return self._euclidean_rem(self(x), self(y)) + + def euclidean_divrem(self, x, y) -> tuple[gr, gr]: + return self._euclidean_divrem(self(x), self(y)) + + ### + # Powering + + def pow(self, x, y) -> gr: + """ + Returns the power x^y, the interpretation of which depends on the ring when y ∉ ℤ + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx == self: + return self._pow(x, y) + if x.ctx == self: + return self._pow_other(x, y) + if y.ctx == self: + return self._other_pow(x, y) + + if isinstance(x, gr) and x.ctx == self and type(y) is int: + try: + return self._pow_si(x, y) + except OverflowError: + pass + + # NOTE: By default, convert everything to the required + # context before performing the operation + return self._pow(self(x), self(y)) + + ### + # Square Roots + + def is_square(self, x) -> bool | None: + """ + Returns whether x is a perfect square in the context. + """ + return self._is_square(self(x)) + + def sqrt(self, x) -> gr: + """ + Returns the square root of `x`. + """ + return self._sqrt(self(x)) + + def rsqrt(self, x) -> gr: + """ + Returns the reciprocal square root of `x`. + """ + return self._rsqrt(self(x)) + + ### + # Greatest Common Divisors + + def gcd(self, x, y) -> gr: + """ + Returns a greatest common divisor (GCD) of `x` and `y`. + Since the GCD is unique only up to multiplication by a unit, + an implementation-defined representative is chosen. + """ + return self._gcd(self(x), self(y)) + + def lcm(self, x, y) -> gr: + """ + Returns a least common multiple (LCM) of x and y. + Since the LCM is unique only up to multiplication by a unit, + an implementation-defined representative is chosen. + """ + return self._lcm(self(x), self(y)) + + ### + # Factorization + + def factor(self, x) -> tuple[gr, list[tuple[gr, int]]]: + """ + Given an element of the context, this returns a factorization (c, f, e): + x = c f₁^e₁ ··· fₙ^eₙ, where fₖ will be irreducible or prime depending on the ring. + The prefactor c stores a unit, sign or coefficient. + Note that c is an element of the same ring as x. + """ + return self._factor(self(x)) + + ### + # Fractions + + def numerator(self, x) -> gr: + """ + Return a numerator p such that x = p / q. + For typical fraction fields, the denominator will be minimal and canonical. + However, some rings may return an arbitrary denominator as long as the numerator matches. + The default implementations simply returns p = x. + """ + return self._numerator(self(x)) + + def denominator(self, x) -> gr: + """ + Return a denominator q such that x = p / q. + For typical fraction fields, the denominator will be minimal and canonical. + However, some rings may return an arbitrary denominator as long as the numerator matches. + The default implementations simply returns q = 1. + """ + return self._denominator(self(x)) + + ### + # Integer and Complex parts + + def floor(self, x) -> gr: + return self._floor(self(x)) + + def ceil(self, x) -> gr: + return self._ceil(self(x)) + + def trunc(self, x) -> gr: + return self._trunc(self(x)) + + def nint(self, x) -> gr: + return self._nint(self(x)) + + def abs(self, x) -> gr: + return self._abs(self(x)) + + def conj(self, x) -> gr: + return self._conj(self(x)) + + def re(self, x) -> gr: + return self._re(self(x)) + + def im(self, x) -> gr: + return self._im(self(x)) + + def sgn(self, x) -> gr: + return self._sgn(self(x)) + + def csgn(self, x) -> gr: + return self._csgn(self(x)) + + def arg(self, x) -> gr: + return self._arg(self(x)) + + ### + # Ordering methods + + def cmp(self, x, y) -> int: + """ + Returns: + - -1 if x < y + - 0 if x = y + - 1 if x > y + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx != self: + return self._cmp_other(x, y) + + if y.ctx == self and x.ctx != self: + return -self._cmp_other(y, x) + + if x.ctx == self == y.ctx: + return self._cmp(x, y) + + return self._cmp(self(x), self(y)) + + def cmpabs(self, x, y) -> int: + """ + Returns: + - -1 if |x| < |y| + - 0 if |x| = |y| + - 1 if |x| > |y| + """ + if isinstance(x, gr) and isinstance(y, gr): + if x.ctx == self and y.ctx != self: + return self._cmpabs_other(x, y) + + if y.ctx == self and x.ctx != self: + return -self._cmpabs_other(y, x) + + if x.ctx == self == y.ctx: + return self._cmpabs(x, y) + + return self._cmpabs(self(x), self(y)) + + def le(self, x, y) -> bool | None: + return truth_to_py(self._le(self(x), self(y))) + + def abs_le(self, x, y) -> bool | None: + return truth_to_py(self._abs_le(self(x), self(y))) + + def lt(self, x, y) -> bool | None: + return truth_to_py(self._lt(self(x), self(y))) + + def abs_lt(self, x, y) -> bool | None: + return truth_to_py(self._abs_lt(self(x), self(y))) + + def ge(self, x, y) -> bool | None: + return truth_to_py(self._ge(self(x), self(y))) + + def abs_ge(self, x, y) -> bool | None: + return truth_to_py(self._abs_ge(self(x), self(y))) + + def gt(self, x, y) -> bool | None: + return truth_to_py(self._gt(self(x), self(y))) + + def abs_gt(self, x, y) -> bool | None: + return truth_to_py(self._abs_gt(self(x), self(y))) + + def min(self, x, y) -> gr: + return self._min(self(x), self(y)) + + def max(self, x, y) -> gr: + return self._max(self(x), self(y)) cdef class gr_scalar_ctx(gr_ctx):