Skip to content

Commit 85f4051

Browse files
bmkesslergriesemer
authored andcommitted
math/big: implement Atkin's ModSqrt for 5 mod 8 primes
For primes congruent to 5 mod 8 there is a simple deterministic method for calculating the modular square root due to Atkin, using one exponentiation and 4 multiplications. A. Atkin. Probabilistic primality testing, summary by F. Morain. Research Report 1779, INRIA, pages 159–163, 1992. This increases the speed of modular square roots for these primes considerably. name old time/op new time/op delta ModSqrt231_5Mod8-4 1.03ms ± 2% 0.36ms ± 5% -65.06% (p=0.008 n=5+5) Change-Id: I024f6e514bbca8d634218983117db2afffe615fe Reviewed-on: https://go-review.googlesource.com/99615 Reviewed-by: Robert Griesemer <[email protected]>
1 parent fd4392b commit 85f4051

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed

src/math/big/int.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,30 @@ func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int {
819819
return z
820820
}
821821

822+
// modSqrt5Mod8 uses Atkin's observation that 2 is not a square mod p
823+
// alpha == (2*a)^((p-5)/8) mod p
824+
// beta == 2*a*alpha^2 mod p is a square root of -1
825+
// b == a*alpha*(beta-1) mod p is a square root of a
826+
// to calculate the square root of any quadratic residue mod p quickly for 5
827+
// mod 8 primes.
828+
func (z *Int) modSqrt5Mod8Prime(x, p *Int) *Int {
829+
// p == 5 mod 8 implies p = e*8 + 5
830+
// e is the quotient and 5 the remainder on division by 8
831+
e := new(Int).Rsh(p, 3) // e = (p - 5) / 8
832+
tx := new(Int).Lsh(x, 1) // tx = 2*x
833+
alpha := new(Int).Exp(tx, e, p)
834+
beta := new(Int).Mul(alpha, alpha)
835+
beta.Mod(beta, p)
836+
beta.Mul(beta, tx)
837+
beta.Mod(beta, p)
838+
beta.Sub(beta, intOne)
839+
beta.Mul(beta, x)
840+
beta.Mod(beta, p)
841+
beta.Mul(beta, alpha)
842+
z.Mod(beta, p)
843+
return z
844+
}
845+
822846
// modSqrtTonelliShanks uses the Tonelli-Shanks algorithm to find the square
823847
// root of a quadratic residue modulo any prime.
824848
func (z *Int) modSqrtTonelliShanks(x, p *Int) *Int {
@@ -885,12 +909,17 @@ func (z *Int) ModSqrt(x, p *Int) *Int {
885909
x = new(Int).Mod(x, p)
886910
}
887911

888-
// Check whether p is 3 mod 4, and if so, use the faster algorithm.
889-
if len(p.abs) > 0 && p.abs[0]%4 == 3 {
912+
switch {
913+
case p.abs[0]%4 == 3:
914+
// Check whether p is 3 mod 4, and if so, use the faster algorithm.
890915
return z.modSqrt3Mod4Prime(x, p)
916+
case p.abs[0]%8 == 5:
917+
// Check whether p is 5 mod 8, use Atkin's algorithm.
918+
return z.modSqrt5Mod8Prime(x, p)
919+
default:
920+
// Otherwise, use Tonelli-Shanks.
921+
return z.modSqrtTonelliShanks(x, p)
891922
}
892-
// Otherwise, use Tonelli-Shanks.
893-
return z.modSqrtTonelliShanks(x, p)
894923
}
895924

896925
// Lsh sets z = x << n and returns z.

src/math/big/int_test.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,7 @@ func BenchmarkModSqrt225_Tonelli(b *testing.B) {
13601360
}
13611361
}
13621362

1363-
func BenchmarkModSqrt224_3Mod4(b *testing.B) {
1363+
func BenchmarkModSqrt225_3Mod4(b *testing.B) {
13641364
p := tri(225)
13651365
x := new(Int).SetUint64(2)
13661366
for i := 0; i < b.N; i++ {
@@ -1369,27 +1369,25 @@ func BenchmarkModSqrt224_3Mod4(b *testing.B) {
13691369
}
13701370
}
13711371

1372-
func BenchmarkModSqrt5430_Tonelli(b *testing.B) {
1373-
if isRaceBuilder {
1374-
b.Skip("skipping on race builder")
1375-
}
1376-
p := tri(5430)
1377-
x := new(Int).SetUint64(2)
1372+
func BenchmarkModSqrt231_Tonelli(b *testing.B) {
1373+
p := tri(231)
1374+
p.Sub(p, intOne)
1375+
p.Sub(p, intOne) // tri(231) - 2 is a prime == 5 mod 8
1376+
x := new(Int).SetUint64(7)
13781377
for i := 0; i < b.N; i++ {
1379-
x.SetUint64(2)
1378+
x.SetUint64(7)
13801379
x.modSqrtTonelliShanks(x, p)
13811380
}
13821381
}
13831382

1384-
func BenchmarkModSqrt5430_3Mod4(b *testing.B) {
1385-
if isRaceBuilder {
1386-
b.Skip("skipping on race builder")
1387-
}
1388-
p := tri(5430)
1389-
x := new(Int).SetUint64(2)
1383+
func BenchmarkModSqrt231_5Mod8(b *testing.B) {
1384+
p := tri(231)
1385+
p.Sub(p, intOne)
1386+
p.Sub(p, intOne) // tri(231) - 2 is a prime == 5 mod 8
1387+
x := new(Int).SetUint64(7)
13901388
for i := 0; i < b.N; i++ {
1391-
x.SetUint64(2)
1392-
x.modSqrt3Mod4Prime(x, p)
1389+
x.SetUint64(7)
1390+
x.modSqrt5Mod8Prime(x, p)
13931391
}
13941392
}
13951393

0 commit comments

Comments
 (0)