Skip to content

Commit 3e97ff9

Browse files
committed
add multiple= option to EllipticCurvePoint_field.set_order()
1 parent 1ca4a47 commit 3e97ff9

File tree

1 file changed

+80
-8
lines changed

1 file changed

+80
-8
lines changed

src/sage/schemes/elliptic_curves/ell_point.py

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,16 +1182,24 @@ def _divide_out(self, p):
11821182
pts = Q.division_points(p)
11831183
return (Q, k)
11841184

1185-
def set_order(self, value, *, check=True):
1185+
def set_order(self, value=None, *, multiple=None, check=True):
11861186
r"""
1187-
Set the value of ``self._order`` to ``value``.
1187+
Set the cached order of this point (i.e., the value of
1188+
``self._order``) to the given ``value``.
11881189
1189-
Use this when you know a priori the order of this point to avoid a
1190-
potentially expensive order calculation.
1190+
Alternatively, when ``multiple`` is given, this method will
1191+
first run :func:`~sage.groups.generic.order_from_multiple`
1192+
to determine the exact order from the given multiple of the
1193+
point order, then cache the result.
1194+
1195+
Use this when you know a priori the order of this point, or
1196+
a multiple of the order, to avoid a potentially expensive
1197+
order calculation.
11911198
11921199
INPUT:
11931200
11941201
- ``value`` -- positive integer
1202+
- ``multiple`` -- positive integer; mutually exclusive with ``value``
11951203
11961204
OUTPUT: ``None``
11971205
@@ -1206,6 +1214,10 @@ def set_order(self, value, *, check=True):
12061214
sage: G.set_order(2) # optional - sage.rings.finite_rings
12071215
sage: 2*G # optional - sage.rings.finite_rings
12081216
(0 : 1 : 0)
1217+
sage: G = E(0, 6) # optional - sage.rings.finite_rings
1218+
sage: G.set_order(multiple=12) # optional - sage.rings.finite_rings
1219+
sage: G._order # optional - sage.rings.finite_rings
1220+
3
12091221
12101222
We now give a more interesting case, the NIST-P521 curve. Its
12111223
order is too big to calculate with Sage, and takes a long time
@@ -1227,7 +1239,31 @@ def set_order(self, value, *, check=True):
12271239
(0 : 1 : 0)
12281240
sage: proof.arithmetic(prev_proof_state) # restore state
12291241
1230-
It is an error to pass a `value` equal to `0`::
1242+
Using ``.set_order()`` with a ``multiple=`` argument can
1243+
be used to compute a point's order *significantly* faster
1244+
than calling :meth:`order` if the point is already known
1245+
to be `m`-torsion::
1246+
1247+
sage: F.<a> = GF((10007, 23))
1248+
sage: E = EllipticCurve(F, [9,9])
1249+
sage: n = E.order()
1250+
sage: m = 5 * 47 * 139 * 1427 * 2027 * 4831 * 275449 * 29523031
1251+
sage: assert m.divides(n)
1252+
sage: P = n/m * E.lift_x(6747+a)
1253+
sage: assert m * P == 0
1254+
sage: P.set_order(multiple=m) # compute exact order
1255+
sage: factor(m // P.order()) # order is now cached
1256+
47 * 139
1257+
1258+
The algorithm used internally for this functionality is
1259+
:meth:`~sage.groups.generic.order_from_multiple`.
1260+
Indeed, simply calling :meth:`order` on ``P`` would take
1261+
much longer since factoring ``n`` is fairly expensive::
1262+
1263+
sage: n == m * 6670822796985115651 * 441770032618665681677 * 9289973478285634606114927
1264+
True
1265+
1266+
It is an error to pass a ``value`` equal to `0`::
12311267
12321268
sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
12331269
sage: G = E.random_point() # optional - sage.rings.finite_rings
@@ -1251,20 +1287,53 @@ def set_order(self, value, *, check=True):
12511287
...
12521288
ValueError: Value 11 illegal: 11 * (5 : 0 : 1) is not the identity
12531289
1254-
However, ``set_order`` can be fooled, though it's not likely in "real cases
1255-
of interest". For instance, the order can be set to a multiple the
1256-
actual order::
1290+
However, ``set_order`` can be fooled. For instance, the order
1291+
can be set to a multiple the actual order::
12571292
12581293
sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
12591294
sage: G = E(5, 0) # G has order 2 # optional - sage.rings.finite_rings
12601295
sage: G.set_order(8) # optional - sage.rings.finite_rings
12611296
sage: G.order() # optional - sage.rings.finite_rings
12621297
8
12631298
1299+
TESTS:
1300+
1301+
Check that some invalid inputs are caught::
1302+
1303+
sage: E = EllipticCurve(GF(101), [5,5])
1304+
sage: P = E.lift_x(11)
1305+
sage: P.set_order(17, multiple=119)
1306+
Traceback (most recent call last):
1307+
...
1308+
ValueError: cannot pass both value and multiple
1309+
sage: P.set_order(17)
1310+
sage: P.set_order(multiple=119+1)
1311+
Traceback (most recent call last):
1312+
...
1313+
ValueError: previously cached order 17 does not divide given multiple 120
1314+
sage: P.set_order(119)
1315+
Traceback (most recent call last):
1316+
...
1317+
ValueError: value 119 contradicts previously cached order 17
1318+
12641319
AUTHORS:
12651320
12661321
- Mariah Lenox (2011-02-16)
1322+
- Lorenz Panny (2022): add ``multiple=`` option
12671323
"""
1324+
if multiple is not None:
1325+
if value is not None:
1326+
raise ValueError('cannot pass both value and multiple')
1327+
1328+
if hasattr(self, '_order'): # already known
1329+
if check and not self._order.divides(multiple):
1330+
raise ValueError(f'previously cached order {self._order} does not divide given multiple {multiple}')
1331+
return
1332+
1333+
from sage.groups.generic import order_from_multiple
1334+
value = order_from_multiple(self, multiple, check=check)
1335+
check = False
1336+
12681337
value = Integer(value)
12691338

12701339
if check:
@@ -1277,6 +1346,9 @@ def set_order(self, value, *, check=True):
12771346
raise ValueError('Value %s illegal: outside max Hasse bound' % value)
12781347
if value * self != E(0):
12791348
raise ValueError('Value %s illegal: %s * %s is not the identity' % (value, value, self))
1349+
if hasattr(self, '_order') and self._order != value: # already known
1350+
raise ValueError(f'value {value} contradicts previously cached order {self._order}')
1351+
12801352
self._order = value
12811353

12821354
# ############################# end ################################

0 commit comments

Comments
 (0)