Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/groups/generic.py
4040 views
1
"""
2
Miscellaneous generic functions
3
4
A collection of functions implementing generic algorithms in arbitrary
5
groups, including additive and multiplicative groups.
6
7
In all cases the group operation is specified by a parameter
8
'operation', which is a string either one of the set of
9
multiplication_names or addition_names specified below, or 'other'.
10
In the latter case, the caller must provide an identity, inverse() and
11
op() functions.
12
13
::
14
15
multiplication_names = ( 'multiplication', 'times', 'product', '*')
16
addition_names = ( 'addition', 'plus', 'sum', '+')
17
18
19
Also included are a generic function for computing multiples (or
20
powers), and an iterator for general multiples and powers.
21
22
EXAMPLES:
23
24
Some examples in the multiplicative group of a finite field:
25
26
- Discrete logs::
27
28
sage: K = GF(3^6,'b')
29
sage: b = K.gen()
30
sage: a = b^210
31
sage: discrete_log(a, b, K.order()-1)
32
210
33
34
- Linear relation finder::
35
36
sage: F.<a>=GF(3^6,'a')
37
sage: a.multiplicative_order().factor()
38
2^3 * 7 * 13
39
sage: b=a^7
40
sage: c=a^13
41
sage: linear_relation(b,c,'*')
42
(13, 7)
43
sage: b^13==c^7
44
True
45
46
- Orders of elements::
47
48
sage: k.<a> = GF(5^5)
49
sage: b = a^4
50
sage: order_from_multiple(b,5^5-1,operation='*')
51
781
52
sage: order_from_bounds(b,(5^4,5^5),operation='*')
53
781
54
55
Some examples in the group of points of an elliptic curve over a finite field:
56
57
- Discrete logs::
58
59
sage: F=GF(37^2,'a')
60
sage: E=EllipticCurve(F,[1,1])
61
sage: F.<a>=GF(37^2,'a')
62
sage: E=EllipticCurve(F,[1,1])
63
sage: P=E(25*a + 16 , 15*a + 7 )
64
sage: P.order()
65
672
66
sage: Q=39*P; Q
67
(36*a + 32 : 5*a + 12 : 1)
68
sage: discrete_log(Q,P,P.order(),operation='+')
69
39
70
71
- Linear relation finder::
72
73
sage: F.<a>=GF(3^6,'a')
74
sage: E=EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
75
sage: P=E(a^5 + a^4 + a^3 + a^2 + a + 2 , 0)
76
sage: Q=E(2*a^3 + 2*a^2 + 2*a , a^3 + 2*a^2 + 1)
77
sage: linear_relation(P,Q,'+')
78
(1, 2)
79
sage: P == 2*Q
80
True
81
82
- Orders of elements::
83
84
sage: k.<a> = GF(5^5)
85
sage: E = EllipticCurve(k,[2,4])
86
sage: P = E(3*a^4 + 3*a , 2*a + 1 )
87
sage: M = E.cardinality(); M
88
3227
89
sage: plist = M.prime_factors()
90
sage: order_from_multiple(P, M, plist, operation='+')
91
3227
92
sage: Q = E(0,2)
93
sage: order_from_multiple(Q, M, plist, operation='+')
94
7
95
sage: order_from_bounds(Q, Hasse_bounds(5^5), operation='+')
96
7
97
98
"""
99
100
###########################################################################
101
# Copyright (C) 2008 William Stein <[email protected]>
102
# John Cremona <[email protected]>
103
#
104
# Distributed under the terms of the GNU General Public License (GPL)
105
# http://www.gnu.org/licenses/
106
###########################################################################
107
108
from copy import copy
109
110
import sage.misc.misc as misc
111
import sage.rings.integer_ring as integer_ring
112
import sage.rings.integer
113
114
#
115
# Lists of names (as strings) which the user may use to identify one
116
# of the standard operations:
117
#
118
multiplication_names = ( 'multiplication', 'times', 'product', '*')
119
addition_names = ( 'addition', 'plus', 'sum', '+')
120
121
from sage.structure.element import generic_power as power
122
123
def multiple(a, n, operation='*', identity=None, inverse=None, op=None):
124
r"""
125
Returns either `na` or `a^n`, where `n` is any integer and `a` is
126
a Python object on which a group operation such as addition or
127
multiplication is defined. Uses the standard binary algorithm.
128
129
INPUT: See the documentation for ``discrete_logarithm()``.
130
131
EXAMPLES::
132
133
sage: multiple(2,5)
134
32
135
sage: multiple(RealField()('2.5'),4)
136
39.0625000000000
137
sage: multiple(2,-3)
138
1/8
139
sage: multiple(2,100,'+') == 100*2
140
True
141
sage: multiple(2,100) == 2**100
142
True
143
sage: multiple(2,-100,) == 2**-100
144
True
145
sage: R.<x>=ZZ[]
146
sage: multiple(x,100)
147
x^100
148
sage: multiple(x,100,'+')
149
100*x
150
sage: multiple(x,-10)
151
1/x^10
152
153
Idempotence is detected, making the following fast::
154
155
sage: multiple(1,10^1000)
156
1
157
158
sage: E=EllipticCurve('389a1')
159
sage: P=E(-1,1)
160
sage: multiple(P,10,'+')
161
(645656132358737542773209599489/22817025904944891235367494656 : 525532176124281192881231818644174845702936831/3446581505217248068297884384990762467229696 : 1)
162
sage: multiple(P,-10,'+')
163
(645656132358737542773209599489/22817025904944891235367494656 : -528978757629498440949529703029165608170166527/3446581505217248068297884384990762467229696 : 1)
164
165
166
"""
167
from operator import inv, mul, neg, add
168
169
if operation in multiplication_names:
170
identity = a.parent()(1)
171
inverse = inv
172
op = mul
173
elif operation in addition_names:
174
identity = a.parent()(0)
175
inverse = neg
176
op = add
177
else:
178
if identity==None or inverse==None or op==None:
179
raise ValueError, "identity, inverse and operation must all be specified"
180
181
if n == 0:
182
return identity
183
184
if n < 0:
185
n = -n
186
a = inverse(a)
187
188
if n == 1:
189
return a
190
191
# check for idempotence, and store the result otherwise
192
aa = op(a,a)
193
if aa == a:
194
return a
195
196
if n == 2:
197
return aa
198
199
if n == 3:
200
return op(aa,a)
201
202
if n == 4:
203
return op(aa,aa)
204
205
# since we've computed a^2, let's start squaring there
206
# so, let's keep the least-significant bit around, just
207
# in case.
208
m = n & 1
209
n = n >> 1
210
211
# One multiplication can be saved by starting with
212
# the second-smallest power needed rather than with 1
213
# we've already squared a, so let's start there.
214
apow = aa
215
while n&1 == 0:
216
apow = op(apow,apow)
217
n = n >> 1
218
power = apow
219
n = n >> 1
220
221
# now multiply that least-significant bit in...
222
if m:
223
power = op(power,a)
224
225
# and this is straight from the book.
226
while n != 0:
227
apow = op(apow,apow)
228
if n&1 != 0:
229
power = op(power,apow)
230
n = n >> 1
231
232
return power
233
234
235
#
236
# Generic iterator for looping through multiples or powers
237
#
238
239
class multiples:
240
r"""
241
Return an iterator which runs through ``P0+i*P`` for ``i`` in ``range(n)``.
242
243
``P`` and ``P0`` must be Sage objects in some group; if the operation is
244
multiplication then the returned values are instead ``P0*P**i``.
245
246
EXAMPLES::
247
248
sage: list(multiples(1,10))
249
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
250
sage: list(multiples(1,10,100))
251
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
252
253
sage: E=EllipticCurve('389a1')
254
sage: P=E(-1,1)
255
sage: for Q in multiples(P,5): print Q, Q.height()/P.height()
256
(0 : 1 : 0) 0.000000000000000
257
(-1 : 1 : 1) 1.00000000000000
258
(10/9 : -35/27 : 1) 4.00000000000000
259
(26/361 : -5720/6859 : 1) 9.00000000000000
260
(47503/16641 : 9862190/2146689 : 1) 16.0000000000000
261
262
sage: R.<x>=ZZ[]
263
sage: list(multiples(x,5))
264
[0, x, 2*x, 3*x, 4*x]
265
sage: list(multiples(x,5,operation='*'))
266
[1, x, x^2, x^3, x^4]
267
sage: list(multiples(x,5,indexed=True))
268
[(0, 0), (1, x), (2, 2*x), (3, 3*x), (4, 4*x)]
269
sage: list(multiples(x,5,indexed=True,operation='*'))
270
[(0, 1), (1, x), (2, x^2), (3, x^3), (4, x^4)]
271
sage: for i,y in multiples(x,5,indexed=True): print "%s times %s = %s"%(i,x,y)
272
0 times x = 0
273
1 times x = x
274
2 times x = 2*x
275
3 times x = 3*x
276
4 times x = 4*x
277
278
sage: for i,n in multiples(3,5,indexed=True,operation='*'): print "3 to the power %s = %s"%(i,n)
279
3 to the power 0 = 1
280
3 to the power 1 = 3
281
3 to the power 2 = 9
282
3 to the power 3 = 27
283
3 to the power 4 = 81
284
"""
285
def __init__(self,P,n,P0=None,indexed=False, operation='+', op=None):
286
"""
287
Create a multiples iterator
288
289
INPUT:
290
291
- ``P`` - step value: any Sage object on which a binary
292
operation is defined
293
- ``n`` - number of multiples: non-negative integer
294
- ``P0`` - offset (default 0): Sage object which can be 'added' to P
295
- ``indexed`` - boolean (default False)
296
297
If ``indexed==False`` then the iterator delivers ``P0+i*P``
298
(if ``operation=='+'``) or ``P0*P**i`` (if
299
``operation=='*'``), for ``i`` in ``range(n)``.
300
301
If ``indexed==True`` then the iterator delivers tuples
302
``(i,P0+i*P)`` or ``(i,P0*P**i)``.
303
304
- ``operation`` - string: '+' (default ) or '*' or `other`.
305
306
If `other`, a function ``op()`` must be supplied (a function
307
of 2 arguments) defining the group binary operation; also
308
``P0`` must be supplied.
309
"""
310
if n<0:
311
raise ValueError, 'n cannot be negative in multiples'
312
313
from operator import mul, add
314
315
if operation in multiplication_names:
316
if P0==None: P0 = P.parent()(1)
317
self.op = mul
318
elif operation in addition_names:
319
if P0==None: P0 = P.parent()(0)
320
self.op = add
321
else:
322
self.op = op
323
if P0==None:
324
raise ValueError, "P0 must be supplied when operation is neither addition nor multiplication"
325
if op==None:
326
raise ValueError, "op() must both be supplied when operation is neither addition nor multiplication"
327
328
self.P=copy(P)
329
self.Q=copy(P0)
330
assert self.P is not None and self.Q is not None
331
self.i = 0
332
self.bound = n
333
self.indexed = indexed
334
335
336
def next(self):
337
"""
338
Returns the next item in this multiples iterator.
339
"""
340
if self.i >= self.bound:
341
raise StopIteration
342
i = self.i
343
val = self.Q
344
self.i +=1
345
self.Q=self.op(self.Q,self.P)
346
if self.indexed:
347
return (i,val)
348
else:
349
return val
350
def __iter__(self):
351
"""
352
Standard member function making this class an iterator.
353
"""
354
return self
355
356
357
def bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None):
358
r"""
359
Totally generic discrete baby-step giant-step function.
360
361
Solves `na=b` (or `a^n=b`) with `lb\le n\le ub` where ``bounds==(lb,ub)``,
362
raising an error if no such `n` exists.
363
364
`a` and `b` must be elements of some group with given identity,
365
inverse of ``x`` given by ``inverse(x)``, and group operation on
366
``x``, ``y`` by ``op(x,y)``.
367
368
If operation is '*' or '+' then the other
369
arguments are provided automatically; otherwise they must be
370
provided by the caller.
371
372
INPUT:
373
374
- ``a`` - group element
375
- ``b`` - group element
376
- ``bounds`` - a 2-tuple of integers ``(lower,upper)`` with ``0<=lower<=upper``
377
- ``operation`` - string: '*', '+', 'other'
378
- ``identity`` - the identity element of the group
379
- ``inverse()`` - function of 1 argument ``x`` returning inverse of ``x``
380
- ``op()`` - function of 2 arguments ``x``, ``y`` returning ``x*y`` in group
381
382
OUTPUT:
383
384
An integer `n` such that `a^n = b` (or `na = b`). If no
385
such `n` exists, this function raises a ValueError exception.
386
387
NOTE: This is a generalization of discrete logarithm. One
388
situation where this version is useful is to find the order of
389
an element in a group where we only have bounds on the group
390
order (see the elliptic curve example below).
391
392
ALGORITHM: Baby step giant step. Time and space are soft
393
`O(\sqrt{n})` where `n` is the difference between upper and lower
394
bounds.
395
396
EXAMPLES::
397
398
sage: b = Mod(2,37); a = b^20
399
sage: bsgs(b, a, (0,36))
400
20
401
402
sage: p=next_prime(10^20)
403
sage: a=Mod(2,p); b=a^(10^25)
404
sage: bsgs(a, b, (10^25-10^6,10^25+10^6)) == 10^25
405
True
406
407
sage: K = GF(3^6,'b')
408
sage: a = K.gen()
409
sage: b = a^210
410
sage: bsgs(a, b, (0,K.order()-1))
411
210
412
413
sage: K.<z>=CyclotomicField(230)
414
sage: w=z^500
415
sage: bsgs(z,w,(0,229))
416
40
417
418
An additive example in an elliptic curve group::
419
420
sage: F.<a>=GF(37^5,'a')
421
sage: E=EllipticCurve(F,[1,1])
422
sage: P=E.lift_x(a); P
423
(a : 9*a^4 + 22*a^3 + 23*a^2 + 30 : 1)
424
425
This will return a multiple of the order of P:
426
sage: bsgs(P,P.parent()(0),Hasse_bounds(F.order()),operation='+')
427
69327408
428
429
AUTHOR:
430
431
- John Cremona (2008-03-15)
432
"""
433
Z = integer_ring.ZZ
434
435
from operator import inv, mul, neg, add
436
437
if operation in multiplication_names:
438
identity = a.parent()(1)
439
inverse = inv
440
op = mul
441
elif operation in addition_names:
442
identity = a.parent()(0)
443
inverse = neg
444
op = add
445
else:
446
if identity==None or inverse==None or op==None:
447
raise ValueError, "identity, inverse and operation must be given"
448
449
lb, ub = bounds
450
if lb<0 or ub<lb:
451
raise ValueError, "bsgs() requires 0<=lb<=ub"
452
453
if a.is_zero() and not b.is_zero():
454
raise ValueError, "No solution in bsgs()"
455
456
ran = 1 + ub - lb # the length of the interval
457
458
c = op(inverse(b),multiple(a,lb,operation=operation))
459
460
if ran < 30: # use simple search for small ranges
461
i = lb
462
d = c
463
# for i,d in multiples(a,ran,c,indexed=True,operation=operation):
464
for i0 in range(ran):
465
i = lb + i0
466
if identity == d: # identity == b^(-1)*a^i, so return i
467
return Z(i)
468
d = op(a,d)
469
raise ValueError, "No solution in bsgs()"
470
471
m = ran.isqrt()+1 # we need sqrt(ran) rounded up
472
table = dict() # will hold pairs (a^(lb+i),lb+i) for i in range(m)
473
474
d=c
475
for i0 in misc.srange(m):
476
i = lb + i0
477
if identity==d: # identity == b^(-1)*a^i, so return i
478
return Z(i)
479
table[d] = i
480
d=op(d,a)
481
482
c = op(c,inverse(d)) # this is now a**(-m)
483
d=identity
484
for i in misc.srange(m):
485
j = table.get(d)
486
if not j==None: # then d == b*a**(-i*m) == a**j
487
return Z(i*m + j)
488
d=op(c,d)
489
490
raise ValueError, "Log of %s to the base %s does not exist in %s."%(b,a,bounds)
491
492
def discrete_log_rho(a, base, ord=None, operation='*', hash_function=hash):
493
"""
494
Pollard Rho algorithm for computing discrete logarithm in cyclic
495
group of prime order.
496
If the group order is very small it falls back to the baby step giant step
497
algorithm.
498
499
INPUT:
500
501
- a - a group element
502
- base - a group element
503
- ord - the order of base or None, in this case we try to compute it
504
- operation - a string (default: '*') wether we are in an
505
additive group or a multiplicative one
506
- hash_function - having an efficient hash function is critical
507
for this algorithm (see examples)
508
509
OUTPUT: return an integer $n$ such that `a=base^n` (or `a=n*base`)
510
511
ALGORITHM: Pollard rho for discrete logarithm, adapted from the article of Edlyn Teske,
512
'A space efficient algorithm for group structure computation'
513
514
EXAMPLES::
515
516
sage: F.<a> = GF(2^13)
517
sage: g = F.gen()
518
sage: discrete_log_rho(g^1234, g)
519
1234
520
521
sage: F.<a> = GF(37^5, 'a')
522
sage: E = EllipticCurve(F, [1,1])
523
sage: G = 3*31*2^4*E.lift_x(a)
524
sage: discrete_log_rho(12345*G, G, ord=46591, operation='+')
525
12345
526
527
It also works with matrices::
528
529
sage: A = matrix(GF(50021),[[10577,23999,28893],[14601,41019,30188],[3081,736,27092]])
530
sage: discrete_log_rho(A^1234567, A)
531
1234567
532
533
Beware, the order must be prime::
534
535
sage: I = IntegerModRing(171980)
536
sage: discrete_log_rho(I(2), I(3))
537
Traceback (most recent call last):
538
...
539
ValueError: for Pollard rho algorithm the order of the group must be prime
540
541
If it fails to find a suitable logarithm, it raises a ``ValueError``::
542
543
sage: I = IntegerModRing(171980)
544
sage: discrete_log_rho(I(31002),I(15501))
545
Traceback (most recent call last):
546
...
547
ValueError: Pollard rho algorithm failed to find a logarithm
548
549
The main limitation on the hash function is that we don't want to have
550
`hash(x*y) = hash(x)+hash(y)`::
551
552
sage: I = IntegerModRing(next_prime(2^23))
553
sage: def test():
554
... try:
555
... discrete_log_rho(I(123456),I(1),operation='+')
556
... except:
557
... print "FAILURE"
558
sage: test() # random failure
559
FAILURE
560
561
If this happens, we can provide a better hash function::
562
563
sage: discrete_log_rho(I(123456),I(1),operation='+', hash_function=lambda x: hash(x*x))
564
123456
565
566
AUTHOR:
567
568
- Yann Laigle-Chapuy (2009-09-05)
569
570
"""
571
from sage.rings.integer import Integer
572
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
573
from operator import mul, add, pow
574
575
# should be reasonable choices
576
partition_size=20
577
memory_size=4
578
579
if operation in addition_names:
580
mult=add
581
power=mul
582
if ord==None:
583
ord=base.additive_order()
584
elif operation in multiplication_names:
585
mult=mul
586
power=pow
587
if ord==None:
588
ord=base.multiplicative_order()
589
else:
590
raise(ValueError, "unknown operation")
591
592
ord = Integer(ord)
593
594
if not ord.is_prime():
595
raise ValueError,"for Pollard rho algorithm the order of the group must be prime"
596
597
# check if we need to set immutable before hashing
598
mut = hasattr(base,'set_immutable')
599
600
isqrtord=ord.isqrt()
601
602
if isqrtord < partition_size: #setup to costly, use bsgs
603
return bsgs(base,a, bounds=(0,ord), operation=operation)
604
605
reset_bound = 8*isqrtord # we take some margin
606
607
I=IntegerModRing(ord)
608
609
for s in xrange(10): # to avoid infinite loops
610
# random walk function setup
611
m=[I.random_element() for i in xrange(partition_size)]
612
n=[I.random_element() for i in xrange(partition_size)]
613
M=[mult(power(base,Integer(m[i])),power(a,Integer(n[i]))) for i in xrange(partition_size)]
614
615
ax = I.random_element()
616
x = power(base,Integer(ax))
617
if mut:
618
x.set_immutable()
619
620
bx = I(0)
621
622
sigma=[(0,None)]*memory_size
623
H={} # memory
624
i0=0
625
nextsigma = 0
626
for i in xrange(reset_bound):
627
#random walk, we need an efficient hash
628
s=hash_function(x) % partition_size
629
(x,ax,bx) = (mult(M[s],x), ax+m[s], bx+n[s])
630
if mut:
631
x.set_immutable()
632
# look for collisions
633
if x in H:
634
ay,by=H[x]
635
if bx == by:
636
break
637
else:
638
res = sage.rings.integer.Integer((ay-ax)/(bx-by))
639
if power(base,res) == a:
640
return res
641
else:
642
break
643
# should we remember this value?
644
elif i >= nextsigma:
645
if sigma[i0][1] is not None:
646
H.pop(sigma[i0][1])
647
sigma[i0]=(i,x)
648
i0 = (i0+1) % memory_size
649
nextsigma = 3*sigma[i0][0] #3 seems a good choice
650
H[x]=(ax,bx)
651
652
raise ValueError, "Pollard rho algorithm failed to find a logarithm"
653
654
655
def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None):
656
r"""
657
Totally generic discrete log function.
658
659
INPUT:
660
661
- ``a`` - group element
662
- ``base`` - group element (the base)
663
- ``ord`` - integer (multiple of order of base, or ``None``)
664
- ``bounds`` - a priori bounds on the log
665
- ``operation`` - string: '*', '+', 'other'
666
- ``identity`` - the group's identity
667
- ``inverse()`` - function of 1 argument ``x`` returning inverse of ``x``
668
- ``op()`` - function of 2 arguments ``x``, ``y`` returning ``x*y`` in group
669
670
``a`` and ``base`` must be elements of some group with identity
671
given by identity, inverse of ``x`` by ``inverse(x)``, and group
672
operation on ``x``, ``y`` by ``op(x,y)``.
673
674
If operation is '*' or '+' then the other
675
arguments are provided automatically; otherwise they must be
676
provided by the caller.
677
678
OUTPUT: Returns an integer `n` such that `b^n = a` (or `nb = a`),
679
assuming that ``ord`` is a multiple of the order of the base `b`.
680
If ``ord`` is not specified, an attempt is made to compute it.
681
682
If no such `n` exists, this function raises a ValueError exception.
683
684
.. warning::
685
686
If ``x`` has a log method, it is likely to be vastly faster
687
than using this function. E.g., if ``x`` is an integer modulo
688
`n`, use its log method instead!
689
690
ALGORITHM: Pohlig-Hellman and Baby step giant step.
691
692
EXAMPLES::
693
694
sage: b = Mod(2,37); a = b^20
695
sage: discrete_log(a, b)
696
20
697
sage: b = Mod(2,997); a = b^20
698
sage: discrete_log(a, b)
699
20
700
701
sage: K = GF(3^6,'b')
702
sage: b = K.gen()
703
sage: a = b^210
704
sage: discrete_log(a, b, K.order()-1)
705
210
706
707
sage: b = Mod(1,37); x = Mod(2,37)
708
sage: discrete_log(x, b)
709
Traceback (most recent call last):
710
...
711
ValueError: No discrete log of 2 found to base 1
712
sage: b = Mod(1,997); x = Mod(2,997)
713
sage: discrete_log(x, b)
714
Traceback (most recent call last):
715
...
716
ValueError: No discrete log of 2 found to base 1
717
718
See trac\#2356:
719
sage: F.<w> = GF(121)
720
sage: v = w^120
721
sage: v.log(w)
722
0
723
724
sage: K.<z>=CyclotomicField(230)
725
sage: w=z^50
726
sage: discrete_log(w,z)
727
50
728
729
An example where the order is infinite: note that we must give
730
an upper bound here::
731
732
sage: K.<a> = QuadraticField(23)
733
sage: eps = 5*a-24 # a fundamental unit
734
sage: eps.multiplicative_order()
735
+Infinity
736
sage: eta = eps^100
737
sage: discrete_log(eta,eps,bounds=(0,1000))
738
100
739
740
In this case we cannot detect negative powers::
741
742
sage: eta = eps^(-3)
743
sage: discrete_log(eta,eps,bounds=(0,100))
744
Traceback (most recent call last):
745
...
746
ValueError: No discrete log of -11515*a - 55224 found to base 5*a - 24
747
748
But we can invert the base (and negate the result) instead::
749
750
sage: - discrete_log(eta^-1,eps,bounds=(0,100))
751
-3
752
753
An additive example: elliptic curve DLOG::
754
755
sage: F=GF(37^2,'a')
756
sage: E=EllipticCurve(F,[1,1])
757
sage: F.<a>=GF(37^2,'a')
758
sage: E=EllipticCurve(F,[1,1])
759
sage: P=E(25*a + 16 , 15*a + 7 )
760
sage: P.order()
761
672
762
sage: Q=39*P; Q
763
(36*a + 32 : 5*a + 12 : 1)
764
sage: discrete_log(Q,P,P.order(),operation='+')
765
39
766
767
An example of big smooth group::
768
769
sage: F.<a>=GF(2^63)
770
sage: g=F.gen()
771
sage: u=g**123456789
772
sage: discrete_log(u,g)
773
123456789
774
775
AUTHORS:
776
777
- William Stein and David Joyner (2005-01-05)
778
- John Cremona (2008-02-29) rewrite using ``dict()`` and make generic
779
780
"""
781
if ord == None:
782
if operation in multiplication_names:
783
try:
784
ord = base.multiplicative_order()
785
except:
786
ord = base.order()
787
elif operation in addition_names:
788
try:
789
ord = base.additive_order()
790
except:
791
ord = base.order()
792
else:
793
try:
794
ord = base.order()
795
except:
796
raise ValueError, "ord must be specified"
797
try:
798
from sage.rings.infinity import Infinity
799
if ord==+Infinity:
800
return bsgs(base,a,bounds, operation=operation)
801
if ord==1 and a!=base:
802
raise ValueError
803
f=ord.factor()
804
l=[0]*len(f)
805
for i,(pi,ri) in enumerate(f):
806
for j in range(ri):
807
if operation in multiplication_names:
808
c=bsgs(base**(ord//pi),(a/base**l[i])**(ord//pi**(j+1)),(0,pi),operation=operation)
809
l[i] += c*(pi**j)
810
elif operation in addition_names:
811
c=bsgs(base*(ord//pi),(a-base*l[i])*(ord//pi**(j+1)),(0,pi),operation=operation)
812
l[i] += c*(pi**j)
813
from sage.rings.arith import CRT_list
814
return CRT_list(l,[pi**ri for pi,ri in f])
815
except ValueError:
816
raise ValueError, "No discrete log of %s found to base %s"%(a,base)
817
818
def discrete_log_generic(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None):
819
"""
820
Alias for ``discrete_log``.
821
"""
822
return discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None)
823
824
def discrete_log_lambda(a, base, bounds, operation='*', hash_function=hash):
825
"""
826
Pollard Lambda algorithm for computing discrete logarithms. It uses
827
only a logarithmic amount of memory. It's useful if you have
828
bounds on the logarithm. If you are computing logarithms in a
829
whole finite group, you should use Pollard Rho algorithm.
830
INPUT:
831
832
- a - a group element
833
- base - a group element
834
- bounds - a couple (lb,ub) representing the range where we look for a logarithm
835
- operation - string: '+', '*' or 'other'
836
- hash_function -- having an efficient hash function is critical for this algorithm
837
838
OUTPUT: Returns an integer `n` such that `a=base^n` (or `a=n*base`)
839
840
ALGORITHM: Pollard Lambda, if bounds are (lb,ub) it has time complexity
841
O(sqrt(ub-lb)) and space complexity O(log(ub-lb))
842
843
EXEMPLES::
844
845
sage: F.<a> = GF(2^63)
846
sage: discrete_log_lambda(a^1234567, a, (1200000,1250000))
847
1234567
848
849
sage: F.<a> = GF(37^5, 'a')
850
sage: E = EllipticCurve(F, [1,1])
851
sage: P=E.lift_x(a); P
852
(a : 9*a^4 + 22*a^3 + 23*a^2 + 30 : 1)
853
854
This will return a multiple of the order of P::
855
856
sage: discrete_log_lambda(P.parent()(0), P, Hasse_bounds(F.order()), operation='+')
857
69327408
858
859
sage: K.<a> = GF(89**5)
860
sage: hs = lambda x: hash(x) + 15
861
sage: discrete_log_lambda(a**(89**3 - 3), a, (89**2, 89**4), operation = '*', hash_function = hs) # long time (10s on sage.math, 2011)
862
704966
863
864
AUTHOR:
865
866
-- Yann Laigle-Chapuy (2009-01-25)
867
868
"""
869
from sage.rings.integer import Integer
870
from operator import mul, add, pow
871
872
if operation in addition_names:
873
mult=add
874
power=mul
875
elif operation in multiplication_names:
876
mult=mul
877
power=pow
878
else:
879
raise(ValueError, "unknown operation")
880
881
lb,ub = bounds
882
if lb<0 or ub<lb:
883
raise ValueError, "discrete_log_lambda() requires 0<=lb<=ub"
884
885
# check for mutability
886
mut = hasattr(base,'set_immutable')
887
888
width = Integer(ub-lb)
889
N = width.isqrt()+1
890
891
M = dict()
892
for s in xrange(10): #to avoid infinite loops
893
#random walk function setup
894
k = 0
895
while (2**k<N):
896
r = sage.misc.prandom.randrange(1,N)
897
M[k] = (r , power(base,r))
898
k += 1
899
#first random walk
900
H = power(base,ub)
901
c = ub
902
for i in xrange(N):
903
if mut: H.set_immutable()
904
r,e = M[hash_function(H)%k]
905
H = mult(H,e)
906
c += r
907
if mut: H.set_immutable()
908
mem=set([H])
909
#second random walk
910
H = a
911
d=0
912
while c-d >= lb:
913
if mut: H.set_immutable()
914
if ub > c-d and H in mem:
915
return c-d
916
r,e = M[hash_function(H)%k]
917
H = mult(H,e)
918
d += r
919
920
raise ValueError, "Pollard Lambda failed to find a log"
921
922
################################################################
923
#
924
# Older version of discrete_log. Works fine but has been
925
# superceded by the version which simply calls the more general
926
# bsgs() function.
927
#
928
################################################################
929
930
# def old_discrete_log(a, base, ord=None, operation='*',
931
# identity=None, inverse=None, op=None):
932
# r"""
933
# Totally generic discrete log function.
934
935
# a and base must be elements of some group with identity given by
936
# identity, inverse of x by inverse(x), and group operation on x,y
937
# by op(x,y).
938
939
# If operation is '*' or '+' then the other
940
# arguments are provided automatically; otherwise they must be
941
# provided by the caller.
942
943
# WARNING: If x has a log method, it is likely to be vastly faster
944
# than using this function. E.g., if x is an integer modulo n, use
945
# its log method instead!
946
947
# INPUT:
948
# a -- group element
949
# base -- group element (the base)
950
# ord -- integer (multiple of order of base, or None)
951
# operation -- string: '*', '+', 'other'
952
# identity -- the group's identity
953
# inverse() -- function of 1 argument x returning inverse of x
954
# op() -- function of 2 arguments x,y returning x*y in group
955
956
# OUTPUT:
957
# Returns an integer $n$ such that $b^n = a$ (or $n*b = a$),
958
# assuming that ord is a multiple of the order of the base $b$.
959
# If ord is not specified an attempt is made to compute it.
960
961
# If no such $n$ exists, this function raises a ValueError exception.
962
963
# ALGORITHM: Baby step giant step.
964
965
# EXAMPLES:
966
# sage: b = Mod(2,37); a = b^20
967
# sage: old_discrete_log(a, b)
968
# 20
969
# sage: b = Mod(2,997); a = b^20
970
# sage: old_discrete_log(a, b)
971
# 20
972
973
# sage: K = GF(3^6,'b')
974
# sage: b = K.gen()
975
# sage: a = b^210
976
# sage: old_discrete_log(a, b, K.order()-1)
977
# 210
978
979
# sage: b = Mod(1,37); x = Mod(2,37)
980
# sage: old_discrete_log(x, b)
981
# Traceback (most recent call last):
982
# ...
983
# ValueError: Log of 2 to the base 1 does not exist.
984
# sage: b = Mod(1,997); x = Mod(2,997)
985
# sage: old_discrete_log(x, b)
986
# Traceback (most recent call last):
987
# ...
988
# ValueError: Log of 2 to the base 1 does not exist.
989
990
# See trac\#2356:
991
# sage: F.<w> = GF(121)
992
# sage: v = w^120
993
# sage: v.log(w)
994
# 0
995
996
# sage: K.<z>=CyclotomicField(230)
997
# sage: w=z^50
998
# sage: old_discrete_log(w,z)
999
# 50
1000
1001
# An example where the order is infinite: note that we must give
1002
# an upper bound here:
1003
# sage: K.<a> = QuadraticField(23)
1004
# sage: eps = 5*a-24 # a fundamental unit
1005
# sage: eps.multiplicative_order()
1006
# +Infinity
1007
# sage: eta = eps^100
1008
# sage: old_discrete_log(eta,eps,1000)
1009
# 100
1010
1011
# In this case we cannot detect negative powers:
1012
# sage: eta = eps^(-3)
1013
# sage: old_discrete_log(eta,eps,100)
1014
# Traceback (most recent call last):
1015
# ...
1016
# ValueError: Log of -11515*a - 55224 to the base 5*a - 24 does not exist.
1017
1018
# But we can invert the base (and negate the result) instead:
1019
# sage: - old_discrete_log(eta^-1,eps,100)
1020
# -3
1021
1022
# An additive example: elliptic curve DLOG:
1023
# sage: F=GF(37^2,'a')
1024
# sage: E=EllipticCurve(F,[1,1])
1025
# sage: F.<a>=GF(37^2,'a')
1026
# sage: E=EllipticCurve(F,[1,1])
1027
# sage: P=E(25*a + 16 , 15*a + 7 )
1028
# sage: P.order()
1029
# 672
1030
# sage: Q=39*P; Q
1031
# (36*a + 32 : 5*a + 12 : 1)
1032
# sage: old_discrete_log(Q,P,P.order(),'+')
1033
# 39
1034
1035
# AUTHOR:
1036
# -- William Stein and David Joyner (2005-01-05)
1037
# -- John Cremona (2008-02-29) rewrite using dict() and make generic
1038
# """
1039
# Z = integer_ring.ZZ
1040
# b = base
1041
1042
# from operator import inv, mul, neg, add
1043
1044
# if operation in multiplication_names:
1045
# identity = b.parent()(1)
1046
# inverse = inv
1047
# op = mul
1048
# if ord==None:
1049
# ord = b.multiplicative_order()
1050
# elif operation in addition_names:
1051
# identity = b.parent()(0)
1052
# inverse = neg
1053
# op = add
1054
# if ord==None:
1055
# ord = b.order()
1056
# else:
1057
# if ord==None or identity==None or inverse==None or op==None:
1058
# raise ValueError, "order, identity, inverse and operation must all be specified"
1059
1060
# ord = Z(ord)
1061
# if ord < 100:
1062
# c = identity
1063
# for i in range(ord):
1064
# if c == a: # is b^i
1065
# return Z(i)
1066
# c = op(c,b)
1067
# raise ValueError, "Log of %s to the base %s does not exist."%(a,b)
1068
1069
# m = ord.isqrt()+1 # we need sqrt(ord) rounded up
1070
# table = dict() # will hold pairs (b^j,j) for j in range(m)
1071
# g = identity # will run through b**j
1072
# for j in range(m):
1073
# if a==g:
1074
# return Z(j)
1075
# table[g] = j
1076
# g = op(g,b)
1077
1078
# g = inverse(g) # this is now b**(-m)
1079
# h = op(a,g) # will run through a*g**i = a*b**(-i*m)
1080
# for i in range(1,m):
1081
# j = table.get(h)
1082
# if not j==None: # then a*b**(-i*m) == b**j
1083
# return Z(i*m + j)
1084
# if i < m-1:
1085
# h = op(h,g)
1086
1087
# raise ValueError, "Log of %s to the base %s does not exist."%(a,b)
1088
1089
1090
################################################################
1091
#
1092
# Generic linear relation finder
1093
#
1094
################################################################
1095
1096
def linear_relation(P, Q, operation='+', identity=None, inverse=None, op=None):
1097
r"""
1098
Function which solves the equation ``a*P=m*Q`` or ``P^a=Q^m``.
1099
1100
Additive version: returns `(a,m)` with minimal `m>0` such that
1101
`aP=mQ`. Special case: if `\left<P\right>` and `\left<Q\right>`
1102
intersect only in `\{0\}` then `(a,m)=(0,n)` where `n` is
1103
``Q.additive_order()``.
1104
1105
Multiplicative version: returns `(a,m)` with minimal `m>0` such
1106
that `P^a=Q^m`. Special case: if `\left<P\right>` and
1107
`\left<Q\right>` intersect only in `\{1\}` then `(a,m)=(0,n)`
1108
where `n` is ``Q.multiplicative_order()``.
1109
1110
ALGORITHM:
1111
1112
Uses the generic ``bsgs()`` function, and so works in general
1113
finite abelian groups.
1114
1115
EXAMPLES:
1116
1117
An additive example (in an elliptic curve group)::
1118
1119
sage: F.<a>=GF(3^6,'a')
1120
sage: E=EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
1121
sage: P=E(a^5 + a^4 + a^3 + a^2 + a + 2 , 0)
1122
sage: Q=E(2*a^3 + 2*a^2 + 2*a , a^3 + 2*a^2 + 1)
1123
sage: linear_relation(P,Q,'+')
1124
(1, 2)
1125
sage: P == 2*Q
1126
True
1127
1128
A multiplicative example (in a finite field's multiplicative group)::
1129
1130
sage: F.<a>=GF(3^6,'a')
1131
sage: a.multiplicative_order().factor()
1132
2^3 * 7 * 13
1133
sage: b=a^7
1134
sage: c=a^13
1135
sage: linear_relation(b,c,'*')
1136
(13, 7)
1137
sage: b^13==c^7
1138
True
1139
"""
1140
1141
from operator import mul, add
1142
Z = integer_ring.ZZ
1143
1144
if operation in multiplication_names:
1145
op = mul
1146
try:
1147
n = P.multiplicative_order()
1148
m = Q.multiplicative_order()
1149
except:
1150
n = P.order()
1151
m = Q.order()
1152
elif operation in addition_names:
1153
op = add
1154
try:
1155
n = P.additive_order()
1156
m = Q.additive_order()
1157
except:
1158
n = P.order()
1159
m = Q.order()
1160
else:
1161
if op==None:
1162
raise ValueError, "operation must be specified"
1163
n = P.order()
1164
m = Q.order()
1165
1166
g = sage.rings.arith.gcd(n,m)
1167
if g==1: return (m,Z(0))
1168
n1 = n//g
1169
m1 = m//g
1170
P1 = multiple(P,n1,operation=operation) # has exact order g
1171
Q1 = multiple(Q,m1,operation=operation) # has exact order g
1172
1173
# now see if Q1 is a multiple of P1; the only multiples we
1174
# need check are h*Q1 where h divides g
1175
for h in g.divisors(): # positive divisors!
1176
try:
1177
Q2 = multiple(Q1,h,operation=operation)
1178
return (n1 * bsgs(P1,Q2,(0,g-1),operation=operation),
1179
m1 * h)
1180
except ValueError:
1181
pass # to next h
1182
raise ValueError, "No solution found in linear_relation!"
1183
1184
################################################################
1185
#
1186
# Generic functions to find orders of elements
1187
#
1188
# 1. order_from_multiple: finds the order given a multiple of the order
1189
#
1190
# 2. order_from_bounds: finds the order given an interval containing a
1191
# multiple of the order
1192
#
1193
################################################################
1194
1195
def order_from_multiple(P, m, plist=None, factorization=None, check=True,
1196
operation='+'):
1197
r"""
1198
Generic function to find order of a group element given a multiple
1199
of its order.
1200
1201
INPUT:
1202
1203
- ``P`` - a Sage object which is a group element;
1204
- ``m`` - a Sage integer which is a multiple of the order of ``P``,
1205
i.e. we require that ``m*P=0`` (or ``P**m=1``);
1206
- ``check`` - a Boolean (default:True), indicating whether we check if ``m``
1207
really is a multiple of the order;
1208
- ``factorization`` - the factorization of ``m``, or ``None`` in which
1209
case this function will need to factor ``m``;
1210
- ``plist`` - a list of the prime factors of ``m``, or ``None`` - kept for compatibility only,
1211
prefer the use of ``factorization``;
1212
- ``operation`` - string: '+' (default) or '*'.
1213
1214
.. note::
1215
1216
It is more efficient for the caller to factor ``m`` and cache
1217
the factors for subsequent calls.
1218
1219
EXAMPLES::
1220
1221
sage: k.<a> = GF(5^5)
1222
sage: b = a^4
1223
sage: order_from_multiple(b,5^5-1,operation='*')
1224
781
1225
sage: E = EllipticCurve(k,[2,4])
1226
sage: P = E(3*a^4 + 3*a , 2*a + 1 )
1227
sage: M = E.cardinality(); M
1228
3227
1229
sage: F = M.factor()
1230
sage: order_from_multiple(P, M, factorization=F, operation='+')
1231
3227
1232
sage: Q = E(0,2)
1233
sage: order_from_multiple(Q, M, factorization=F, operation='+')
1234
7
1235
1236
sage: K.<z>=CyclotomicField(230)
1237
sage: w=z^50
1238
sage: order_from_multiple(w,230,operation='*')
1239
23
1240
1241
sage: F=GF(2^1279,'a')
1242
sage: n=F.cardinality()-1 # Mersenne prime
1243
sage: order_from_multiple(F.random_element(),n,factorization=[(n,1)],operation='*')==n
1244
True
1245
1246
sage: K.<a> = GF(3^60)
1247
sage: order_from_multiple(a, 3^60-1, operation='*', check=False)
1248
42391158275216203514294433200
1249
"""
1250
from operator import mul, add
1251
Z = integer_ring.ZZ
1252
1253
if operation in multiplication_names:
1254
identity = P.parent()(1)
1255
elif operation in addition_names:
1256
identity = P.parent()(0)
1257
else:
1258
raise ValueError, "unknown group operation"
1259
1260
if P == identity:
1261
return Z(1)
1262
1263
M=Z(m)
1264
if check:
1265
assert multiple(P,M,operation=operation) == identity
1266
1267
if factorization:
1268
F = factorization
1269
elif plist:
1270
F = [(p,M.valuation(p)) for p in plist]
1271
else:
1272
F = M.factor()
1273
1274
if len(F) == 1 and list(F) == [(M,1)]:
1275
return M
1276
1277
# Efficiency improvement (2009-10-27, implemented by Yann Laigle-Chapuy):
1278
# we use an internal recursive function to avoid unnecessary computations.
1279
def _order_from_multiple_helper(Q, L, S):
1280
"""
1281
internal use, to minimize the number of group operations.
1282
"""
1283
l = len(L)
1284
if l == 1:
1285
# we determine the power of p dividing the order,
1286
1287
# Efficiency improvement (2009-04-01, suggested by Ryan Hinton,
1288
# implemented by John Cremona): avoid the last multiplication by p.
1289
# For example, if M itself is prime the code used to compute M*P
1290
# twice (unless P=0), now it does it once.
1291
p,e = L[0]
1292
e0 = 0
1293
while (Q != identity) and (e0<e-1):
1294
Q = multiple(Q,p,operation=operation)
1295
e0 += 1
1296
if (Q != identity):
1297
e0 += 1
1298
return p**e0
1299
else:
1300
# try to split the list wisely
1301
sum_left = 0
1302
i = 0
1303
for k in range(l):
1304
p,e = L[k]
1305
# multiplying by p**e require roughly 'e log_2(p) / 2' additions
1306
v = e * sage.functions.log.log(float(p))
1307
if abs(sum_left + v - (S / 2)) > abs(sum_left - (S / 2)):
1308
break
1309
sum_left += v
1310
L1 = L[:k]
1311
L2 = L[k:]
1312
# recursive calls
1313
o1 = _order_from_multiple_helper(
1314
multiple(Q, sage.misc.misc.prod([p**e for p,e in L2]), operation),
1315
L1,
1316
sum_left)
1317
o2 = _order_from_multiple_helper(
1318
multiple(Q, o1 , operation),
1319
L2,
1320
S-sum_left)
1321
return o1*o2
1322
1323
return _order_from_multiple_helper(P, F, sage.functions.log.log(float(M)) )
1324
1325
1326
1327
1328
def order_from_bounds(P, bounds, d=None, operation='+',
1329
identity=None, inverse=None, op=None):
1330
r"""
1331
Generic function to find order of a group element, given only
1332
upper and lower bounds for a multiple of the order (e.g. bounds on
1333
the order of the group of which it is an element)
1334
1335
INPUT:
1336
1337
- ``P`` - a Sage object which is a group element
1338
1339
- ``bounds`` - a 2-tuple ``(lb,ub)`` such that ``m*P=0`` (or
1340
``P**m=1``) for some ``m`` with ``lb<=m<=ub``.
1341
1342
- ``d`` - (optional) a positive integer; only ``m`` which are
1343
multiples of this will be considered.
1344
1345
- ``operation`` - string: '+' (default ) or '*' or other.
1346
If other, the following must be supplied:
1347
1348
- ``identity``: the identity element for the group;
1349
- ``inverse()``: a function of one argument giving the inverse
1350
of a group element;
1351
- ``op()``: a function of 2 arguments defining the group binary
1352
operation.
1353
1354
1355
.. note::
1356
1357
Typically ``lb`` and ``ub`` will be bounds on the group order,
1358
and from previous calculation we know that the group order is
1359
divisible by ``d``.
1360
1361
EXAMPLES::
1362
1363
sage: k.<a> = GF(5^5)
1364
sage: b = a^4
1365
sage: order_from_bounds(b,(5^4,5^5),operation='*')
1366
781
1367
sage: E = EllipticCurve(k,[2,4])
1368
sage: P = E(3*a^4 + 3*a , 2*a + 1 )
1369
sage: bounds = Hasse_bounds(5^5)
1370
sage: Q = E(0,2)
1371
sage: order_from_bounds(Q, bounds, operation='+')
1372
7
1373
sage: order_from_bounds(P, bounds, 7, operation='+')
1374
3227
1375
1376
sage: K.<z>=CyclotomicField(230)
1377
sage: w=z^50
1378
sage: order_from_bounds(w,(200,250),operation='*')
1379
23
1380
1381
"""
1382
from operator import mul, add
1383
Z = integer_ring.ZZ
1384
1385
if operation in multiplication_names:
1386
op = mul
1387
identity = P.parent()(1)
1388
elif operation in addition_names:
1389
op = add
1390
identity = P.parent()(0)
1391
else:
1392
if op==None:
1393
raise ValueError, "operation and identity must be specified"
1394
1395
Q = P
1396
if d == None: d = 1
1397
if d > 1:
1398
Q = multiple(P,d,operation=operation)
1399
lb, ub = bounds
1400
bounds = ( sage.rings.arith.integer_ceil(lb/d),
1401
sage.rings.arith.integer_floor(ub/d) )
1402
1403
# Use generic bsgs to find n=d*m with lb<=n<=ub and n*P=0
1404
1405
m = d * bsgs(Q, identity, bounds, operation=operation)
1406
1407
# Now use the order_from_multiple() function to finish the job:
1408
1409
return order_from_multiple(P, m, operation=operation, check=False)
1410
1411
def merge_points(P1,P2, operation='+',
1412
identity=None, inverse=None, op=None, check=True):
1413
r"""
1414
Returns a group element whose order is the lcm of the given elements.
1415
1416
INPUT:
1417
1418
- ``P1`` -- a pair `(g_1,n_1)` where `g_1` is a group element of order `n_1`
1419
- ``P2`` -- a pair `(g_2,n_2)` where `g_2` is a group element of order `n_2`
1420
- ``operation`` -- string: '+' (default ) or '*' or other. If
1421
other, the following must be supplied:
1422
1423
- ``identity``: the identity element for the group;
1424
- ``inverse()``: a function of one argument giving the inverse
1425
of a group element;
1426
- ``op()``: a function of 2 arguments defining the group
1427
binary operation.
1428
1429
1430
OUTPUT:
1431
1432
A pair `(g_3,n_3)` where `g_3` has order `n_3=\hbox{lcm}(n_1,n_2)`.
1433
1434
EXAMPLES::
1435
1436
sage: F.<a>=GF(3^6,'a')
1437
sage: b = a^7
1438
sage: c = a^13
1439
sage: ob = (3^6-1)//7
1440
sage: oc = (3^6-1)//13
1441
sage: merge_points((b,ob),(c,oc),operation='*')
1442
(a^4 + 2*a^3 + 2*a^2, 728)
1443
sage: d,od = merge_points((b,ob),(c,oc),operation='*')
1444
sage: od == d.multiplicative_order()
1445
True
1446
sage: od == lcm(ob,oc)
1447
True
1448
1449
sage: E=EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
1450
sage: P=E(2*a^5 + 2*a^4 + a^3 + 2 , a^4 + a^3 + a^2 + 2*a + 2)
1451
sage: P.order()
1452
7
1453
sage: Q=E(2*a^5 + 2*a^4 + 1 , a^5 + 2*a^3 + 2*a + 2 )
1454
sage: Q.order()
1455
4
1456
sage: R,m = merge_points((P,7),(Q,4), operation='+')
1457
sage: R.order() == m
1458
True
1459
sage: m == lcm(7,4)
1460
True
1461
"""
1462
from operator import mul, add
1463
Z = integer_ring.ZZ
1464
1465
g1, n1 = P1
1466
g2, n2 = P2
1467
1468
if operation in multiplication_names:
1469
op = mul
1470
identity = g1.parent()(1)
1471
elif operation in addition_names:
1472
op = add
1473
identity = g1.parent()(0)
1474
else:
1475
if op==None:
1476
raise ValueError, "operation and identity must be specified"
1477
1478
if check:
1479
assert multiple(g1,n1,operation=operation) == identity
1480
assert multiple(g2,n2,operation=operation) == identity
1481
1482
# trivial cases
1483
if n1.divides(n2):
1484
return (g2,n2)
1485
if n2.divides(n1):
1486
return (g1,n1)
1487
1488
m,k1,k2 = sage.rings.arith.xlcm(n1,n2);
1489
m1 = n1//k1
1490
m2 = n2//k2
1491
g1 = multiple(g1,m1,operation=operation)
1492
g2 = multiple(g2,m2,operation=operation)
1493
return (op(g1,g2), m)
1494
1495
1496