Skip to content

Commit 96d1e4a

Browse files
committed
math/big: Allow non-prime modulus for ModInverse
The inverse is defined whenever the element and the modulus are relatively prime. The code already handles this situation, but the spec does not. Test that it does indeed work. Fixes #8875 LGTM=agl R=agl CC=golang-codereviews https://golang.org/cl/155010043
1 parent a3416cf commit 96d1e4a

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

src/math/big/int.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -752,15 +752,16 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
752752
return z
753753
}
754754

755-
// ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where
756-
// p is a prime) and returns z.
757-
func (z *Int) ModInverse(g, p *Int) *Int {
755+
// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
756+
// and returns z. If g and n are not relatively prime, the result is undefined.
757+
func (z *Int) ModInverse(g, n *Int) *Int {
758758
var d Int
759-
d.GCD(z, nil, g, p)
760-
// x and y are such that g*x + p*y = d. Since p is prime, d = 1. Taking
761-
// that modulo p results in g*x = 1, therefore x is the inverse element.
759+
d.GCD(z, nil, g, n)
760+
// x and y are such that g*x + n*y = d. Since g and n are
761+
// relatively prime, d = 1. Taking that modulo n results in
762+
// g*x = 1, therefore x is the inverse element.
762763
if z.neg {
763-
z.Add(z, p)
764+
z.Add(z, n)
764765
}
765766
return z
766767
}

src/math/big/int_test.go

+26-10
Original file line numberDiff line numberDiff line change
@@ -1448,24 +1448,40 @@ func TestNot(t *testing.T) {
14481448

14491449
var modInverseTests = []struct {
14501450
element string
1451-
prime string
1451+
modulus string
14521452
}{
1453-
{"1", "7"},
1454-
{"1", "13"},
1453+
{"1234567", "458948883992"},
14551454
{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"},
14561455
}
14571456

14581457
func TestModInverse(t *testing.T) {
1459-
var element, prime Int
1458+
var element, modulus, gcd, inverse Int
14601459
one := NewInt(1)
14611460
for i, test := range modInverseTests {
14621461
(&element).SetString(test.element, 10)
1463-
(&prime).SetString(test.prime, 10)
1464-
inverse := new(Int).ModInverse(&element, &prime)
1465-
inverse.Mul(inverse, &element)
1466-
inverse.Mod(inverse, &prime)
1467-
if inverse.Cmp(one) != 0 {
1468-
t.Errorf("#%d: failed (e·e^(-1)=%s)", i, inverse)
1462+
(&modulus).SetString(test.modulus, 10)
1463+
(&inverse).ModInverse(&element, &modulus)
1464+
(&inverse).Mul(&inverse, &element)
1465+
(&inverse).Mod(&inverse, &modulus)
1466+
if (&inverse).Cmp(one) != 0 {
1467+
t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse)
1468+
}
1469+
}
1470+
// exhaustive test for small values
1471+
for n := 2; n < 100; n++ {
1472+
(&modulus).SetInt64(int64(n))
1473+
for x := 1; x < n; x++ {
1474+
(&element).SetInt64(int64(x))
1475+
(&gcd).GCD(nil, nil, &element, &modulus)
1476+
if (&gcd).Cmp(one) != 0 {
1477+
continue
1478+
}
1479+
(&inverse).ModInverse(&element, &modulus)
1480+
(&inverse).Mul(&inverse, &element)
1481+
(&inverse).Mod(&inverse, &modulus)
1482+
if (&inverse).Cmp(one) != 0 {
1483+
t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse)
1484+
}
14691485
}
14701486
}
14711487
}

0 commit comments

Comments
 (0)