Data update

This commit is contained in:
Ingy döt Net 2026-04-30 12:34:36 -04:00
parent 4bb20c9b71
commit cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions

View file

@ -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.

View file

@ -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

View file

@ -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")
}
}