Skip to content

Commit 5f7351b

Browse files
author
Release Manager
committed
Trac #30022: sage.rings.integer, rational: Remove compile-time dependency on cypari2 and flint
Functions such as `set_from_pari_gen` should probably be moved to `sage.libs.pari.convert_sage`. The compile-time dependency on `sage.libs.flint.ulong_extras` is for the use of `n_factor`. URL: https://trac.sagemath.org/30022 Reported by: mkoeppe Ticket author(s): Matthias Koeppe, Jonathan Kliem Reviewer(s): Jonathan Kliem, Matthias Koeppe
2 parents 39228cb + b4477da commit 5f7351b

File tree

6 files changed

+314
-154
lines changed

6 files changed

+314
-154
lines changed

src/sage/libs/flint/ulong_extras.pyx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def n_factor_to_list(unsigned long n, int proved):
2+
"""
3+
A wrapper around ``n_factor``.
4+
5+
EXAMPLES::
6+
7+
sage: from sage.libs.flint.ulong_extras import n_factor_to_list
8+
sage: n_factor_to_list(60, 20)
9+
[(2, 2), (3, 1), (5, 1)]
10+
sage: n_factor_to_list((10**6).next_prime() + 1, 0)
11+
[(2, 2), (53, 2), (89, 1)]
12+
"""
13+
cdef n_factor_t f
14+
n_factor_init(&f)
15+
n_factor(&f, n, proved)
16+
return [(f.p[i], int(f.exp[i])) for i in range(f.num)]

src/sage/libs/pari/convert_sage.pxd

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
from cypari2.gen cimport Gen
2+
from sage.rings.integer cimport Integer
3+
from sage.rings.rational cimport Rational
24

35
cpdef gen_to_sage(Gen z, locals=*)
6+
7+
cpdef set_integer_from_gen(Integer self, Gen x)
8+
cpdef Gen new_gen_from_integer(Integer self)
9+
cpdef set_rational_from_gen(Rational self, Gen x)
10+
cpdef Gen new_gen_from_rational(Rational self)
11+
12+
cpdef pari_is_prime(Integer p)
13+
cpdef pari_is_prime_power(Integer q, bint get_data)

src/sage/libs/pari/convert_sage.pyx

+216-4
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,21 @@ Convert PARI objects to Sage types
1414
# http://www.gnu.org/licenses/
1515
#*****************************************************************************
1616

17+
from cysignals.signals cimport sig_on, sig_off
18+
1719
from cypari2.types cimport (GEN, typ, t_INT, t_FRAC, t_REAL, t_COMPLEX,
1820
t_INTMOD, t_PADIC, t_INFINITY, t_VEC, t_COL,
1921
t_VECSMALL, t_MAT, t_STR,
2022
lg, precp)
2123
from cypari2.pari_instance cimport prec_words_to_bits
22-
from cypari2.paridecl cimport gel, inf_get_sign
23-
24-
from sage.rings.integer cimport Integer
25-
from sage.rings.rational cimport Rational
24+
from cypari2.paridecl cimport *
25+
from cypari2.gen cimport objtogen
26+
from cypari2.stack cimport new_gen
27+
from .convert_gmp cimport INT_to_mpz, new_gen_from_mpz_t, new_gen_from_mpq_t, INTFRAC_to_mpq
28+
29+
from sage.libs.gmp.mpz cimport mpz_fits_slong_p, mpz_sgn, mpz_get_ui, mpz_set, mpz_set_si
30+
from sage.libs.gmp.mpq cimport mpq_denref, mpq_numref
31+
from sage.rings.integer cimport smallInteger
2632
from sage.rings.all import RealField, ComplexField, QuadraticField
2733
from sage.matrix.args cimport MatrixArgs
2834
from sage.rings.padics.factory import Qp
@@ -315,3 +321,209 @@ cpdef gen_to_sage(Gen z, locals=None):
315321
from sage.misc.sage_eval import sage_eval
316322
locals = {} if locals is None else locals
317323
return sage_eval(str(z), locals=locals)
324+
325+
326+
cpdef set_integer_from_gen(Integer self, Gen x):
327+
r"""
328+
EXAMPLES::
329+
330+
sage: [Integer(pari(x)) for x in [1, 2^60, 2., GF(3)(1), GF(9,'a')(2)]]
331+
[1, 1152921504606846976, 2, 1, 2]
332+
sage: Integer(pari(2.1)) # indirect doctest
333+
Traceback (most recent call last):
334+
...
335+
TypeError: Attempt to coerce non-integral real number to an Integer
336+
"""
337+
# Simplify and lift until we get an integer
338+
while typ((<Gen>x).g) != t_INT:
339+
x = x.simplify()
340+
paritype = typ((<Gen>x).g)
341+
if paritype == t_INT:
342+
break
343+
elif paritype == t_REAL:
344+
# Check that the fractional part is zero
345+
if not x.frac().gequal0():
346+
raise TypeError("Attempt to coerce non-integral real number to an Integer")
347+
# floor yields an integer
348+
x = x.floor()
349+
break
350+
elif paritype == t_PADIC:
351+
if x._valp() < 0:
352+
raise TypeError("Cannot convert p-adic with negative valuation to an integer")
353+
# Lifting a PADIC yields an integer
354+
x = x.lift()
355+
break
356+
elif paritype == t_INTMOD:
357+
# Lifting an INTMOD yields an integer
358+
x = x.lift()
359+
break
360+
elif paritype == t_POLMOD:
361+
x = x.lift()
362+
elif paritype == t_FFELT:
363+
# x = (f modulo defining polynomial of finite field);
364+
# we extract f.
365+
sig_on()
366+
x = new_gen(FF_to_FpXQ_i((<Gen>x).g))
367+
else:
368+
raise TypeError("Unable to coerce PARI %s to an Integer"%x)
369+
370+
# Now we have a true PARI integer, convert it to Sage
371+
INT_to_mpz(self.value, (<Gen>x).g)
372+
373+
374+
cpdef Gen new_gen_from_integer(Integer self):
375+
"""
376+
TESTS::
377+
378+
sage: Rational(pari(2)) # indirect doctest
379+
2
380+
sage: Rational(pari(-1))
381+
-1
382+
"""
383+
return new_gen_from_mpz_t(self.value)
384+
385+
386+
cpdef set_rational_from_gen(Rational self, Gen x):
387+
r"""
388+
EXAMPLES::
389+
390+
sage: [Rational(pari(x)) for x in [1, 1/2, 2^60, 2., GF(3)(1), GF(9,'a')(2)]]
391+
[1, 1/2, 1152921504606846976, 2, 1, 2]
392+
sage: Rational(pari(2.1)) # indirect doctest
393+
Traceback (most recent call last):
394+
...
395+
TypeError: Attempt to coerce non-integral real number to an Integer
396+
"""
397+
x = x.simplify()
398+
if is_rational_t(typ((<Gen>x).g)):
399+
INTFRAC_to_mpq(self.value, (<Gen>x).g)
400+
else:
401+
a = Integer(x)
402+
mpz_set(mpq_numref(self.value), a.value)
403+
mpz_set_si(mpq_denref(self.value), 1)
404+
405+
406+
cpdef Gen new_gen_from_rational(Rational self):
407+
"""
408+
TESTS::
409+
410+
sage: Integer(pari(2/2)) # indirect doctest
411+
1
412+
sage: Rational(pari(-1/2))
413+
-1/2
414+
"""
415+
return new_gen_from_mpq_t(self.value)
416+
417+
418+
cpdef list pari_divisors_small(Integer self):
419+
r"""
420+
Return the list of divisors of this number using PARI ``divisorsu``.
421+
422+
.. SEEALSO::
423+
424+
This method is better used through :meth:`sage.rings.integer.Integer.divisors`.
425+
426+
EXAMPLES::
427+
428+
sage: from sage.libs.pari.convert_sage import pari_divisors_small
429+
sage: pari_divisors_small(4)
430+
[1, 2, 4]
431+
432+
The integer must fit into an unsigned long::
433+
434+
sage: pari_divisors_small(-4)
435+
Traceback (most recent call last):
436+
...
437+
AssertionError
438+
sage: pari_divisors_small(2**65)
439+
Traceback (most recent call last):
440+
...
441+
AssertionError
442+
"""
443+
# we need n to fit into a long and not a unsigned long in order to use
444+
# smallInteger
445+
assert mpz_fits_slong_p(self.value) and mpz_sgn(self.value) > 0
446+
447+
cdef unsigned long n = mpz_get_ui(self.value)
448+
449+
global avma
450+
cdef pari_sp ltop = avma
451+
cdef GEN d
452+
cdef list output
453+
454+
try:
455+
sig_on()
456+
d = divisorsu(n)
457+
sig_off()
458+
output = [smallInteger(d[i]) for i in range(1,lg(d))]
459+
return output
460+
finally:
461+
avma = ltop
462+
463+
464+
cpdef pari_is_prime(Integer p):
465+
r"""
466+
Return whether ``p`` is a prime.
467+
468+
The caller must ensure that ``p.value`` fits in a long.
469+
470+
EXAMPLES::
471+
472+
sage: from sage.libs.pari.convert_sage import pari_is_prime
473+
sage: pari_is_prime(2)
474+
True
475+
sage: pari_is_prime(3)
476+
True
477+
sage: pari_is_prime(1)
478+
False
479+
sage: pari_is_prime(4)
480+
False
481+
482+
Its recommended to use :meth:`sage.rings.integer.Integer.is_prime`, which checks overflow.
483+
The following is incorrect, because the number does not fit into a long::
484+
485+
sage: pari_is_prime(2**64 + 2)
486+
True
487+
"""
488+
return bool(uisprime(mpz_get_ui(p.value)))
489+
490+
491+
cpdef pari_is_prime_power(Integer q, bint get_data):
492+
r"""
493+
Return whether ``q`` is a prime power.
494+
495+
The caller must ensure that ``q.value`` fits in a long.
496+
497+
OUTPUT:
498+
499+
If ``get_data`` return a tuple of the prime and the exponent.
500+
Otherwise return a boolean.
501+
502+
EXAMPLES::
503+
504+
sage: from sage.libs.pari.convert_sage import pari_is_prime_power
505+
sage: pari_is_prime_power(2, False)
506+
True
507+
sage: pari_is_prime_power(2, True)
508+
(2, 1)
509+
sage: pari_is_prime_power(4, False)
510+
True
511+
sage: pari_is_prime_power(4, True)
512+
(2, 2)
513+
sage: pari_is_prime_power(6, False)
514+
False
515+
sage: pari_is_prime_power(6, True)
516+
(6, 0)
517+
518+
Its recommended to use :meth:`sage.rings.integer.Integer.is_prime_power`, which checks overflow.
519+
The following is incorrect, because the number does not fit into a long::
520+
521+
sage: pari_is_prime_power(2**64 + 2, False)
522+
True
523+
"""
524+
cdef long p, n
525+
n = uisprimepower(mpz_get_ui(q.value), <ulong*>(&p))
526+
if n:
527+
return (smallInteger(p), smallInteger(n)) if get_data else True
528+
else:
529+
return (q, smallInteger(0)) if get_data else False

src/sage/rings/integer.pxd

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ cdef class Integer(EuclideanDomainElement):
3131
cdef bint _is_power_of(Integer self, Integer n)
3232

3333
cdef bint _pseudoprime_is_prime(self, proof) except -1
34-
cpdef list _pari_divisors_small(self)
3534

3635
cdef int mpz_set_str_python(mpz_ptr z, char* s, int base) except -1
3736

0 commit comments

Comments
 (0)