From 998672321824758fec45afa74ff80f567d0ec360 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 28 Aug 2021 11:04:39 +0200 Subject: [PATCH] remove compile time dependency of Integer and Rational on cypari2 --- src/sage/libs/pari/convert_sage.pxd | 7 ++ src/sage/libs/pari/convert_sage.pyx | 167 +++++++++++++++++++++++++++- src/sage/rings/integer.pxd | 1 - src/sage/rings/integer.pyx | 112 +++++++------------ src/sage/rings/rational.pyx | 27 +++-- 5 files changed, 221 insertions(+), 93 deletions(-) diff --git a/src/sage/libs/pari/convert_sage.pxd b/src/sage/libs/pari/convert_sage.pxd index 466c8a7da3f..263ac38a156 100644 --- a/src/sage/libs/pari/convert_sage.pxd +++ b/src/sage/libs/pari/convert_sage.pxd @@ -1,6 +1,13 @@ from cypari2.gen cimport Gen from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational cpdef gen_to_sage(Gen z, locals=*) cpdef set_integer_from_gen(Integer self, Gen x) +cpdef Gen new_gen_from_integer(Integer self) +cpdef set_rational_from_gen(Rational self, Gen x) +cpdef Gen new_gen_from_rational(Rational self) + +cpdef pari_is_prime(Integer p) +cpdef pari_is_prime_power(Integer q, bint get_data) diff --git a/src/sage/libs/pari/convert_sage.pyx b/src/sage/libs/pari/convert_sage.pyx index 8891c69d9ab..4b41e758c68 100644 --- a/src/sage/libs/pari/convert_sage.pyx +++ b/src/sage/libs/pari/convert_sage.pyx @@ -14,7 +14,7 @@ Convert PARI objects to Sage types # http://www.gnu.org/licenses/ #***************************************************************************** -from cysignals.signals cimport sig_on +from cysignals.signals cimport sig_on, sig_off from cypari2.types cimport (GEN, typ, t_INT, t_FRAC, t_REAL, t_COMPLEX, t_INTMOD, t_PADIC, t_INFINITY, t_VEC, t_COL, @@ -24,10 +24,11 @@ from cypari2.pari_instance cimport prec_words_to_bits from cypari2.paridecl cimport * from cypari2.gen cimport objtogen from cypari2.stack cimport new_gen -from .convert_gmp cimport INT_to_mpz, new_gen_from_mpz_t +from .convert_gmp cimport INT_to_mpz, new_gen_from_mpz_t, new_gen_from_mpq_t, INTFRAC_to_mpq -from sage.rings.integer cimport Integer -from sage.rings.rational cimport Rational +from sage.libs.gmp.mpz cimport mpz_fits_slong_p, mpz_sgn, mpz_get_ui, mpz_set, mpz_set_si +from sage.libs.gmp.mpq cimport mpq_denref, mpq_numref +from sage.rings.integer cimport smallInteger from sage.rings.all import RealField, ComplexField, QuadraticField from sage.matrix.args cimport MatrixArgs from sage.rings.padics.factory import Qp @@ -368,3 +369,161 @@ cpdef set_integer_from_gen(Integer self, Gen x): # Now we have a true PARI integer, convert it to Sage INT_to_mpz(self.value, (x).g) + + +cpdef Gen new_gen_from_integer(Integer self): + """ + TESTS:: + + sage: Rational(pari(2)) # indirect doctest + 2 + sage: Rational(pari(-1)) + -1 + """ + return new_gen_from_mpz_t(self.value) + + +cpdef set_rational_from_gen(Rational self, Gen x): + r""" + EXAMPLES:: + + sage: [Rational(pari(x)) for x in [1, 1/2, 2^60, 2., GF(3)(1), GF(9,'a')(2)]] + [1, 1/2, 1152921504606846976, 2, 1, 2] + sage: Rational(pari(2.1)) # indirect doctest + Traceback (most recent call last): + ... + TypeError: Attempt to coerce non-integral real number to an Integer + """ + x = x.simplify() + if is_rational_t(typ((x).g)): + INTFRAC_to_mpq(self.value, (x).g) + else: + a = Integer(x) + mpz_set(mpq_numref(self.value), a.value) + mpz_set_si(mpq_denref(self.value), 1) + + +cpdef Gen new_gen_from_rational(Rational self): + """ + TESTS:: + + sage: Integer(pari(2/2)) # indirect doctest + 1 + sage: Rational(pari(-1/2)) + -1/2 + """ + return new_gen_from_mpq_t(self.value) + + +cpdef list pari_divisors_small(Integer self): + r""" + Return the list of divisors of this number using PARI ``divisorsu``. + + .. SEEALSO:: + + This method is better used through :meth:`sage.rings.integer.Integer.divisors`. + + EXAMPLES:: + + sage: from sage.libs.pari.convert_sage import pari_divisors_small + sage: pari_divisors_small(4) + [1, 2, 4] + + The integer must fit into an unsigned long:: + + sage: pari_divisors_small(-4) + Traceback (most recent call last): + ... + AssertionError + sage: pari_divisors_small(2**65) + Traceback (most recent call last): + ... + AssertionError + """ + # we need n to fit into a long and not a unsigned long in order to use + # smallInteger + assert mpz_fits_slong_p(self.value) and mpz_sgn(self.value) > 0 + + cdef unsigned long n = mpz_get_ui(self.value) + + global avma + cdef pari_sp ltop = avma + cdef GEN d + cdef list output + + try: + sig_on() + d = divisorsu(n) + sig_off() + output = [smallInteger(d[i]) for i in range(1,lg(d))] + return output + finally: + avma = ltop + + +cpdef pari_is_prime(Integer p): + r""" + Return whether ``p`` is a prime. + + The caller must ensure that ``p.value`` fits in a long. + + EXAMPLES:: + + sage: from sage.libs.pari.convert_sage import pari_is_prime + sage: pari_is_prime(2) + True + sage: pari_is_prime(3) + True + sage: pari_is_prime(1) + False + sage: pari_is_prime(4) + False + + Its recommended to use :meth:`sage.rings.integer.Integer.is_prime`, which checks overflow. + The following is incorrect, because the number does not fit into a long:: + + sage: pari_is_prime(2**64 + 2) + True + """ + return bool(uisprime(mpz_get_ui(p.value))) + + +cpdef pari_is_prime_power(Integer q, bint get_data): + r""" + Return whether ``q`` is a prime power. + + The caller must ensure that ``q.value`` fits in a long. + + OUTPUT: + + If ``get_data`` return a tuple of the prime and the exponent. + Otherwise return a boolean. + + EXAMPLES:: + + sage: from sage.libs.pari.convert_sage import pari_is_prime_power + sage: pari_is_prime_power(2, False) + True + sage: pari_is_prime_power(2, True) + (2, 1) + sage: pari_is_prime_power(4, False) + True + sage: pari_is_prime_power(4, True) + (2, 2) + sage: pari_is_prime_power(6, False) + False + sage: pari_is_prime_power(6, True) + (6, 0) + + Its recommended to use :meth:`sage.rings.integer.Integer.is_prime_power`, which checks overflow. + The following is incorrect, because the number does not fit into a long:: + + sage: pari_is_prime_power(2**64 + 2, False) + True + """ + cdef long p, n + n = uisprimepower(mpz_get_ui(q.value), (&p)) + if n: + return (smallInteger(p), smallInteger(n)) if get_data else True + else: + return (q, smallInteger(0)) if get_data else False diff --git a/src/sage/rings/integer.pxd b/src/sage/rings/integer.pxd index eccbf41776c..e2cf5136241 100644 --- a/src/sage/rings/integer.pxd +++ b/src/sage/rings/integer.pxd @@ -31,7 +31,6 @@ cdef class Integer(EuclideanDomainElement): cdef bint _is_power_of(Integer self, Integer n) cdef bint _pseudoprime_is_prime(self, proof) except -1 - cpdef list _pari_divisors_small(self) cdef int mpz_set_str_python(mpz_ptr z, char* s, int base) except -1 diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 1c09eff559a..415d4fe8070 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -173,15 +173,12 @@ from sage.structure.coerce cimport coercion_model from sage.structure.element cimport (Element, EuclideanDomainElement, parent) from sage.structure.parent cimport Parent -from cypari2.paridecl cimport * from sage.rings.rational cimport Rational from sage.arith.rational_reconstruction cimport mpq_rational_reconstruction from sage.libs.gmp.pylong cimport * from sage.libs.gmp.mpq cimport mpq_neg from sage.libs.gmp.binop cimport mpq_add_z, mpq_mul_z, mpq_div_zz -from cypari2.gen cimport objtogen -from sage.libs.pari.convert_gmp cimport new_gen_from_mpz_t from sage.libs.flint.ulong_extras cimport * import sage.rings.infinity @@ -2801,9 +2798,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: ZZ(8).log(int(2)) 3 - + TESTS:: - + sage: (-2).log(3) (I*pi + log(2))/log(3) """ @@ -2948,50 +2945,6 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): prime_factors = prime_divisors - cpdef list _pari_divisors_small(self): - r""" - Return the list of divisors of this number using PARI ``divisorsu``. - - .. SEEALSO:: - - This method is better used through :meth:`divisors`. - - EXAMPLES:: - - sage: 4._pari_divisors_small() - [1, 2, 4] - - The integer must fit into an unsigned long:: - - sage: (-4)._pari_divisors_small() - Traceback (most recent call last): - ... - AssertionError - sage: (2**65)._pari_divisors_small() - Traceback (most recent call last): - ... - AssertionError - """ - # we need n to fit into a long and not a unsigned long in order to use - # smallInteger - assert mpz_fits_slong_p(self.value) and mpz_sgn(self.value) > 0 - - cdef unsigned long n = mpz_get_ui(self.value) - - global avma - cdef pari_sp ltop = avma - cdef GEN d - cdef list output - - try: - sig_on() - d = divisorsu(n) - sig_off() - output = [smallInteger(d[i]) for i in range(1,lg(d))] - return output - finally: - avma = ltop - @cython.boundscheck(False) @cython.wraparound(False) def divisors(self, method=None): @@ -3088,10 +3041,18 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): raise ValueError("n must be nonzero") if (method is None or method == 'pari') and mpz_fits_slong_p(self.value): + try: + from sage.libs.pari.convert_sage import pari_divisors_small + method = 'pari' + except ImportError: + if method == 'pari': + raise ImportError("method `pari` requested, but cypari2 not present") + + if method == 'pari': if mpz_sgn(self.value) > 0: - return self._pari_divisors_small() + return pari_divisors_small(self) else: - return (-self)._pari_divisors_small() + return pari_divisors_small(-self) elif method is not None and method != 'sage': raise ValueError("method must be 'pari' or 'sage'") @@ -5140,30 +5101,27 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if mpz_sgn(self.value) <= 0: return (self, zero) if get_data else False - cdef long p, n if mpz_fits_slong_p(self.value): - # Note that self.value fits in a long, so there is no - # overflow possible because of mixing signed/unsigned longs. - # We call the PARI function uisprimepower() - n = uisprimepower(mpz_get_ui(self.value), (&p)) - if n: - return (smallInteger(p), smallInteger(n)) if get_data else True - else: - return (self, zero) if get_data else False - else: - if proof is None: - from sage.structure.proof.proof import get_flag - proof = get_flag(proof, "arithmetic") + try: + from sage.libs.pari.convert_sage import pari_is_prime_power + return pari_is_prime_power(self, get_data) + except ImportError: + pass - if proof: - n, pari_p = self.__pari__().isprimepower() - else: - n, pari_p = self.__pari__().ispseudoprimepower() + cdef long n + if proof is None: + from sage.structure.proof.proof import get_flag + proof = get_flag(proof, "arithmetic") - if n: - return (Integer(pari_p), smallInteger(n)) if get_data else True - else: - return (self, zero) if get_data else False + if proof: + n, pari_p = self.__pari__().isprimepower() + else: + n, pari_p = self.__pari__().ispseudoprimepower() + + if n: + return (Integer(pari_p), smallInteger(n)) if get_data else True + else: + return (self, zero) if get_data else False def is_prime(self, proof=None): r""" @@ -5229,7 +5187,11 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return False if mpz_fits_ulong_p(self.value): - return bool(uisprime(mpz_get_ui(self.value))) + try: + from sage.libs.pari.convert_sage import pari_is_prime + return pari_is_prime(self) + except ImportError: + pass if proof is None: from sage.structure.proof.proof import get_flag @@ -5580,6 +5542,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): """ + from cypari2.gen import objtogen if self.is_square(): raise ValueError("class_number not defined for square integers") if not self%4 in [0,1]: @@ -5987,7 +5950,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): 1041334 """ - return new_gen_from_mpz_t(self.value) + from sage.libs.pari.convert_sage import new_gen_from_integer + return new_gen_from_integer(self) def _interface_init_(self, I=None): """ diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 72396d24f3e..58e7be8dedd 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -77,10 +77,6 @@ import sage.rings.rational_field cimport sage.rings.integer as integer from .integer cimport Integer -from cypari2.paridecl cimport * -from cypari2.gen cimport Gen as pari_gen -from sage.libs.pari.convert_gmp cimport INT_to_mpz, INTFRAC_to_mpq, new_gen_from_mpq_t - from .integer_ring import ZZ from sage.arith.rational_reconstruction cimport mpq_rational_reconstruction @@ -107,6 +103,13 @@ from cpython.int cimport PyInt_AS_LONG cimport sage.rings.fast_arith import sage.rings.fast_arith + +try: + from cypari2.gen import Gen as pari_gen +except ImportError: + pari_gen = () + + cdef sage.rings.fast_arith.arith_int ai ai = sage.rings.fast_arith.arith_int() @@ -647,13 +650,8 @@ cdef class Rational(sage.structure.element.FieldElement): mpq_canonicalize(self.value) elif isinstance(x, pari_gen): - x = x.simplify() - if is_rational_t(typ((x).g)): - INTFRAC_to_mpq(self.value, (x).g) - else: - a = integer.Integer(x) - mpz_set(mpq_numref(self.value), a.value) - mpz_set_si(mpq_denref(self.value), 1) + from sage.libs.pari.convert_sage import set_rational_from_gen + set_rational_from_gen(self, x) elif isinstance(x, list) and len(x) == 1: self.__set_value(x[0], base) @@ -3150,9 +3148,9 @@ cdef class Rational(sage.structure.element.FieldElement): 3 sage: (125/8).log(5/2,prec=53) 3.00000000000000 - + TESTS:: - + sage: (25/2).log(5/2) log(25/2)/log(5/2) sage: (-1/2).log(3) @@ -3774,7 +3772,8 @@ cdef class Rational(sage.structure.element.FieldElement): sage: m.type() 't_FRAC' """ - return new_gen_from_mpq_t(self.value) + from sage.libs.pari.convert_sage import new_gen_from_rational + return new_gen_from_rational(self) def _interface_init_(self, I=None): """