Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
remove compile time dependency of Integer and Rational on cypari2
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Kliem committed Aug 28, 2021
1 parent 08084b3 commit 9986723
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 93 deletions.
7 changes: 7 additions & 0 deletions src/sage/libs/pari/convert_sage.pxd
Original file line number Diff line number Diff line change
@@ -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)
167 changes: 163 additions & 4 deletions src/sage/libs/pari/convert_sage.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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, (<Gen>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((<Gen>x).g)):
INTFRAC_to_mpq(self.value, (<Gen>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), <ulong*>(&p))
if n:
return (smallInteger(p), smallInteger(n)) if get_data else True
else:
return (q, smallInteger(0)) if get_data else False
1 change: 0 additions & 1 deletion src/sage/rings/integer.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
112 changes: 38 additions & 74 deletions src/sage/rings/integer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
"""
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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'")

Expand Down Expand Up @@ -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), <ulong*>(&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"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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):
"""
Expand Down
Loading

0 comments on commit 9986723

Please sign in to comment.