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

Commit

Permalink
use Pari for composite moduli instead of resorting to generic algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
yyyyx4 committed Aug 14, 2021
1 parent bd073b4 commit 7559691
Showing 1 changed file with 62 additions and 28 deletions.
90 changes: 62 additions & 28 deletions src/sage/rings/finite_rings/integer_mod.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ cdef class IntegerMod_abstract(FiniteRingElement):
else:
return sib(self.parent())(v)

def log(self, b=None, logarithm_exists=False):
def log(self, b=None, **kwargs):
r"""
Return an integer `x` such that `b^x = a`, where
`a` is ``self``.
Expand All @@ -636,19 +636,14 @@ cdef class IntegerMod_abstract(FiniteRingElement):
``R.multiplicative_generator()`` is used, where
``R`` is the parent of ``self``.
- ``logarithm_exists`` - a boolean (default ``False``). If ``True``
it assumes that the logarithm exists in order to speed up
the computation, the code might end up in an infinite loop if this
is set to ``True`` but the logarithm does not exist.
OUTPUT: Integer `x` such that `b^x = a`, if this exists; a ValueError otherwise.
.. NOTE::
If the modulus is prime and b is a generator, this calls Pari's ``znlog``
function, which is rather fast. If not, it falls back on the generic
discrete log implementation in :meth:`sage.groups.generic.discrete_log`.
The algorithm first factors the modulus, then invokes Pari's ``znlog``
function for each odd prime power in the factorization of the modulus.
This method can be quite slow for large moduli.
EXAMPLES::
Expand All @@ -671,6 +666,12 @@ cdef class IntegerMod_abstract(FiniteRingElement):
sage: x^a
4294967356
An example with a highly composite modulus::
sage: m = 2^99 * 77^7 * 123456789 * 13712923537615486607^2
sage: pow(5, 5735816763073854953388147237921, m).log(5)
5735816763073854953388147237921
Things that can go wrong. E.g., if the base is not a generator for
the multiplicative group, or not even a unit.
Expand Down Expand Up @@ -710,6 +711,11 @@ cdef class IntegerMod_abstract(FiniteRingElement):
sage: (x^e).log(x)==e
True
Examples like this took extremey long before :trac:`32375`::
sage: pow(5, 10^50-1, 123337052926643**4).log(5)
99999999999999999999999999999999999999999999999999
AUTHORS:
- David Joyner and William Stein (2005-11)
Expand All @@ -718,40 +724,68 @@ cdef class IntegerMod_abstract(FiniteRingElement):
by David Kohel.
- Simon King (2010-07-07): fix a side effect on PARI
- Lorenz Panny (2021-08-14): speedups for composite moduli
"""

for kw in kwargs.keys():
if kw == 'logarithm_exists':
from sage.misc.superseded import deprecation
deprecation(32375, 'The "logarithm_exists" keyword to .log() is no longer necessary and will be removed at some point.')
else:
raise TypeError(f'log() got an unexpected keyword argument: {kw!r}')

if not self.is_unit():
raise ValueError("logarithm of %s is not defined since it is not a unit modulo %s"%(self, self.modulus()))

if b is None:
b = self._parent.multiplicative_generator()
logarithm_exists = True
else:
b = self._parent(b)
if not (logarithm_exists or b.is_unit()):
if not b.is_unit():
raise ValueError("logarithm with base %s is not defined since it is not a unit modulo %s"%(b, b.modulus()))

if logarithm_exists or self.modulus().is_prime():
if not logarithm_exists:
oa = self.multiplicative_order()
ob = b.multiplicative_order()
if not oa.divides(ob):
raise ValueError("No discrete log of %s found to base %s modulo %s"%(self, b, self.modulus()))
exc = lambda mod: ValueError("No discrete log of %s found to base %s modulo %s"%(self, b, mod))

n, m = 0, 1

for p,e in self.modulus().factor():

q = p**e
a_red = Mod(self.lift(), q)
b_red = Mod(b.lift(), q)

na, nb = a_red.multiplicative_order(), b_red.multiplicative_order()
if na > nb: # cannot be a power of b
raise exc(q)

if p == 2 and e >= 3: # (ZZ/2^e)* is not cyclic; must not give unsolvable DLPs to Pari

from sage.groups.generic import discrete_log
try:
v = discrete_log(a_red, b_red, nb)
except ValueError:
raise exc(q)

try:
n = pari(self).znlog(pari(b), pari(b.multiplicative_order()))
n = n.sage()
except PariError as msg:
raise RuntimeError("%s\nPARI failed to compute discrete log (perhaps base is not a generator or is too large)" % msg)
else:
if n == []:
raise ValueError("No discrete log of %s found to base %s modulo %s"%(self, b, self.modulus()))
return n

try:
v = pari(a_red).znlog(pari(b_red)).sage()
except PariError as msg:
raise RuntimeError("%s\nPARI failed to compute discrete log modulo %s (perhaps base is not a generator or is too large)" % (msg, q))
if v == []:
raise exc(q)

from sage.arith.all import crt, lcm
try:
n = crt(n, v, m, nb)
m = lcm(m, nb)
except ValueError: # contradictory partial solutions
raise exc(self.modulus())

else: # fall back on slower native implementation
assert b**n == self

from sage.groups.generic import discrete_log
return discrete_log(self, b)
return n

def generalised_log(self):
r"""
Expand Down

0 comments on commit 7559691

Please sign in to comment.