@@ -1188,16 +1188,24 @@ def _divide_out(self, p):
11881188 pts = Q .division_points (p )
11891189 return (Q , k )
11901190
1191- def set_order (self , value , * , check = True ):
1191+ def set_order (self , value = None , * , multiple = None , check = True ):
11921192 r"""
1193- Set the value of ``self._order`` to ``value``.
1193+ Set the cached order of this point (i.e., the value of
1194+ ``self._order``) to the given ``value``.
11941195
1195- Use this when you know a priori the order of this point to avoid a
1196- potentially expensive order calculation.
1196+ Alternatively, when ``multiple`` is given, this method will
1197+ first run :func:`~sage.groups.generic.order_from_multiple`
1198+ to determine the exact order from the given multiple of the
1199+ point order, then cache the result.
1200+
1201+ Use this when you know a priori the order of this point, or
1202+ a multiple of the order, to avoid a potentially expensive
1203+ order calculation.
11971204
11981205 INPUT:
11991206
12001207 - ``value`` -- positive integer
1208+ - ``multiple`` -- positive integer; mutually exclusive with ``value``
12011209
12021210 OUTPUT: ``None``
12031211
@@ -1212,6 +1220,10 @@ def set_order(self, value, *, check=True):
12121220 sage: G.set_order(2) # optional - sage.rings.finite_rings
12131221 sage: 2*G # optional - sage.rings.finite_rings
12141222 (0 : 1 : 0)
1223+ sage: G = E(0, 6) # optional - sage.rings.finite_rings
1224+ sage: G.set_order(multiple=12) # optional - sage.rings.finite_rings
1225+ sage: G._order # optional - sage.rings.finite_rings
1226+ 3
12151227
12161228 We now give a more interesting case, the NIST-P521 curve. Its
12171229 order is too big to calculate with Sage, and takes a long time
@@ -1233,7 +1245,31 @@ def set_order(self, value, *, check=True):
12331245 (0 : 1 : 0)
12341246 sage: proof.arithmetic(prev_proof_state) # restore state
12351247
1236- It is an error to pass a `value` equal to `0`::
1248+ Using ``.set_order()`` with a ``multiple=`` argument can
1249+ be used to compute a point's order *significantly* faster
1250+ than calling :meth:`order` if the point is already known
1251+ to be `m`-torsion::
1252+
1253+ sage: F.<a> = GF((10007, 23))
1254+ sage: E = EllipticCurve(F, [9,9])
1255+ sage: n = E.order()
1256+ sage: m = 5 * 47 * 139 * 1427 * 2027 * 4831 * 275449 * 29523031
1257+ sage: assert m.divides(n)
1258+ sage: P = n/m * E.lift_x(6747+a)
1259+ sage: assert m * P == 0
1260+ sage: P.set_order(multiple=m) # compute exact order
1261+ sage: factor(m // P.order()) # order is now cached
1262+ 47 * 139
1263+
1264+ The algorithm used internally for this functionality is
1265+ :meth:`~sage.groups.generic.order_from_multiple`.
1266+ Indeed, simply calling :meth:`order` on ``P`` would take
1267+ much longer since factoring ``n`` is fairly expensive::
1268+
1269+ sage: n == m * 6670822796985115651 * 441770032618665681677 * 9289973478285634606114927
1270+ True
1271+
1272+ It is an error to pass a ``value`` equal to `0`::
12371273
12381274 sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
12391275 sage: G = E.random_point() # optional - sage.rings.finite_rings
@@ -1257,20 +1293,53 @@ def set_order(self, value, *, check=True):
12571293 ...
12581294 ValueError: Value 11 illegal: 11 * (5 : 0 : 1) is not the identity
12591295
1260- However, ``set_order`` can be fooled, though it's not likely in "real cases
1261- of interest". For instance, the order can be set to a multiple the
1262- actual order::
1296+ However, ``set_order`` can be fooled. For instance, the order
1297+ can be set to a multiple the actual order::
12631298
12641299 sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 # optional - sage.rings.finite_rings
12651300 sage: G = E(5, 0) # G has order 2 # optional - sage.rings.finite_rings
12661301 sage: G.set_order(8) # optional - sage.rings.finite_rings
12671302 sage: G.order() # optional - sage.rings.finite_rings
12681303 8
12691304
1305+ TESTS:
1306+
1307+ Check that some invalid inputs are caught::
1308+
1309+ sage: E = EllipticCurve(GF(101), [5,5])
1310+ sage: P = E.lift_x(11)
1311+ sage: P.set_order(17, multiple=119)
1312+ Traceback (most recent call last):
1313+ ...
1314+ ValueError: cannot pass both value and multiple
1315+ sage: P.set_order(17)
1316+ sage: P.set_order(multiple=119+1)
1317+ Traceback (most recent call last):
1318+ ...
1319+ ValueError: previously cached order 17 does not divide given multiple 120
1320+ sage: P.set_order(119)
1321+ Traceback (most recent call last):
1322+ ...
1323+ ValueError: value 119 contradicts previously cached order 17
1324+
12701325 AUTHORS:
12711326
12721327 - Mariah Lenox (2011-02-16)
1328+ - Lorenz Panny (2022): add ``multiple=`` option
12731329 """
1330+ if multiple is not None :
1331+ if value is not None :
1332+ raise ValueError ('cannot pass both value and multiple' )
1333+
1334+ if hasattr (self , '_order' ): # already known
1335+ if check and not self ._order .divides (multiple ):
1336+ raise ValueError (f'previously cached order { self ._order } does not divide given multiple { multiple } ' )
1337+ return
1338+
1339+ from sage .groups .generic import order_from_multiple
1340+ value = order_from_multiple (self , multiple , check = check )
1341+ check = False
1342+
12741343 value = Integer (value )
12751344
12761345 if check :
@@ -1283,6 +1352,9 @@ def set_order(self, value, *, check=True):
12831352 raise ValueError ('Value %s illegal: outside max Hasse bound' % value )
12841353 if value * self != E (0 ):
12851354 raise ValueError ('Value %s illegal: %s * %s is not the identity' % (value , value , self ))
1355+ if hasattr (self , '_order' ) and self ._order != value : # already known
1356+ raise ValueError (f'value { value } contradicts previously cached order { self ._order } ' )
1357+
12861358 self ._order = value
12871359
12881360 # ############################# end ################################
0 commit comments