diff options
Diffstat (limited to 'src/pkg/big/nat.go')
| -rw-r--r-- | src/pkg/big/nat.go | 311 |
1 files changed, 300 insertions, 11 deletions
diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index c8e69a382..8fabd7c8d 100644 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -6,9 +6,7 @@ // These are the building blocks for the operations on signed integers // and rationals. -// NOTE: PACKAGE UNDER CONSTRUCTION. -// -// The big package implements multi-precision arithmetic (big numbers). +// This package implements multi-precision arithmetic (big numbers). // The following numeric types are supported: // // - Int signed integers @@ -17,8 +15,13 @@ // of the operands it may be overwritten (and its memory reused). // To enable chaining of operations, the result is also returned. // +// If possible, one should use big over bignum as the latter is headed for +// deprecation. +// package big +import "rand" + // An unsigned integer x of the form // // x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] @@ -257,12 +260,40 @@ func divNW(z, x []Word, y Word) (q []Word, r Word) { } +func divNN(z, z2, u, v []Word) (q, r []Word) { + if len(v) == 0 { + panic("Divide by zero undefined") + } + + if cmpNN(u, v) < 0 { + q = makeN(z, 0, false); + r = setN(z2, u); + return; + } + + if len(v) == 1 { + var rprime Word; + q, rprime = divNW(z, u, v[0]); + if rprime > 0 { + r = makeN(z2, 1, false); + r[0] = rprime; + } else { + r = makeN(z2, 0, false) + } + return; + } + + q, r = divLargeNN(z, z2, u, v); + return; +} + + // q = (uIn-r)/v, with 0 <= r < y // See Knuth, Volume 2, section 4.3.1, Algorithm D. // Preconditions: // len(v) >= 2 -// len(uIn) >= 1 + len(vIn) -func divNN(z, z2, uIn, v []Word) (q, r []Word) { +// len(uIn) >= len(v) +func divLargeNN(z, z2, uIn, v []Word) (q, r []Word) { n := len(v); m := len(uIn) - len(v); @@ -274,7 +305,7 @@ func divNN(z, z2, uIn, v []Word) (q, r []Word) { shift := leadingZeroBits(v[n-1]); shiftLeft(v, v, shift); shiftLeft(u, uIn, shift); - u[len(uIn)] = uIn[len(uIn)-1] >> (uint(_W) - uint(shift)); + u[len(uIn)] = uIn[len(uIn)-1] >> (_W - uint(shift)); // D2. for j := m; j >= 0; j-- { @@ -335,7 +366,7 @@ func log2(x Word) int { func log2N(x []Word) int { m := len(x); if m > 0 { - return (m-1)*int(_W) + log2(x[m-1]) + return (m-1)*_W + log2(x[m-1]) } return -1; } @@ -439,7 +470,7 @@ func leadingZeroBits(x Word) int { c := 0; if x < 1<<(_W/2) { x <<= _W / 2; - c = int(_W / 2); + c = _W / 2; } for i := 0; x != 0; i++ { @@ -449,7 +480,47 @@ func leadingZeroBits(x Word) int { x <<= 1; } - return int(_W); + return _W; +} + +const deBruijn32 = 0x077CB531 + +var deBruijn32Lookup = []byte{ + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, +} + +const deBruijn64 = 0x03f79d71b4ca8b09 + +var deBruijn64Lookup = []byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +// trailingZeroBits returns the number of consecutive zero bits on the right +// side of the given Word. +// See Knuth, volume 4, section 7.3.1 +func trailingZeroBits(x Word) int { + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multipling by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn constant is + // such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + switch _W { + case 32: + return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) + case 64: + return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) + default: + panic("Unknown word size") + } + + return 0; } @@ -458,7 +529,7 @@ func shiftLeft(dst, src []Word, n int) { return } - ñ := uint(_W) - uint(n); + ñ := _W - uint(n); for i := len(src) - 1; i >= 1; i-- { dst[i] = src[i] << uint(n); dst[i] |= src[i-1] >> ñ; @@ -472,7 +543,7 @@ func shiftRight(dst, src []Word, n int) { return } - ñ := uint(_W) - uint(n); + ñ := _W - uint(n); for i := 0; i < len(src)-1; i++ { dst[i] = src[i] >> uint(n); dst[i] |= src[i+1] << ñ; @@ -483,3 +554,221 @@ func shiftRight(dst, src []Word, n int) { // greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) func greaterThan(x1, x2, y1, y2 Word) bool { return x1 > y1 || x1 == y1 && x2 > y2 } + + +// modNW returns x % d. +func modNW(x []Word, d Word) (r Word) { + // TODO(agl): we don't actually need to store the q value. + q := makeN(nil, len(x), false); + return divWVW(&q[0], 0, &x[0], d, len(x)); +} + + +// powersOfTwoDecompose finds q and k such that q * 1<<k = n and q is odd. +func powersOfTwoDecompose(n []Word) (q []Word, k Word) { + if len(n) == 0 { + return n, 0 + } + + zeroWords := 0; + for n[zeroWords] == 0 { + zeroWords++ + } + // One of the words must be non-zero by invariant, therefore + // zeroWords < len(n). + x := trailingZeroBits(n[zeroWords]); + + q = makeN(nil, len(n)-zeroWords, false); + shiftRight(q, n[zeroWords:len(n)], x); + + k = Word(_W*zeroWords + x); + return; +} + + +// randomN creates a random integer in [0..limit), using the space in z if +// possible. n is the bit length of limit. +func randomN(z []Word, rand *rand.Rand, limit []Word, n int) []Word { + bitLengthOfMSW := uint(n % _W); + mask := Word((1 << bitLengthOfMSW) - 1); + z = makeN(z, len(limit), false); + + for { + for i := range z { + switch _W { + case 32: + z[i] = Word(rand.Uint32()) + case 64: + z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32 + } + } + + z[len(limit)-1] &= mask; + + if cmpNN(z, limit) < 0 { + break + } + } + + return z; +} + + +// If m != nil, expNNN calculates x**y mod m. Otherwise it calculates x**y. It +// reuses the storage of z if possible. +func expNNN(z, x, y, m []Word) []Word { + if len(y) == 0 { + z = makeN(z, 1, false); + z[0] = 1; + return z; + } + + if m != nil { + // We likely end up being as long as the modulus. + z = makeN(z, len(m), false) + } + z = setN(z, x); + v := y[len(y)-1]; + // It's invalid for the most significant word to be zero, therefore we + // will find a one bit. + shift := leadingZeros(v) + 1; + v <<= shift; + var q []Word; + + const mask = 1 << (_W - 1); + + // We walk through the bits of the exponent one by one. Each time we + // see a bit, we square, thus doubling the power. If the bit is a one, + // we also multiply by x, thus adding one to the power. + + w := _W - int(shift); + for j := 0; j < w; j++ { + z = mulNN(z, z, z); + + if v&mask != 0 { + z = mulNN(z, z, x) + } + + if m != nil { + q, z = divNN(q, z, z, m) + } + + v <<= 1; + } + + for i := len(y) - 2; i >= 0; i-- { + v = y[i]; + + for j := 0; j < _W; j++ { + z = mulNN(z, z, z); + + if v&mask != 0 { + z = mulNN(z, z, x) + } + + if m != nil { + q, z = divNN(q, z, z, m) + } + + v <<= 1; + } + } + + return z; +} + + +// lenN returns the bit length of z. +func lenN(z []Word) int { + if len(z) == 0 { + return 0 + } + + return (len(z)-1)*_W + (_W - leadingZeroBits(z[len(z)-1])); +} + + +const ( + primesProduct32 = 0xC0CFD797; // Π {p ∈ primes, 2 < p <= 29} + primesProduct64 = 0xE221F97C30E94E1D; // Π {p ∈ primes, 2 < p <= 53} +) + +var bigOne = []Word{1} +var bigTwo = []Word{2} + +// ProbablyPrime performs n Miller-Rabin tests to check whether n is prime. +// If it returns true, n is prime with probability 1 - 1/4^n. +// If it returns false, n is not prime. +func probablyPrime(n []Word, reps int) bool { + if len(n) == 0 { + return false + } + + if len(n) == 1 { + if n[0]%2 == 0 { + return n[0] == 2 + } + + // We have to exclude these cases because we reject all + // multiples of these numbers below. + if n[0] == 3 || n[0] == 5 || n[0] == 7 || n[0] == 11 || + n[0] == 13 || n[0] == 17 || n[0] == 19 || n[0] == 23 || + n[0] == 29 || n[0] == 31 || n[0] == 37 || n[0] == 41 || + n[0] == 43 || n[0] == 47 || n[0] == 53 { + return true + } + } + + var r Word; + switch _W { + case 32: + r = modNW(n, primesProduct32) + case 64: + r = modNW(n, primesProduct64&_M) + default: + panic("Unknown word size") + } + + if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 || + r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 { + return false + } + + if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 || + r%43 == 0 || r%47 == 0 || r%53 == 0) { + return false + } + + nm1 := subNN(nil, n, bigOne); + // 1<<k * q = nm1; + q, k := powersOfTwoDecompose(nm1); + + nm3 := subNN(nil, nm1, bigTwo); + rand := rand.New(rand.NewSource(int64(n[0]))); + + var x, y, quotient []Word; + nm3Len := lenN(nm3); + +NextRandom: + for i := 0; i < reps; i++ { + x = randomN(x, rand, nm3, nm3Len); + addNN(x, x, bigTwo); + y = expNNN(y, x, q, n); + if cmpNN(y, bigOne) == 0 || cmpNN(y, nm1) == 0 { + continue + } + for j := Word(1); j < k; j++ { + y = mulNN(y, y, y); + quotient, y = divNN(quotient, y, y, n); + if cmpNN(y, nm1) == 0 { + continue NextRandom + } + if cmpNN(y, bigOne) == 0 { + return false + } + } + return false; + } + + return true; +} |
