-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtonelli_shanks_algorithm.sf
46 lines (41 loc) · 1.13 KB
/
tonelli_shanks_algorithm.sf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/ruby
#
## https://rosettacode.org/wiki/Tonelli-Shanks_algorithm
#
func tonelli(n, p) {
legendre(n, p) == 1 || die "not a square (mod p)"
var q = p-1
var s = valuation(q, 2)
s == 1 ? return(powmod(n, (p + 1) >> 2, p)) : (q >>= s)
var c = powmod(2 ..^ p -> first {|z| legendre(z, p) == -1 }, q, p)
var r = powmod(n, (q + 1) >> 1, p)
var t = powmod(n, q, p)
var m = s
var t2 = 0
while (!p.divides(t - 1)) {
t2 = ((t * t) % p)
var b
for i in (1 ..^ m) {
if (p.divides(t2 - 1)) {
b = powmod(c, 1 << (m - i - 1), p)
m = i
break
}
t2 = ((t2 * t2) % p)
}
r = ((r * b) % p)
c = ((b * b) % p)
t = ((t * c) % p)
}
return r
}
var tests = [
[10, 13], [56, 101], [1030, 10009], [44402, 100049],
[665820697, 1000000009], [881398088036, 1000000000039],
[41660815127637347468140745042827704103445750172002, 10**50 + 577],
]
for n,p in tests {
var r = tonelli(n, p)
assert((r*r - n) % p == 0)
say "Roots of #{n} are (#{r}, #{p-r}) mod #{p}"
}