mirror of
https://github.com/golang/go
synced 2024-11-22 04:04:40 -07:00
big: Add Lsh and Value; convert pidigits to use big
This yields a pretty significant performance boost to pidigits and there are still some improvements to be made. Here are my numbers: amd64 w/ bignum: pidigits 10000 gcc -O2 pidigits.c -lgmp 2.10u 0.00s 2.10r gc pidigits 22.92u 0.02s 22.97r gc_B pidigits 22.62u 0.00s 22.65r amd64 w/ big: pidigits 10000 gcc -O2 pidigits.c -lgmp 2.09u 0.02s 2.11r gc pidigits 12.68u 0.04s 12.72r gc_B pidigits 12.71u 0.03s 12.75r 386 w/ bignum: pidigits 10000 gcc -O2 pidigits.c -lgmp 2.09u 0.00s 2.09r gc pidigits 44.30u 0.01s 44.35r gc_B pidigits 44.29u 0.03s 44.35r 386 w/ big: pidigits 10000 gcc -O2 pidigits.c -lgmp 2.10u 0.00s 2.10r gc pidigits 22.70u 0.06s 22.79r gc_B pidigits 22.80u 0.09s 22.91r R=rsc, gri CC=golang-dev https://golang.org/cl/881050
This commit is contained in:
parent
4ba177ae36
commit
76cbbc8a95
@ -100,21 +100,26 @@ func (z *Int) Mul(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
|
||||
// Div calculates q = (x-r)/y where 0 <= r < y. The receiver is set to q.
|
||||
func (z *Int) Div(x, y *Int) (q, r *Int) {
|
||||
q = z
|
||||
r = new(Int)
|
||||
div(q, r, x, y)
|
||||
return
|
||||
// Div calculates q = (x-r)/y and sets z = q.
|
||||
func (z *Int) Div(x, y *Int) *Int {
|
||||
r := new(Int)
|
||||
div(z, r, x, y)
|
||||
return z
|
||||
}
|
||||
|
||||
|
||||
// Mod calculates q = (x-r)/y and returns r.
|
||||
func (z *Int) Mod(x, y *Int) (r *Int) {
|
||||
// Mod calculates q = (x-r)/y and sets z = r.
|
||||
func (z *Int) Mod(x, y *Int) *Int {
|
||||
q := new(Int)
|
||||
r = z
|
||||
div(q, r, x, y)
|
||||
return
|
||||
div(q, z, x, y)
|
||||
return z
|
||||
}
|
||||
|
||||
|
||||
// DivMod calculates q = (x-r)/y and sets z = q. (It returns z, r.)
|
||||
func (z *Int) DivMod(x, y, r *Int) (*Int, *Int) {
|
||||
div(z, r, x, y)
|
||||
return z, r
|
||||
}
|
||||
|
||||
|
||||
@ -169,6 +174,23 @@ func (z *Int) String() string {
|
||||
}
|
||||
|
||||
|
||||
// Int64 returns the int64 representation of z.
|
||||
// If z cannot be represented in an int64, the result is undefined.
|
||||
func (z *Int) Int64() int64 {
|
||||
if len(z.abs) == 0 {
|
||||
return 0
|
||||
}
|
||||
v := int64(z.abs[0])
|
||||
if _W == 32 && len(z.abs) > 1 {
|
||||
v |= int64(z.abs[1]) << 32
|
||||
}
|
||||
if z.neg {
|
||||
v = -v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
// SetString sets z to the value of s, interpreted in the given base.
|
||||
// If base is 0 then SetString attempts to detect the base by at the prefix of
|
||||
// s. '0x' implies base 16, '0' implies base 8. Otherwise base 10 is assumed.
|
||||
@ -324,7 +346,8 @@ func GcdInt(d, x, y, a, b *Int) {
|
||||
temp := new(Int)
|
||||
|
||||
for len(B.abs) > 0 {
|
||||
q, r := q.Div(A, B)
|
||||
r := new(Int)
|
||||
q, r = q.DivMod(A, B, r)
|
||||
|
||||
A, B = B, r
|
||||
|
||||
@ -359,12 +382,28 @@ func GcdInt(d, x, y, a, b *Int) {
|
||||
func ProbablyPrime(z *Int, n int) bool { return !z.neg && probablyPrime(z.abs, n) }
|
||||
|
||||
|
||||
// Rsh sets z = x >> s and returns z.
|
||||
func (z *Int) Rsh(x *Int, n int) *Int {
|
||||
removedWords := n / _W
|
||||
z.abs = makeN(z.abs, len(x.abs)-removedWords, false)
|
||||
// Lsh sets z = x << n and returns z.
|
||||
func (z *Int) Lsh(x *Int, n uint) *Int {
|
||||
addedWords := int(n) / _W
|
||||
// Don't assign z.abs yet, in case z == x
|
||||
znew := makeN(z.abs, len(x.abs)+addedWords+1, false)
|
||||
z.neg = x.neg
|
||||
shiftRight(z.abs, x.abs[removedWords:], n%_W)
|
||||
z.abs = normN(z.abs)
|
||||
shiftLeft(znew[addedWords:], x.abs, n%_W)
|
||||
for i := range znew[0:addedWords] {
|
||||
znew[i] = 0
|
||||
}
|
||||
z.abs = normN(znew)
|
||||
return z
|
||||
}
|
||||
|
||||
|
||||
// Rsh sets z = x >> n and returns z.
|
||||
func (z *Int) Rsh(x *Int, n uint) *Int {
|
||||
removedWords := int(n) / _W
|
||||
// Don't assign z.abs yet, in case z == x
|
||||
znew := makeN(z.abs, len(x.abs)-removedWords, false)
|
||||
z.neg = x.neg
|
||||
shiftRight(znew, x.abs[removedWords:], n%_W)
|
||||
z.abs = normN(znew)
|
||||
return z
|
||||
}
|
||||
|
@ -192,7 +192,8 @@ func TestDivSigns(t *testing.T) {
|
||||
for i, test := range divSignsTests {
|
||||
x := new(Int).New(test.x)
|
||||
y := new(Int).New(test.y)
|
||||
q, r := new(Int).Div(x, y)
|
||||
r := new(Int)
|
||||
q, r := new(Int).DivMod(x, y, r)
|
||||
expectedQ := new(Int).New(test.q)
|
||||
expectedR := new(Int).New(test.r)
|
||||
|
||||
@ -249,7 +250,8 @@ func checkDiv(x, y []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
q, r := new(Int).Div(u, v)
|
||||
r := new(Int)
|
||||
q, r := new(Int).DivMod(u, v, r)
|
||||
|
||||
if r.Cmp(v) >= 0 {
|
||||
return false
|
||||
@ -297,7 +299,8 @@ func TestDiv(t *testing.T) {
|
||||
expectedQ, _ := new(Int).SetString(test.q, 10)
|
||||
expectedR, _ := new(Int).SetString(test.r, 10)
|
||||
|
||||
q, r := new(Int).Div(x, y)
|
||||
r := new(Int)
|
||||
q, r := new(Int).DivMod(x, y, r)
|
||||
|
||||
if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 {
|
||||
t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR)
|
||||
@ -313,7 +316,8 @@ func TestDivStepD6(t *testing.T) {
|
||||
u := &Int{false, []Word{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}}
|
||||
v := &Int{false, []Word{5, 2 + 1<<(_W-1), 1 << (_W - 1)}}
|
||||
|
||||
q, r := new(Int).Div(u, v)
|
||||
r := new(Int)
|
||||
q, r := new(Int).DivMod(u, v, r)
|
||||
const expectedQ64 = "18446744073709551613"
|
||||
const expectedR64 = "3138550867693340382088035895064302439801311770021610913807"
|
||||
const expectedQ32 = "4294967293"
|
||||
@ -519,32 +523,32 @@ func TestProbablyPrime(t *testing.T) {
|
||||
}
|
||||
|
||||
|
||||
type rshTest struct {
|
||||
type intShiftTest struct {
|
||||
in string
|
||||
shift int
|
||||
shift uint
|
||||
out string
|
||||
}
|
||||
|
||||
|
||||
var rshTests = []rshTest{
|
||||
rshTest{"0", 0, "0"},
|
||||
rshTest{"0", 1, "0"},
|
||||
rshTest{"0", 2, "0"},
|
||||
rshTest{"1", 0, "1"},
|
||||
rshTest{"1", 1, "0"},
|
||||
rshTest{"1", 2, "0"},
|
||||
rshTest{"2", 0, "2"},
|
||||
rshTest{"2", 1, "1"},
|
||||
rshTest{"2", 2, "0"},
|
||||
rshTest{"4294967296", 0, "4294967296"},
|
||||
rshTest{"4294967296", 1, "2147483648"},
|
||||
rshTest{"4294967296", 2, "1073741824"},
|
||||
rshTest{"18446744073709551616", 0, "18446744073709551616"},
|
||||
rshTest{"18446744073709551616", 1, "9223372036854775808"},
|
||||
rshTest{"18446744073709551616", 2, "4611686018427387904"},
|
||||
rshTest{"18446744073709551616", 64, "1"},
|
||||
rshTest{"340282366920938463463374607431768211456", 64, "18446744073709551616"},
|
||||
rshTest{"340282366920938463463374607431768211456", 128, "1"},
|
||||
var rshTests = []intShiftTest{
|
||||
intShiftTest{"0", 0, "0"},
|
||||
intShiftTest{"0", 1, "0"},
|
||||
intShiftTest{"0", 2, "0"},
|
||||
intShiftTest{"1", 0, "1"},
|
||||
intShiftTest{"1", 1, "0"},
|
||||
intShiftTest{"1", 2, "0"},
|
||||
intShiftTest{"2", 0, "2"},
|
||||
intShiftTest{"2", 1, "1"},
|
||||
intShiftTest{"2", 2, "0"},
|
||||
intShiftTest{"4294967296", 0, "4294967296"},
|
||||
intShiftTest{"4294967296", 1, "2147483648"},
|
||||
intShiftTest{"4294967296", 2, "1073741824"},
|
||||
intShiftTest{"18446744073709551616", 0, "18446744073709551616"},
|
||||
intShiftTest{"18446744073709551616", 1, "9223372036854775808"},
|
||||
intShiftTest{"18446744073709551616", 2, "4611686018427387904"},
|
||||
intShiftTest{"18446744073709551616", 64, "1"},
|
||||
intShiftTest{"340282366920938463463374607431768211456", 64, "18446744073709551616"},
|
||||
intShiftTest{"340282366920938463463374607431768211456", 128, "1"},
|
||||
}
|
||||
|
||||
|
||||
@ -559,3 +563,112 @@ func TestRsh(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestRshSelf(t *testing.T) {
|
||||
for i, test := range rshTests {
|
||||
z, _ := new(Int).SetString(test.in, 10)
|
||||
expected, _ := new(Int).SetString(test.out, 10)
|
||||
z.Rsh(z, test.shift)
|
||||
|
||||
if z.Cmp(expected) != 0 {
|
||||
t.Errorf("#%d got %s want %s", i, z, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var lshTests = []intShiftTest{
|
||||
intShiftTest{"0", 0, "0"},
|
||||
intShiftTest{"0", 1, "0"},
|
||||
intShiftTest{"0", 2, "0"},
|
||||
intShiftTest{"1", 0, "1"},
|
||||
intShiftTest{"1", 1, "2"},
|
||||
intShiftTest{"1", 2, "4"},
|
||||
intShiftTest{"2", 0, "2"},
|
||||
intShiftTest{"2", 1, "4"},
|
||||
intShiftTest{"2", 2, "8"},
|
||||
intShiftTest{"-87", 1, "-174"},
|
||||
intShiftTest{"4294967296", 0, "4294967296"},
|
||||
intShiftTest{"4294967296", 1, "8589934592"},
|
||||
intShiftTest{"4294967296", 2, "17179869184"},
|
||||
intShiftTest{"18446744073709551616", 0, "18446744073709551616"},
|
||||
intShiftTest{"9223372036854775808", 1, "18446744073709551616"},
|
||||
intShiftTest{"4611686018427387904", 2, "18446744073709551616"},
|
||||
intShiftTest{"1", 64, "18446744073709551616"},
|
||||
intShiftTest{"18446744073709551616", 64, "340282366920938463463374607431768211456"},
|
||||
intShiftTest{"1", 128, "340282366920938463463374607431768211456"},
|
||||
}
|
||||
|
||||
|
||||
func TestLsh(t *testing.T) {
|
||||
for i, test := range lshTests {
|
||||
in, _ := new(Int).SetString(test.in, 10)
|
||||
expected, _ := new(Int).SetString(test.out, 10)
|
||||
out := new(Int).Lsh(in, test.shift)
|
||||
|
||||
if out.Cmp(expected) != 0 {
|
||||
t.Errorf("#%d got %s want %s", i, out, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestLshSelf(t *testing.T) {
|
||||
for i, test := range lshTests {
|
||||
z, _ := new(Int).SetString(test.in, 10)
|
||||
expected, _ := new(Int).SetString(test.out, 10)
|
||||
z.Lsh(z, test.shift)
|
||||
|
||||
if z.Cmp(expected) != 0 {
|
||||
t.Errorf("#%d got %s want %s", i, z, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestLshRsh(t *testing.T) {
|
||||
for i, test := range rshTests {
|
||||
in, _ := new(Int).SetString(test.in, 10)
|
||||
out := new(Int).Lsh(in, test.shift)
|
||||
out = out.Rsh(out, test.shift)
|
||||
|
||||
if in.Cmp(out) != 0 {
|
||||
t.Errorf("#%d got %s want %s", i, out, in)
|
||||
}
|
||||
}
|
||||
for i, test := range lshTests {
|
||||
in, _ := new(Int).SetString(test.in, 10)
|
||||
out := new(Int).Lsh(in, test.shift)
|
||||
out.Rsh(out, test.shift)
|
||||
|
||||
if in.Cmp(out) != 0 {
|
||||
t.Errorf("#%d got %s want %s", i, out, in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var int64Tests = []int64{
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
4294967295,
|
||||
-4294967295,
|
||||
4294967296,
|
||||
-4294967296,
|
||||
9223372036854775807,
|
||||
-9223372036854775807,
|
||||
-9223372036854775808,
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
for i, testVal := range int64Tests {
|
||||
in := NewInt(testVal)
|
||||
out := in.Int64()
|
||||
|
||||
if out != testVal {
|
||||
t.Errorf("#%d got %d want %d", i, out, testVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ func divLargeNN(z, z2, uIn, v []Word) (q, r []Word) {
|
||||
q = makeN(z, m+1, false)
|
||||
|
||||
// D1.
|
||||
shift := leadingZeroBits(v[n-1])
|
||||
shift := uint(leadingZeroBits(v[n-1]))
|
||||
shiftLeft(v, v, shift)
|
||||
shiftLeft(u, uIn, shift)
|
||||
u[len(uIn)] = uIn[len(uIn)-1] >> (_W - uint(shift))
|
||||
@ -530,31 +530,34 @@ func trailingZeroBits(x Word) int {
|
||||
}
|
||||
|
||||
|
||||
func shiftLeft(dst, src []Word, n int) {
|
||||
// To avoid losing the top n bits, dst should be sized so that
|
||||
// len(dst) == len(src) + 1.
|
||||
func shiftLeft(dst, src []Word, n uint) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ñ := _W - uint(n)
|
||||
for i := len(src) - 1; i >= 1; i-- {
|
||||
dst[i] = src[i] << uint(n)
|
||||
dst[i] |= src[i-1] >> ñ
|
||||
ñ := _W - n
|
||||
if len(dst) > len(src) {
|
||||
dst[len(src)] |= src[len(src)-1] >> ñ
|
||||
}
|
||||
dst[0] = src[0] << uint(n)
|
||||
for i := len(src) - 1; i >= 1; i-- {
|
||||
dst[i] = src[i]<<n | src[i-1]>>ñ
|
||||
}
|
||||
dst[0] = src[0] << n
|
||||
}
|
||||
|
||||
|
||||
func shiftRight(dst, src []Word, n int) {
|
||||
func shiftRight(dst, src []Word, n uint) {
|
||||
if len(src) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ñ := _W - uint(n)
|
||||
ñ := _W - n
|
||||
for i := 0; i < len(src)-1; i++ {
|
||||
dst[i] = src[i] >> uint(n)
|
||||
dst[i] |= src[i+1] << ñ
|
||||
dst[i] = src[i]>>n | src[i+1]<<ñ
|
||||
}
|
||||
dst[len(src)-1] = src[len(src)-1] >> uint(n)
|
||||
dst[len(src)-1] = src[len(src)-1] >> n
|
||||
}
|
||||
|
||||
|
||||
@ -585,7 +588,7 @@ func powersOfTwoDecompose(n []Word) (q []Word, k Word) {
|
||||
x := trailingZeroBits(n[zeroWords])
|
||||
|
||||
q = makeN(nil, len(n)-zeroWords, false)
|
||||
shiftRight(q, n[zeroWords:], x)
|
||||
shiftRight(q, n[zeroWords:], uint(x))
|
||||
q = normN(q)
|
||||
|
||||
k = Word(_W*zeroWords + x)
|
||||
|
@ -131,7 +131,7 @@ func TestLeadingZeroBits(t *testing.T) {
|
||||
|
||||
type shiftTest struct {
|
||||
in []Word
|
||||
shift int
|
||||
shift uint
|
||||
out []Word
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bignum"
|
||||
"big"
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
@ -47,11 +47,14 @@ var n = flag.Int("n", 27, "number of digits")
|
||||
var silent = flag.Bool("s", false, "don't print result")
|
||||
|
||||
var (
|
||||
tmp1 *bignum.Integer
|
||||
tmp2 *bignum.Integer
|
||||
numer = bignum.Int(1)
|
||||
accum = bignum.Int(0)
|
||||
denom = bignum.Int(1)
|
||||
tmp1 = big.NewInt(0)
|
||||
tmp2 = big.NewInt(0)
|
||||
y2 = big.NewInt(0)
|
||||
bigk = big.NewInt(0)
|
||||
numer = big.NewInt(1)
|
||||
accum = big.NewInt(0)
|
||||
denom = big.NewInt(1)
|
||||
ten = big.NewInt(10)
|
||||
)
|
||||
|
||||
func extract_digit() int64 {
|
||||
@ -60,36 +63,39 @@ func extract_digit() int64 {
|
||||
}
|
||||
|
||||
// Compute (numer * 3 + accum) / denom
|
||||
tmp1 = numer.Shl(1)
|
||||
bignum.Iadd(tmp1, tmp1, numer)
|
||||
bignum.Iadd(tmp1, tmp1, accum)
|
||||
tmp1, tmp2 := tmp1.QuoRem(denom)
|
||||
tmp1.Lsh(numer, 1)
|
||||
tmp1.Add(tmp1, numer)
|
||||
tmp1.Add(tmp1, accum)
|
||||
tmp1.DivMod(tmp1, denom, tmp2)
|
||||
|
||||
// Now, if (numer * 4 + accum) % denom...
|
||||
bignum.Iadd(tmp2, tmp2, numer)
|
||||
tmp2.Add(tmp2, numer)
|
||||
|
||||
// ... is normalized, then the two divisions have the same result.
|
||||
if tmp2.Cmp(denom) >= 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return tmp1.Value()
|
||||
return tmp1.Int64()
|
||||
}
|
||||
|
||||
func next_term(k int64) {
|
||||
y2 := k*2 + 1
|
||||
// TODO(eds) If big.Int ever gets a Scale method, y2 and bigk could be int64
|
||||
y2.New(k*2 + 1)
|
||||
bigk.New(k)
|
||||
|
||||
tmp1 = numer.Shl(1)
|
||||
bignum.Iadd(accum, accum, tmp1)
|
||||
bignum.Iscale(accum, y2)
|
||||
bignum.Iscale(numer, k)
|
||||
bignum.Iscale(denom, y2)
|
||||
tmp1.Lsh(numer, 1)
|
||||
accum.Add(accum, tmp1)
|
||||
accum.Mul(accum, y2)
|
||||
numer.Mul(numer, bigk)
|
||||
denom.Mul(denom, y2)
|
||||
}
|
||||
|
||||
func eliminate_digit(d int64) {
|
||||
bignum.Isub(accum, accum, denom.Mul1(d))
|
||||
bignum.Iscale(accum, 10)
|
||||
bignum.Iscale(numer, 10)
|
||||
tmp := big.NewInt(0).Set(denom)
|
||||
accum.Sub(accum, tmp.Mul(tmp, big.NewInt(d)))
|
||||
accum.Mul(accum, ten)
|
||||
numer.Mul(numer, ten)
|
||||
}
|
||||
|
||||
func printf(s string, arg ...interface{}) {
|
||||
|
Loading…
Reference in New Issue
Block a user