Purpose: Compute a modular square root — find with for an odd prime — in expected time.
Algorithm
- Check solvability with the Euler criterion: , otherwise no root exists.
- Easy case : the answer is . Done in one modpow.
- General case. Write with odd.
- Find any quadratic non-residue (try ; half of all residues work, so this takes tries on average). Set .
- Initialise , , .
- While :
- find the least with ;
- set , then , , , .
- Return (and , the other root).
Code
long long tonelli(long long a, long long p) {
a %= p; if (a < 0) a += p;
if (a == 0) return 0;
if (powmod(a, (p - 1) / 2, p) != 1) return -1; // not a residue
if (p % 4 == 3) return powmod(a, (p + 1) / 4, p);
long long q = p - 1; int s = 0;
while (q % 2 == 0) { q /= 2; s++; }
long long z = 2;
while (powmod(z, (p - 1) / 2, p) != p - 1) z++; // find a non-residue
long long c = powmod(z, q, p);
long long x = powmod(a, (q + 1) / 2, p);
long long t = powmod(a, q, p);
int m = s;
while (t != 1) {
int i = 0;
long long tt = t;
while (tt != 1) { tt = tt * tt % p; i++; }
long long b = powmod(c, 1LL << (m - i - 1), p);
x = x * b % p;
c = b * b % p;
t = t * c % p;
m = i;
}
return x;
}Paradigm
Group-theoretic descent. Each iteration halves the order of inside the 2-Sylow subgroup of , so the loop terminates in at most rounds.
Complexity
- Time: expected — for the non-residue search (constant tries × one modpow) plus for the descent
- Space:
Why It Works
Throughout the loop the invariant holds, and lies in the subgroup of order . The value is the exact order of (as a power of two), and is chosen so that has the same order as but is its inverse there — multiplying kills the top bit. Since strictly decreases, we reach , at which point the invariant reads . ∎
Variants / Use Cases
- Cipolla’s algorithm — an alternative with worst case, better when is divisible by a huge power of two
- Quadratic residues — the theory this implements
- Discrete root — solving generalises this via primitive roots
- Prime powers and composites — lift with Hensel lifting, then recombine with CRT
- Counting points on curves, Cornacchia — need a square root as a subroutine