Skip to content

Commit 34de976

Browse files
author
Release Manager
committed
gh-37329: add optional order= argument to .log() method for PARI finite-field elements When the order of the base element of a finite-field logarithm is already known, factoring the unit-group order can be skipped. PARI's `fflog()` accepts the order of the base element as an optional input, but that argument is currently not exposed in Sage. This patch takes care of it. An example of the speedup is given in the new doctests. URL: #37329 Reported by: Lorenz Panny Reviewer(s): Giacomo Pope
2 parents 7f85dca + 6bd17e5 commit 34de976

File tree

1 file changed

+50
-13
lines changed

1 file changed

+50
-13
lines changed

src/sage/rings/finite_rings/element_pari_ffelt.pyx

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,14 +1100,17 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement):
11001100
else:
11011101
raise ValueError("element is not a square")
11021102

1103-
def log(self, base):
1103+
def log(self, base, order=None, *, check=False):
11041104
"""
11051105
Return a discrete logarithm of ``self`` with respect to the
11061106
given base.
11071107
11081108
INPUT:
11091109
11101110
- ``base`` -- non-zero field element
1111+
- ``order`` -- integer (optional), the order of the base
1112+
- ``check`` -- boolean (optional, default ``False``): If set,
1113+
test whether the given ``order`` is correct.
11111114
11121115
OUTPUT:
11131116
@@ -1133,6 +1136,23 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement):
11331136
sage: F(1).log(a)
11341137
0
11351138
1139+
::
1140+
1141+
sage: p = 2^127-1
1142+
sage: F.<t> = GF((p, 3))
1143+
sage: elt = F.random_element()^(p^2+p+1)
1144+
sage: (elt^2).log(elt, p-1)
1145+
2
1146+
1147+
Passing the ``order`` argument can lead to huge speedups when
1148+
factoring the order of the entire unit group is expensive but
1149+
the order of the base element is much smaller::
1150+
1151+
sage: %timeit (elt^2).log(elt) # not tested
1152+
6.18 s ± 85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1153+
sage: %timeit (elt^2).log(elt, p-1) # not tested
1154+
147 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1155+
11361156
Some cases where the logarithm is not defined or does not exist::
11371157
11381158
sage: F.<a> = GF(3^10, impl='pari_ffelt')
@@ -1148,27 +1168,44 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement):
11481168
Traceback (most recent call last):
11491169
...
11501170
ArithmeticError: discrete logarithm of 0 is not defined
1171+
1172+
TESTS:
1173+
1174+
An example for ``check=True``::
1175+
1176+
sage: a = GF(101^5).primitive_element()
1177+
sage: a.log(a, 10510100500, check=True)
1178+
1
1179+
sage: a.log(a, 5255050250, check=True)
1180+
Traceback (most recent call last):
1181+
...
1182+
ValueError: element does not have the provided order
11511183
"""
11521184
base = self._parent(base)
11531185
if self.is_zero():
11541186
raise ArithmeticError("discrete logarithm of 0 is not defined")
11551187
if base.is_zero():
11561188
raise ArithmeticError("discrete logarithm with base 0 is not defined")
11571189

1158-
# Compute the orders of self and base to check whether self
1159-
# actually lies in the cyclic group generated by base. PARI
1160-
# requires that this is the case.
1161-
# We also have to specify the order of the base anyway
1162-
# because PARI assumes by default that this element generates
1163-
# the multiplicative group.
1164-
cdef GEN x, base_order, self_order
1190+
# Compute the order of the base to check whether the element actually
1191+
# lies in the group generated by the base. PARI may otherwise enter an
1192+
# infinite loop.
1193+
# We also have to specify the order of the base anyway as PARI assumes
1194+
# by default that the base generates the entire multiplicative group.
1195+
cdef GEN x, base_order
11651196
sig_on()
1166-
base_order = FF_order((<FiniteFieldElement_pari_ffelt>base).val, NULL)
1167-
self_order = FF_order(self.val, NULL)
1168-
if not dvdii(base_order, self_order):
1169-
# self_order does not divide base_order
1197+
if order is None:
1198+
base_order = FF_order((<FiniteFieldElement_pari_ffelt>base).val, NULL)
1199+
else:
1200+
if check:
1201+
from sage.groups.generic import has_order
1202+
if not has_order(base, order, '*'):
1203+
clear_stack()
1204+
raise ValueError('element does not have the provided order')
1205+
base_order = _new_GEN_from_mpz_t((<Integer>order).value)
1206+
if not gequal1(powgi(self.val, base_order)):
11701207
clear_stack()
1171-
raise ArithmeticError("element %s does not lie in group generated by %s"%(self, base))
1208+
raise ArithmeticError(f'element {self} does not lie in group generated by {base}')
11721209
x = FF_log(self.val, (<FiniteFieldElement_pari_ffelt>base).val, base_order)
11731210
return Integer(new_gen(x))
11741211

0 commit comments

Comments
 (0)