From 4a5fdc8888c6bda38a0e128a9c68c9711eb35c35 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 13 Apr 2023 19:08:12 -0700 Subject: [PATCH 1/5] sage.rings.factorint: Split into separate modules per library used --- src/doc/en/reference/rings_standard/index.rst | 2 + src/sage/libs/flint/fmpz_factor.pyx | 4 +- src/sage/rings/factorint.pyx | 140 ------------------ src/sage/rings/integer.pyx | 4 +- 4 files changed, 6 insertions(+), 144 deletions(-) diff --git a/src/doc/en/reference/rings_standard/index.rst b/src/doc/en/reference/rings_standard/index.rst index 4bb5338609f..97bf2b8ea35 100644 --- a/src/doc/en/reference/rings_standard/index.rst +++ b/src/doc/en/reference/rings_standard/index.rst @@ -12,6 +12,8 @@ Integers sage/rings/bernmm sage/rings/bernoulli_mod_p sage/rings/factorint + sage/rings/factorint_flint + sage/rings/factorint_pari sage/rings/fast_arith sage/rings/sum_of_squares sage/arith/functions diff --git a/src/sage/libs/flint/fmpz_factor.pyx b/src/sage/libs/flint/fmpz_factor.pyx index 1d49e8ac22b..330ba3d4d4e 100644 --- a/src/sage/libs/flint/fmpz_factor.pyx +++ b/src/sage/libs/flint/fmpz_factor.pyx @@ -8,9 +8,9 @@ cdef fmpz_factor_to_pairlist(const fmpz_factor_t factors): (factor, exponent) pairs. The factors are Integers, and the exponents are Python ints. This is used and indirectly tested by both :func:`qsieve` and - :func:`sage.rings.factorint.factor_using_flint`. The output + :func:`sage.rings.factorint_flint.factor_using_flint`. The output format was ultimately based on that of - :func:`sage.rings.factorint.factor_using_pari`. + :func:`sage.rings.factorint_pari.factor_using_pari`. """ cdef list pairs = [] diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index 77def414b13..9ab7956502f 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -17,12 +17,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -from cysignals.signals cimport sig_on, sig_off - from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.mpz cimport * -from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_set_mpz -from sage.libs.flint.fmpz_factor cimport * from sage.rings.integer cimport Integer from sage.rings.fast_arith import prime_range @@ -292,139 +288,3 @@ cpdef factor_trial_division(m, long limit=LONG_MAX): return IntegerFactorization(F, unit=unit, unsafe=True, sort=False, simplify=False) - - -def factor_using_pari(n, int_=False, debug_level=0, proof=None): - r""" - Factor this integer using PARI. - - This function returns a list of pairs, not a ``Factorization`` - object. The first element of each pair is the factor, of type - ``Integer`` if ``int_`` is ``False`` or ``int`` otherwise, - the second element is the positive exponent, of type ``int``. - - INPUT: - - - ``int_`` -- (default: ``False``), whether the factors are - of type ``int`` instead of ``Integer`` - - - ``debug_level`` -- (default: 0), debug level of the call - to PARI - - - ``proof`` -- (default: ``None``), whether the factors are - required to be proven prime; if ``None``, the global default - is used - - OUTPUT: - - A list of pairs. - - EXAMPLES:: - - sage: factor(-2**72 + 3, algorithm='pari') # indirect doctest - -1 * 83 * 131 * 294971519 * 1472414939 - - Check that PARI's debug level is properly reset (:trac:`18792`):: - - sage: alarm(0.5); factor(2^1000 - 1, verbose=5) - Traceback (most recent call last): - ... - AlarmInterrupt - sage: pari.get_debug_level() - 0 - """ - from sage.libs.pari.all import pari - - if proof is None: - from sage.structure.proof.proof import get_flag - proof = get_flag(proof, "arithmetic") - - prev = pari.get_debug_level() - - cdef Py_ssize_t i - try: - if prev != debug_level: - pari.set_debug_level(debug_level) - - p, e = n.__pari__().factor(proof=proof) - if int_: - return [(int(p[i]), int(e[i])) for i in range(len(p))] - else: - return [(Integer(p[i]), int(e[i])) for i in range(len(p))] - finally: - if prev != debug_level: - pari.set_debug_level(prev) - - -def factor_using_flint(Integer n): - r""" - Factor the nonzero integer ``n`` using FLINT. - - This function returns a list of (factor, exponent) pairs. The - factors will be of type ``Integer``, and the exponents will be of - type ``int``. - - INPUT: - - - ``n`` -- a nonzero sage Integer; the number to factor. - - OUTPUT: - - A list of ``(Integer, int)`` pairs representing the factors and - their exponents. - - EXAMPLES:: - - sage: from sage.rings.factorint import factor_using_flint - sage: n = ZZ(9962572652930382) - sage: factors = factor_using_flint(n) - sage: factors - [(2, 1), (3, 1), (1660428775488397, 1)] - sage: prod( f^e for (f,e) in factors ) == n - True - - Negative numbers will have a leading factor of ``(-1)^1``:: - - sage: n = ZZ(-1 * 2 * 3) - sage: factor_using_flint(n) - [(-1, 1), (2, 1), (3, 1)] - - The factorization of unity is empty:: - - sage: factor_using_flint(ZZ.one()) - [] - - While zero has a single factor, of... zero:: - - sage: factor_using_flint(ZZ.zero()) - [(0, 1)] - - TESTS: - - Check that the integers [-10,000, 10,000] are factored correctly:: - - sage: all( - ....: prod( f^e for (f,e) in factor_using_flint(ZZ(c*k)) ) == c*k - ....: for k in range(10000) - ....: for c in [-1, 1] - ....: ) - True - """ - if n.is_zero(): - return [(n, int(1))] - - cdef fmpz_t p - fmpz_init(p) - fmpz_set_mpz(p, (n).value) - - cdef fmpz_factor_t factors - fmpz_factor_init(factors) - - sig_on() - fmpz_factor(factors, p) - sig_off() - - pairs = fmpz_factor_to_pairlist(factors) - - fmpz_factor_clear(factors) - return pairs diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 61c980557ff..a481436b8d7 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -3985,13 +3985,13 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return factor_trial_division(self) if algorithm == 'pari': - from sage.rings.factorint import factor_using_pari + from sage.rings.factorint_pari import factor_using_pari F = factor_using_pari(n, int_=int_, debug_level=verbose, proof=proof) F.sort() return IntegerFactorization(F, unit=unit, unsafe=True, sort=False, simplify=False) elif algorithm == 'flint': - from sage.rings.factorint import factor_using_flint + from sage.rings.factorint_flint import factor_using_flint F = factor_using_flint(n) F.sort() return IntegerFactorization(F, unit=unit, unsafe=True, From a7a7455611c53f724a681a7fd1ea67976de7a0b7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 13 Apr 2023 19:24:43 -0700 Subject: [PATCH 2/5] src/sage/rings/factorint.pyx: Add # optional, fix doctest coding style, markup --- src/sage/rings/factorint.pyx | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index 9ab7956502f..ba6dc0ed613 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -49,23 +49,23 @@ cpdef aurifeuillian(n, m, F=None, bint check=True): EXAMPLES:: sage: from sage.rings.factorint import aurifeuillian - sage: aurifeuillian(2,2) + sage: aurifeuillian(2, 2) [5, 13] - sage: aurifeuillian(2,2^5) + sage: aurifeuillian(2, 2^5) [1985, 2113] - sage: aurifeuillian(5,3) + sage: aurifeuillian(5, 3) [1471, 2851] - sage: aurifeuillian(15,1) + sage: aurifeuillian(15, 1) [19231, 142111] - sage: aurifeuillian(12,3) + sage: aurifeuillian(12, 3) Traceback (most recent call last): ... ValueError: n has to be square-free - sage: aurifeuillian(1,2) + sage: aurifeuillian(1, 2) Traceback (most recent call last): ... ValueError: n has to be greater than 1 - sage: aurifeuillian(2,0) + sage: aurifeuillian(2, 0) Traceback (most recent call last): ... ValueError: m has to be positive @@ -129,24 +129,24 @@ cpdef factor_aurifeuillian(n, check=True): EXAMPLES:: sage: from sage.rings.factorint import factor_aurifeuillian as fa - sage: fa(2^6+1) + sage: fa(2^6 + 1) # optional - sage.libs.pari [5, 13] - sage: fa(2^58+1) + sage: fa(2^58 + 1) # optional - sage.libs.pari [536838145, 536903681] - sage: fa(3^3+1) + sage: fa(3^3 + 1) # optional - sage.libs.pari [4, 1, 7] - sage: fa(5^5-1) + sage: fa(5^5 - 1) # optional - sage.libs.pari [4, 11, 71] - sage: prod(_) == 5^5-1 + sage: prod(_) == 5^5 - 1 # optional - sage.libs.pari True - sage: fa(2^4+1) + sage: fa(2^4 + 1) # optional - sage.libs.pari [17] - sage: fa((6^2*3)^3+1) + sage: fa((6^2*3)^3 + 1) # optional - sage.libs.pari [109, 91, 127] TESTS:: - sage: for n in [2,3,5,6,30,31,33]: + sage: for n in [2,3,5,6,30,31,33]: # optional - sage.libs.pari ....: for m in [8,96,109201283]: ....: s = -1 if n % 4 == 1 else 1 ....: y = (m^2*n)^n + s @@ -202,9 +202,9 @@ cpdef factor_aurifeuillian(n, check=True): def factor_cunningham(m, proof=None): r""" - Return factorization of self obtained using trial division + Return factorization of ``self`` obtained using trial division for all primes in the so called Cunningham table. This is - efficient if self has some factors of type `b^n+1` or `b^n-1`, + efficient if ``self`` has some factors of type `b^n+1` or `b^n-1`, with `b` in `\{2,3,5,6,7,10,11,12\}`. You need to install an optional package to use this method, @@ -222,7 +222,7 @@ def factor_cunningham(m, proof=None): sage: from sage.rings.factorint import factor_cunningham sage: factor_cunningham(2^257-1) # optional - cunningham_tables 535006138814359 * 1155685395246619182673033 * 374550598501810936581776630096313181393 - sage: factor_cunningham((3^101+1)*(2^60).next_prime(),proof=False) # optional - cunningham_tables + sage: factor_cunningham((3^101+1)*(2^60).next_prime(), proof=False) # optional - cunningham_tables 2^2 * 379963 * 1152921504606847009 * 1017291527198723292208309354658785077827527 """ @@ -245,12 +245,12 @@ def factor_cunningham(m, proof=None): cpdef factor_trial_division(m, long limit=LONG_MAX): r""" - Return partial factorization of self obtained using trial division - for all primes up to limit, where limit must fit in a C signed long. + Return partial factorization of ``self`` obtained using trial division + for all primes up to ``limit``, where ``limit`` must fit in a C ``signed long``. INPUT: - - ``limit`` -- integer (default: ``LONG_MAX``) that fits in a C signed long + - ``limit`` -- integer (default: ``LONG_MAX``) that fits in a C ``signed long`` EXAMPLES:: From 18947368e5947da78b5da03b69d0dc6b3b6b83ac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 13 Apr 2023 19:32:49 -0700 Subject: [PATCH 3/5] src/sage/rings/factorint_*.pyx: New --- src/sage/rings/factorint_flint.pyx | 82 ++++++++++++++++++++++++++++++ src/sage/rings/factorint_pari.pyx | 64 +++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/sage/rings/factorint_flint.pyx create mode 100644 src/sage/rings/factorint_pari.pyx diff --git a/src/sage/rings/factorint_flint.pyx b/src/sage/rings/factorint_flint.pyx new file mode 100644 index 00000000000..0f88789cfc7 --- /dev/null +++ b/src/sage/rings/factorint_flint.pyx @@ -0,0 +1,82 @@ +# sage.doctest: optional - sage.libs.flint + + +from cysignals.signals cimport sig_on, sig_off + +from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_set_mpz +from sage.libs.flint.fmpz_factor cimport * +from sage.rings.integer cimport Integer + + +def factor_using_flint(Integer n): + r""" + Factor the nonzero integer ``n`` using FLINT. + + This function returns a list of (factor, exponent) pairs. The + factors will be of type ``Integer``, and the exponents will be of + type ``int``. + + INPUT: + + - ``n`` -- a nonzero sage Integer; the number to factor. + + OUTPUT: + + A list of ``(Integer, int)`` pairs representing the factors and + their exponents. + + EXAMPLES:: + + sage: from sage.rings.factorint import factor_using_flint + sage: n = ZZ(9962572652930382) + sage: factors = factor_using_flint(n) + sage: factors + [(2, 1), (3, 1), (1660428775488397, 1)] + sage: prod( f^e for (f,e) in factors ) == n + True + + Negative numbers will have a leading factor of ``(-1)^1``:: + + sage: n = ZZ(-1 * 2 * 3) + sage: factor_using_flint(n) + [(-1, 1), (2, 1), (3, 1)] + + The factorization of unity is empty:: + + sage: factor_using_flint(ZZ.one()) + [] + + While zero has a single factor, of... zero:: + + sage: factor_using_flint(ZZ.zero()) + [(0, 1)] + + TESTS: + + Check that the integers [-10,000, 10,000] are factored correctly:: + + sage: all( + ....: prod( f^e for (f,e) in factor_using_flint(ZZ(c*k)) ) == c*k + ....: for k in range(10000) + ....: for c in [-1, 1] + ....: ) + True + """ + if n.is_zero(): + return [(n, int(1))] + + cdef fmpz_t p + fmpz_init(p) + fmpz_set_mpz(p, (n).value) + + cdef fmpz_factor_t factors + fmpz_factor_init(factors) + + sig_on() + fmpz_factor(factors, p) + sig_off() + + pairs = fmpz_factor_to_pairlist(factors) + + fmpz_factor_clear(factors) + return pairs diff --git a/src/sage/rings/factorint_pari.pyx b/src/sage/rings/factorint_pari.pyx new file mode 100644 index 00000000000..7fc86ed661d --- /dev/null +++ b/src/sage/rings/factorint_pari.pyx @@ -0,0 +1,64 @@ +# sage.doctest: optional - sage.libs.pari + +from sage.libs.pari.all import pari +from sage.rings.integer cimport Integer + + +def factor_using_pari(n, int_=False, debug_level=0, proof=None): + r""" + Factor this integer using PARI. + + This function returns a list of pairs, not a :class:`Factorization` + object. The first element of each pair is the factor, of type + ``Integer`` if ``int_`` is ``False`` or ``int`` otherwise, + the second element is the positive exponent, of type ``int``. + + INPUT: + + - ``int_`` -- (default: ``False``), whether the factors are + of type ``int`` instead of ``Integer`` + + - ``debug_level`` -- (default: 0), debug level of the call + to PARI + + - ``proof`` -- (default: ``None``), whether the factors are + required to be proven prime; if ``None``, the global default + is used + + OUTPUT: + + A list of pairs. + + EXAMPLES:: + + sage: factor(-2**72 + 3, algorithm='pari') # indirect doctest + -1 * 83 * 131 * 294971519 * 1472414939 + + Check that PARI's debug level is properly reset (:trac:`18792`):: + + sage: alarm(0.5); factor(2^1000 - 1, verbose=5) + Traceback (most recent call last): + ... + AlarmInterrupt + sage: pari.get_debug_level() + 0 + """ + if proof is None: + from sage.structure.proof.proof import get_flag + proof = get_flag(proof, "arithmetic") + + prev = pari.get_debug_level() + + cdef Py_ssize_t i + try: + if prev != debug_level: + pari.set_debug_level(debug_level) + + p, e = n.__pari__().factor(proof=proof) + if int_: + return [(int(p[i]), int(e[i])) for i in range(len(p))] + else: + return [(Integer(p[i]), int(e[i])) for i in range(len(p))] + finally: + if prev != debug_level: + pari.set_debug_level(prev) From efd1f3baa16b36afeb4ef17faee41e9b1cdba34d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 22 Apr 2023 21:24:58 -0700 Subject: [PATCH 4/5] src/sage/rings/factorint_flint.pyx: Fix import in doctest --- src/sage/rings/factorint_flint.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/factorint_flint.pyx b/src/sage/rings/factorint_flint.pyx index 0f88789cfc7..3582b3792c7 100644 --- a/src/sage/rings/factorint_flint.pyx +++ b/src/sage/rings/factorint_flint.pyx @@ -27,7 +27,7 @@ def factor_using_flint(Integer n): EXAMPLES:: - sage: from sage.rings.factorint import factor_using_flint + sage: from sage.rings.factorint_flint import factor_using_flint sage: n = ZZ(9962572652930382) sage: factors = factor_using_flint(n) sage: factors From e8051b8f935eed0590e0d4500c1eea83e8855a2a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 Apr 2023 19:38:51 -0700 Subject: [PATCH 5/5] src/sage/rings/factorint*: Update/add copyright following 'git blame -M -C src/sage/rings/factorint.pyx -w --date=format:%Y 10.0.rc0 | sort -k2' --- src/sage/rings/factorint.pyx | 5 ++++- src/sage/rings/factorint_flint.pyx | 17 ++++++++++++++++- src/sage/rings/factorint_pari.pyx | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index ba6dc0ed613..5718d6691ae 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -9,7 +9,10 @@ AUTHORS: """ #***************************************************************************** -# Copyright (C) 2010 André Apitzsch +# Copyright (C) 2010-2011 André Apitzsch +# 2012 Nils Bruin +# 2014 David Roe +# 2014 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/rings/factorint_flint.pyx b/src/sage/rings/factorint_flint.pyx index 3582b3792c7..44d8c3ebd4a 100644 --- a/src/sage/rings/factorint_flint.pyx +++ b/src/sage/rings/factorint_flint.pyx @@ -1,5 +1,20 @@ # sage.doctest: optional - sage.libs.flint - +r""" +Integer factorization using FLINT + +AUTHORS: + +- Michael Orlitzky (2023) +""" + +#***************************************************************************** +# Copyright (C) 2023 Michael Orlitzky +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** from cysignals.signals cimport sig_on, sig_off diff --git a/src/sage/rings/factorint_pari.pyx b/src/sage/rings/factorint_pari.pyx index 7fc86ed661d..b94682d0deb 100644 --- a/src/sage/rings/factorint_pari.pyx +++ b/src/sage/rings/factorint_pari.pyx @@ -1,4 +1,20 @@ # sage.doctest: optional - sage.libs.pari +r""" +Integer factorization using PARI + +AUTHORS: + +- Jeroen Demeyer (2015) +""" + +#***************************************************************************** +# Copyright (C) 2015 Jeroen Demeyer +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.libs.pari.all import pari from sage.rings.integer cimport Integer