Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
|
|
@ -0,0 +1,113 @@
|
|||
-module(tonelli_shanks).
|
||||
-export([tonelli/2, legendre/2, task/1, main/1]).
|
||||
|
||||
%% Public API
|
||||
tonelli(N, P) ->
|
||||
case legendre(N, P) of
|
||||
1 ->
|
||||
{Q, S} = decompose(P - 1, 0),
|
||||
if
|
||||
S =:= 1 ->
|
||||
mod_pow(N, (P + 1) div 4, P);
|
||||
S > 1 ->
|
||||
Z = find_non_square(P),
|
||||
C = mod_pow(Z, Q, P),
|
||||
R = mod_pow(N, (Q + 1) div 2, P),
|
||||
T = mod_pow(N, Q, P),
|
||||
tonelli_loop(P, C, R, T, S)
|
||||
end;
|
||||
_ ->
|
||||
error({not_a_square_mod_p, N, P})
|
||||
end.
|
||||
|
||||
%% Tonelli-Shanks loop
|
||||
tonelli_loop(P, _C, R, T, _M) when T =:= 1 ->
|
||||
R;
|
||||
tonelli_loop(P, C, R, T, M) ->
|
||||
{T2, I} = find_least_i(T, 1, M, P),
|
||||
B = mod_pow(C, mod_pow(2, M - I - 1, P - 1), P),
|
||||
C1 = mod_mul(B, B, P),
|
||||
R1 = mod_mul(R, B, P),
|
||||
T1 = mod_mul(T, C1, P),
|
||||
tonelli_loop(P, C1, R1, T1, I).
|
||||
|
||||
%% Find smallest i such that t^(2^i) ≡ 1 mod p
|
||||
find_least_i(T, I, M, P) when I < M ->
|
||||
T2 = mod_mul(T, T, P),
|
||||
if
|
||||
T2 =:= 1 -> {T2, I};
|
||||
true -> find_least_i(T2, I + 1, M, P)
|
||||
end;
|
||||
find_least_i(_, _, _, _) ->
|
||||
error(no_i_found).
|
||||
|
||||
%% Legendre symbol: a^((p-1)/2) mod p
|
||||
legendre(A, P) ->
|
||||
mod_pow(A, (P - 1) div 2, P).
|
||||
|
||||
%% Decompose p-1 = q * 2^s where q is odd
|
||||
decompose(Q, S) when Q rem 2 =:= 0 ->
|
||||
decompose(Q div 2, S + 1);
|
||||
decompose(Q, S) ->
|
||||
{Q, S}.
|
||||
|
||||
%% Find a quadratic non-residue (Legendre = p - 1)
|
||||
find_non_square(P) ->
|
||||
find_non_square(2, P).
|
||||
|
||||
find_non_square(Z, P) ->
|
||||
case legendre(Z, P) of
|
||||
L when L =:= P - 1 -> Z;
|
||||
_ -> find_non_square(Z + 1, P)
|
||||
end.
|
||||
|
||||
%% Modular exponentiation: base^exp mod mod
|
||||
mod_pow(Base, Exp, Mod) ->
|
||||
mod_pow(Base, Exp, Mod, 1).
|
||||
|
||||
mod_pow(_Base, 0, _Mod, Acc) ->
|
||||
Acc;
|
||||
mod_pow(Base, Exp, Mod, Acc) ->
|
||||
Acc1 = case Exp rem 2 of
|
||||
1 -> (Acc * Base) rem Mod;
|
||||
0 -> Acc
|
||||
end,
|
||||
Base2 = (Base * Base) rem Mod,
|
||||
mod_pow(Base2, Exp div 2, Mod, Acc1).
|
||||
|
||||
%% Modular multiplication
|
||||
mod_mul(A, B, Mod) ->
|
||||
(A * B) rem Mod.
|
||||
|
||||
%% Test cases
|
||||
task(TestCases) ->
|
||||
lists:foreach(fun([N, P]) ->
|
||||
try
|
||||
R = tonelli(N, P),
|
||||
io:format("n = ~p p = ~p~n roots : ~p ~p~n", [N, P, R, P - R])
|
||||
catch
|
||||
_:Error ->
|
||||
io:format("Error for n=~p, p=~p: ~p~n", [N, P, Error])
|
||||
end
|
||||
end, TestCases).
|
||||
|
||||
main(_) ->
|
||||
Cases = [
|
||||
[10, 13],
|
||||
[56, 101],
|
||||
[1030, 10009],
|
||||
[44402, 100049],
|
||||
[665820697, 1000000009],
|
||||
[881398088036, 1000000000039],
|
||||
[41660815127637347468140745042827704103445750172002, 100000000000000000000000000000000000000000000000577]
|
||||
],
|
||||
task(Cases),
|
||||
|
||||
% Test error case
|
||||
try
|
||||
tonelli(1032, 1009),
|
||||
io:format("Error: Should have failed~n")
|
||||
catch
|
||||
_:Error ->
|
||||
io:format("Correctly caught error: ~p~n", [Error])
|
||||
end.
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
Function Invoke-ModuloExponentiation ([BigInt]$Base, [BigInt]$Exponent, $Modulo) {
|
||||
$Result = 1
|
||||
$Base = $Base % $Modulo
|
||||
If ($Base -eq 0) {return 0}
|
||||
|
||||
While ($Exponent -gt 0) {
|
||||
If (($Exponent -band 1) -eq 1) {$Result = ($Result * $Base) % $Modulo}
|
||||
$Exponent = $Exponent -shr 1
|
||||
$Base = ($Base * $Base) % $Modulo
|
||||
}
|
||||
return ($Result % $Modulo)
|
||||
}
|
||||
|
||||
Function Get-Legendre ([BigInt]$Integer, [BigInt]$Prime) {
|
||||
return (Invoke-ModuloExponentiation -Base $Integer -Exponent (($Prime - 1) / 2) -Modulo $Prime)
|
||||
}
|
||||
|
||||
Function Invoke-TonelliShanks ([BigInt]$Integer, [BigInt]$Prime) {
|
||||
If ((Get-Legendre $Integer $Prime) -ne 1) {throw "$Integer not a square (mod $Prime)"}
|
||||
[bigint]$q = $Prime - 1
|
||||
$s = 0
|
||||
While (($q % 2) -eq 0) {
|
||||
$q = $q / 2
|
||||
$s++
|
||||
}
|
||||
If ($s -eq 1) {
|
||||
return (Invoke-ModuloExponentiation $Integer -Exponent (($Prime + 1) / 4) -Modulo $Prime)
|
||||
}
|
||||
For ($z = 2; [Bigint]::Compare($z, $Prime) -lt 0; $z++) {
|
||||
If ([BigInt]::Compare(($Prime - 1), (Get-Legendre $z $Prime)) -eq 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
$c = Invoke-ModuloExponentiation -Base $z -Exponent $q -Modulo $Prime
|
||||
$r = Invoke-ModuloExponentiation -Base $Integer -Exponent (($q + 1) / 2) -Modulo $Prime
|
||||
$t = Invoke-ModuloExponentiation -Base $Integer -Exponent $q -Modulo $Prime
|
||||
$m = $s
|
||||
$t2 = 0
|
||||
|
||||
While ((($t - 1) % $Prime) -ne 0) {
|
||||
$t2 = $t * $t % $Prime
|
||||
Foreach ($i in (1..$m)) {
|
||||
If ((($t2 -1) % $Prime) -eq 0) {
|
||||
break
|
||||
}
|
||||
$t2 = Invoke-ModuloExponentiation -Base $t2 -Exponent 2 -Modulo $Prime
|
||||
}
|
||||
$b = Invoke-ModuloExponentiation -Base $c -Exponent ([Math]::Pow(2, ($m - $i - 1))) -Modulo $Prime
|
||||
$r = ($r * $b) % $Prime
|
||||
$c = ($b * $b) % $Prime
|
||||
$t = ($t * $c) % $Prime
|
||||
$m = $i
|
||||
}
|
||||
return $r
|
||||
}
|
||||
|
||||
$TonelliTests = @(
|
||||
@{Integer = [BigInt]::Parse('10'); Prime = [BigInt]::Parse('13')},
|
||||
@{Integer = [BigInt]::Parse('56'); Prime = [BigInt]::Parse('101')},
|
||||
@{Integer = [BigInt]::Parse('1030'); Prime = [BigInt]::Parse('10009')},
|
||||
@{Integer = [BigInt]::Parse('44402'); Prime = [BigInt]::Parse('100049')},
|
||||
@{Integer = [BigInt]::Parse('665820697'); Prime = [BigInt]::Parse('1000000009')},
|
||||
@{Integer = [BigInt]::Parse('881398088036'); Prime = [BigInt]::Parse('1000000000039')},
|
||||
@{Integer = [BigInt]::Parse('41660815127637347468140745042827704103445750172002'); Prime = [BigInt]::Parse('100000000000000000000000000000000000000000000000577')}
|
||||
)
|
||||
|
||||
$TonelliTests | Foreach-Object {
|
||||
$Result = Invoke-TonelliShanks @_
|
||||
[PSCustomObject]@{
|
||||
n = $_['Integer']
|
||||
p = $_['Prime']
|
||||
Roots = @($Result, ($_['Prime'] - $Result))
|
||||
}
|
||||
} | Format-List
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
import Foundation
|
||||
|
||||
struct Pair {
|
||||
let n: UInt64
|
||||
let p: UInt64
|
||||
}
|
||||
|
||||
struct Solution {
|
||||
let root1: UInt64
|
||||
let root2: UInt64
|
||||
let isSquare: Bool
|
||||
}
|
||||
|
||||
func multiplyModulus(_ a: UInt64, _ b: UInt64, modulus: UInt64) -> UInt64 {
|
||||
var a = a % modulus
|
||||
var b = b % modulus
|
||||
|
||||
if b < a {
|
||||
swap(&a, &b)
|
||||
}
|
||||
|
||||
var result: UInt64 = 0
|
||||
while a > 0 {
|
||||
if a % 2 == 1 {
|
||||
result = (result + b) % modulus
|
||||
}
|
||||
b = (b << 1) % modulus
|
||||
a >>= 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func powerModulus(_ base: UInt64, _ exponent: UInt64, modulus: UInt64) -> UInt64 {
|
||||
if modulus == 1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var base = base % modulus
|
||||
var result: UInt64 = 1
|
||||
var exponent = exponent
|
||||
|
||||
while exponent > 0 {
|
||||
if (exponent & 1) == 1 {
|
||||
result = multiplyModulus(result, base, modulus: modulus)
|
||||
}
|
||||
base = multiplyModulus(base, base, modulus: modulus)
|
||||
exponent >>= 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func legendre(_ a: UInt64, _ p: UInt64) -> UInt64 {
|
||||
return powerModulus(a, (p - 1) / 2, modulus: p)
|
||||
}
|
||||
|
||||
func tonelliShanks(_ n: UInt64, _ p: UInt64) -> Solution {
|
||||
if legendre(n, p) != 1 {
|
||||
return Solution(root1: 0, root2: 0, isSquare: false)
|
||||
}
|
||||
|
||||
// Factor out powers of 2 from p - 1
|
||||
var q = p - 1
|
||||
var s: UInt64 = 0
|
||||
while q % 2 == 0 {
|
||||
q /= 2
|
||||
s += 1
|
||||
}
|
||||
|
||||
if s == 1 {
|
||||
let result = powerModulus(n, (p + 1) / 4, modulus: p)
|
||||
return Solution(root1: result, root2: p - result, isSquare: true)
|
||||
}
|
||||
|
||||
// Find a non-square z such as ( z | p ) = -1
|
||||
var z: UInt64 = 2
|
||||
while legendre(z, p) != p - 1 {
|
||||
z += 1
|
||||
}
|
||||
|
||||
var c = powerModulus(z, q, modulus: p)
|
||||
var t = powerModulus(n, q, modulus: p)
|
||||
var m = s
|
||||
var result = powerModulus(n, (q + 1) >> 1, modulus: p)
|
||||
|
||||
while t != 1 {
|
||||
var i: UInt64 = 1
|
||||
var z = multiplyModulus(t, t, modulus: p)
|
||||
while z != 1 && i < m - 1 {
|
||||
i += 1
|
||||
z = multiplyModulus(z, z, modulus: p)
|
||||
}
|
||||
let b = powerModulus(c, 1 << (m - i - 1), modulus: p)
|
||||
c = multiplyModulus(b, b, modulus: p)
|
||||
t = multiplyModulus(t, c, modulus: p)
|
||||
m = i
|
||||
result = multiplyModulus(result, b, modulus: p)
|
||||
}
|
||||
|
||||
return Solution(root1: result, root2: p - result, isSquare: true)
|
||||
}
|
||||
|
||||
let tests: [Pair] = [
|
||||
Pair(n: 10, p: 13),
|
||||
Pair(n: 56, p: 101),
|
||||
Pair(n: 1030, p: 1009),
|
||||
Pair(n: 1032, p: 1009),
|
||||
Pair(n: 44402, p: 100049),
|
||||
Pair(n: 665820697, p: 1000000009),
|
||||
Pair(n: 881398088036, p: 1000000000039)
|
||||
]
|
||||
|
||||
for test in tests {
|
||||
let solution = tonelliShanks(test.n, test.p)
|
||||
print("n = \(test.n), p = \(test.p)", terminator: "")
|
||||
if solution.isSquare {
|
||||
print(" has solutions: \(solution.root1) and \(solution.root2)\n")
|
||||
} else {
|
||||
print(" has no solutions because n is not a square modulo p\n")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue