Commit 96d1e4ab authored by Keith Randall's avatar Keith Randall

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
parent a3416cf5
...@@ -752,15 +752,16 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { ...@@ -752,15 +752,16 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
return z return z
} }
// ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where // ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
// p is a prime) and returns z. // and returns z. If g and n are not relatively prime, the result is undefined.
func (z *Int) ModInverse(g, p *Int) *Int { func (z *Int) ModInverse(g, n *Int) *Int {
var d Int var d Int
d.GCD(z, nil, g, p) d.GCD(z, nil, g, n)
// x and y are such that g*x + p*y = d. Since p is prime, d = 1. Taking // x and y are such that g*x + n*y = d. Since g and n are
// that modulo p results in g*x = 1, therefore x is the inverse element. // relatively prime, d = 1. Taking that modulo n results in
// g*x = 1, therefore x is the inverse element.
if z.neg { if z.neg {
z.Add(z, p) z.Add(z, n)
} }
return z return z
} }
......
...@@ -1448,24 +1448,40 @@ func TestNot(t *testing.T) { ...@@ -1448,24 +1448,40 @@ func TestNot(t *testing.T) {
var modInverseTests = []struct { var modInverseTests = []struct {
element string element string
prime string modulus string
}{ }{
{"1", "7"}, {"1234567", "458948883992"},
{"1", "13"},
{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"},
} }
func TestModInverse(t *testing.T) { func TestModInverse(t *testing.T) {
var element, prime Int var element, modulus, gcd, inverse Int
one := NewInt(1) one := NewInt(1)
for i, test := range modInverseTests { for i, test := range modInverseTests {
(&element).SetString(test.element, 10) (&element).SetString(test.element, 10)
(&prime).SetString(test.prime, 10) (&modulus).SetString(test.modulus, 10)
inverse := new(Int).ModInverse(&element, &prime) (&inverse).ModInverse(&element, &modulus)
inverse.Mul(inverse, &element) (&inverse).Mul(&inverse, &element)
inverse.Mod(inverse, &prime) (&inverse).Mod(&inverse, &modulus)
if inverse.Cmp(one) != 0 { if (&inverse).Cmp(one) != 0 {
t.Errorf("#%d: failed (e·e^(-1)=%s)", i, inverse) t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse)
}
}
// exhaustive test for small values
for n := 2; n < 100; n++ {
(&modulus).SetInt64(int64(n))
for x := 1; x < n; x++ {
(&element).SetInt64(int64(x))
(&gcd).GCD(nil, nil, &element, &modulus)
if (&gcd).Cmp(one) != 0 {
continue
}
(&inverse).ModInverse(&element, &modulus)
(&inverse).Mul(&inverse, &element)
(&inverse).Mod(&inverse, &modulus)
if (&inverse).Cmp(one) != 0 {
t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse)
}
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment