@@ -1182,16 +1182,24 @@ def _divide_out(self, p):
1182
1182
pts = Q .division_points (p )
1183
1183
return (Q , k )
1184
1184
1185
- def set_order (self , value , * , check = True ):
1185
+ def set_order (self , value = None , * , multiple = None , check = True ):
1186
1186
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``.
1188
1189
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.
1191
1198
1192
1199
INPUT:
1193
1200
1194
1201
- ``value`` -- positive integer
1202
+ - ``multiple`` -- positive integer; mutually exclusive with ``value``
1195
1203
1196
1204
OUTPUT: ``None``
1197
1205
@@ -1206,6 +1214,10 @@ def set_order(self, value, *, check=True):
1206
1214
sage: G.set_order(2) # optional - sage.rings.finite_rings
1207
1215
sage: 2*G # optional - sage.rings.finite_rings
1208
1216
(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
1209
1221
1210
1222
We now give a more interesting case, the NIST-P521 curve. Its
1211
1223
order is too big to calculate with Sage, and takes a long time
@@ -1227,7 +1239,31 @@ def set_order(self, value, *, check=True):
1227
1239
(0 : 1 : 0)
1228
1240
sage: proof.arithmetic(prev_proof_state) # restore state
1229
1241
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`::
1231
1267
1232
1268
sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
1233
1269
sage: G = E.random_point() # optional - sage.rings.finite_rings
@@ -1251,20 +1287,53 @@ def set_order(self, value, *, check=True):
1251
1287
...
1252
1288
ValueError: Value 11 illegal: 11 * (5 : 0 : 1) is not the identity
1253
1289
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::
1257
1292
1258
1293
sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
1259
1294
sage: G = E(5, 0) # G has order 2 # optional - sage.rings.finite_rings
1260
1295
sage: G.set_order(8) # optional - sage.rings.finite_rings
1261
1296
sage: G.order() # optional - sage.rings.finite_rings
1262
1297
8
1263
1298
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
+
1264
1319
AUTHORS:
1265
1320
1266
1321
- Mariah Lenox (2011-02-16)
1322
+ - Lorenz Panny (2022): add ``multiple=`` option
1267
1323
"""
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
+
1268
1337
value = Integer (value )
1269
1338
1270
1339
if check :
@@ -1277,6 +1346,9 @@ def set_order(self, value, *, check=True):
1277
1346
raise ValueError ('Value %s illegal: outside max Hasse bound' % value )
1278
1347
if value * self != E (0 ):
1279
1348
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
+
1280
1352
self ._order = value
1281
1353
1282
1354
# ############################# end ################################
0 commit comments