First cut at a more realistic multi-precision package:
- implemented low-level operations on word vectors
- implemented corresponding amd64 assembly routines for word vector operations
- implemented first set of operations on unsigned integers
- implemented first set of operations on signed integers
- implemented systematic test cases for each data type
R=rsc
DELTA=1330 (1330 added, 0 deleted, 0 changed)
OCL=33132
CL=33285
diff --git a/src/pkg/big/Makefile b/src/pkg/big/Makefile
new file mode 100644
index 0000000..d98f5b2
--- /dev/null
+++ b/src/pkg/big/Makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=big
+GOFILES=\
+ defs.go\
+ arith.go\
+ big.go\
+ bigN.go\
+ bigZ.go\
+
+OFILES=\
+ arith_$(GOARCH).$O\
+
+include $(GOROOT)/src/Make.pkg
diff --git a/src/pkg/big/arith.go b/src/pkg/big/arith.go
new file mode 100644
index 0000000..45b7a0c
--- /dev/null
+++ b/src/pkg/big/arith.go
@@ -0,0 +1,239 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file provides Go implementations of elementary multi-precision
+// arithmetic operations on word vectors. Needed for platforms without
+// assembly implementations of these routines.
+
+package big
+
+import "unsafe"
+
+
+// ----------------------------------------------------------------------------
+// Elementary operations on words
+
+func addWW_s(x, y, c Word) (z1, z0 Word)
+
+// z1<<_W + z0 = x+y+c, with c == 0 or 1
+func addWW(x, y, c Word) (z1, z0 Word) {
+ yc := y+c;
+ z0 = x+yc;
+ if z0 < x || yc < y {
+ z1 = 1;
+ }
+ return;
+}
+
+
+func subWW_s(x, y, c Word) (z1, z0 Word)
+
+// z1<<_W + z0 = x-y-c, with c == 0 or 1
+func subWW(x, y, c Word) (z1, z0 Word) {
+ yc := y+c;
+ z0 = x-yc;
+ if z0 > x || yc < y {
+ z1 = 1;
+ }
+ return;
+}
+
+
+// z1<<_W + z0 = x*y
+func mulW(x, y Word) (z1, z0 Word) {
+ // Split x and y into 2 halfWords each, multiply
+ // the halfWords separately while avoiding overflow,
+ // and return the product as 2 Words.
+
+ if x < y {
+ x, y = y, x
+ }
+
+ if x < _B2 {
+ // y < _B2 because y <= x
+ // sub-digits of x and y are (0, x) and (0, y)
+ // z = z[0] = x*y
+ z0 = x*y;
+ return;
+ }
+
+ if y < _B2 {
+ // sub-digits of x and y are (x1, x0) and (0, y)
+ // x = (x1*_B2 + x0)
+ // y = (y1*_B2 + y0)
+ x1, x0 := x>>_W2, x&_M2;
+
+ // x*y = t2*_B2*_B2 + t1*_B2 + t0
+ t0 := x0*y;
+ t1 := x1*y;
+
+ // compute result digits but avoid overflow
+ // z = z[1]*_B + z[0] = x*y
+ z0 = t1<<_W2 + t0;
+ z1 = (t1 + t0>>_W2) >> _W2;
+ return;
+ }
+
+ // general case
+ // sub-digits of x and y are (x1, x0) and (y1, y0)
+ // x = (x1*_B2 + x0)
+ // y = (y1*_B2 + y0)
+ x1, x0 := x>>_W2, x&_M2;
+ y1, y0 := y>>_W2, y&_M2;
+
+ // x*y = t2*_B2*_B2 + t1*_B2 + t0
+ t0 := x0*y0;
+ t1 := x1*y0 + x0*y1;
+ t2 := x1*y1;
+
+ // compute result digits but avoid overflow
+ // z = z[1]*_B + z[0] = x*y
+ z0 = t1<<_W2 + t0;
+ z1 = t2 + (t1 + t0>>_W2) >> _W2;
+ return;
+}
+
+
+// z1<<_W + z0 = x*y + c
+func mulAddWW(x, y, c Word) (z1, z0 Word) {
+ // Split x and y into 2 halfWords each, multiply
+ // the halfWords separately while avoiding overflow,
+ // and return the product as 2 Words.
+
+ // TODO(gri) Should implement special cases for faster execution.
+
+ // general case
+ // sub-digits of x, y, and c are (x1, x0), (y1, y0), (c1, c0)
+ // x = (x1*_B2 + x0)
+ // y = (y1*_B2 + y0)
+ x1, x0 := x>>_W2, x&_M2;
+ y1, y0 := y>>_W2, y&_M2;
+ c1, c0 := c>>_W2, c&_M2;
+
+ // x*y + c = t2*_B2*_B2 + t1*_B2 + t0
+ t0 := x0*y0 + c0;
+ t1 := x1*y0 + x0*y1 + c1;
+ t2 := x1*y1;
+
+ // compute result digits but avoid overflow
+ // z = z[1]*_B + z[0] = x*y
+ z0 = t1<<_W2 + t0;
+ z1 = t2 + (t1 + t0>>_W2) >> _W2;
+ return;
+}
+
+
+func divWW_s(x1, x0, y Word) (q, r Word)
+
+// q = (x1<<_W + x0 - r)/y
+func divWW(x1, x0, y Word) (q, r Word) {
+ if x1 == 0 {
+ q, r = x0/y, x0%y;
+ return;
+ }
+
+ // TODO(gri) implement general case w/o assembly code
+ q, r = divWW_s(x1, x0, y);
+ return;
+}
+
+
+// ----------------------------------------------------------------------------
+// Elementary operations on vectors
+
+// For each function f there is a corresponding function f_s which
+// implements the same functionality as f but is written in assembly.
+
+
+func addVV_s(z, x, y *Word, n int) (c Word)
+
+// addVV sets z and returns c such that z+c = x+y.
+// z, x, y are n-word vectors.
+func addVV(z, x, y *Word, n int) (c Word) {
+ for i := 0; i < n; i++ {
+ c, *z = addWW(*x, *y, c);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
+ y = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(y)) + _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
+
+ }
+ return
+}
+
+
+func subVV_s(z, x, y *Word, n int) (c Word)
+
+// subVV sets z and returns c such that z-c = x-y.
+// z, x, y are n-word vectors.
+func subVV(z, x, y *Word, n int) (c Word) {
+ for i := 0; i < n; i++ {
+ c, *z = subWW(*x, *y, c);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
+ y = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(y)) + _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
+ }
+ return
+}
+
+
+func addVW_s(z, x *Word, y Word, n int) (c Word)
+
+// addVW sets z and returns c such that z+c = x-y.
+// z, x are n-word vectors.
+func addVW(z, x *Word, y Word, n int) (c Word) {
+ c = y;
+ for i := 0; i < n; i++ {
+ c, *z = addWW(*x, c, 0);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
+
+ }
+ return
+}
+
+func subVW_s(z, x *Word, y Word, n int) (c Word)
+
+// subVW sets z and returns c such that z-c = x-y.
+// z, x are n-word vectors.
+func subVW(z, x *Word, y Word, n int) (c Word) {
+ c = y;
+ for i := 0; i < n; i++ {
+ c, *z = subWW(*x, c, 0);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
+
+ }
+ return
+}
+
+
+func mulVW_s(z, x *Word, y Word, n int) (c Word)
+
+// mulVW sets z and returns c such that z+c = x*y.
+// z, x are n-word vectors.
+func mulVW(z, x *Word, y Word, n int) (c Word) {
+ for i := 0; i < n; i++ {
+ c, *z = mulAddWW(*x, y, c);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
+ }
+ return
+}
+
+
+func divWVW_s(z* Word, xn Word, x *Word, y Word, n int) (r Word)
+
+// divWVW sets z and returns r such that z-r = (xn<<(n*_W) + x) / y.
+// z, x are n-word vectors; xn is the extra word x[n] of x.
+func divWVW(z* Word, xn Word, x *Word, y Word, n int) (r Word) {
+ r = xn;
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + uintptr(n-1)*_S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + uintptr(n-1)*_S)));
+ for i := n-1; i >= 0; i-- {
+ *z, r = divWW(r, *x, y);
+ x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) - _S)));
+ z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) - _S)));
+ }
+ return;
+}
diff --git a/src/pkg/big/arith_amd64.s b/src/pkg/big/arith_amd64.s
new file mode 100644
index 0000000..0853846
--- /dev/null
+++ b/src/pkg/big/arith_amd64.s
@@ -0,0 +1,219 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file provides fast assembly versions of the routines in arith.go.
+//
+// Note: Eventually, these functions should be named like their corresponding
+// Go implementations. For now their names have "_s" appended so that
+// they can be linked and tested together.
+
+// ----------------------------------------------------------------------------
+// Elementary operations on words
+
+// func addWW_s(x, y, c Word) (z1, z0 Word)
+// z1<<_W + z0 = x+y+c, with c == 0 or 1
+TEXT big·addWW_s(SB),7,$0
+ MOVQ a+0(FP), AX
+ XORQ DX, DX
+ ADDQ a+8(FP), AX
+ ADCQ $0, DX
+ ADDQ a+16(FP), AX
+ ADCQ $0, DX
+ MOVQ DX, a+24(FP)
+ MOVQ AX, a+32(FP)
+ RET
+
+
+// func subWW_s(x, y, c Word) (z1, z0 Word)
+// z1<<_W + z0 = x-y-c, with c == 0 or 1
+TEXT big·subWW_s(SB),7,$0
+ MOVQ a+0(FP), AX
+ XORQ DX, DX
+ SUBQ a+8(FP), AX
+ ADCQ $0, DX
+ SUBQ a+16(FP), AX
+ ADCQ $0, DX
+ MOVQ DX, a+24(FP)
+ MOVQ AX, a+32(FP)
+ RET
+
+
+// func mulWW_s(x, y Word) (z1, z0 Word)
+// z1<<64 + z0 = x*y
+//
+TEXT big·mulWW_s(SB),7,$0
+ MOVQ a+0(FP), AX
+ MULQ a+8(FP)
+ MOVQ DX, a+16(FP)
+ MOVQ AX, a+24(FP)
+ RET
+
+
+// func mulAddWW_s(x, y, c Word) (z1, z0 Word)
+// z1<<64 + z0 = x*y + c
+//
+TEXT big·mulAddWW_s(SB),7,$0
+ MOVQ a+0(FP), AX
+ MULQ a+8(FP)
+ ADDQ a+16(FP), AX
+ ADCQ $0, DX
+ MOVQ DX, a+24(FP)
+ MOVQ AX, a+32(FP)
+ RET
+
+
+// func divWW_s(x1, x0, y Word) (q, r Word)
+// q = (x1<<64 + x0)/y + r
+//
+TEXT big·divWW_s(SB),7,$0
+ MOVQ a+0(FP), DX
+ MOVQ a+8(FP), AX
+ DIVQ a+16(FP)
+ MOVQ AX, a+24(FP)
+ MOVQ DX, a+32(FP)
+ RET
+
+
+// ----------------------------------------------------------------------------
+// Elementary operations on vectors
+
+// TODO(gri) - experiment with unrolled loops for faster execution
+
+// func addVV_s(z, x, y *Word, n int) (c Word)
+TEXT big·addVV_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), R8 // x
+ MOVQ a+16(FP), R9 // y
+ MOVL a+24(FP), R11 // n
+ XORQ BX, BX // i = 0
+ XORQ DX, DX // c = 0
+ JMP E1
+
+L1: MOVQ (R8)(BX*8), AX
+ RCRQ $1, DX
+ ADCQ (R9)(BX*8), AX
+ RCLQ $1, DX
+ MOVQ AX, (R10)(BX*8)
+ ADDL $1, BX // i++
+
+E1: CMPQ BX, R11 // i < n
+ JL L1
+
+ MOVQ DX, a+32(FP) // return c
+ RET
+
+
+// func subVV_s(z, x, y *Word, n int) (c Word)
+// (same as addVV_s except for SBBQ instead of ADCQ and label names)
+TEXT big·subVV_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), R8 // x
+ MOVQ a+16(FP), R9 // y
+ MOVL a+24(FP), R11 // n
+ XORQ BX, BX // i = 0
+ XORQ DX, DX // c = 0
+ JMP E2
+
+L2: MOVQ (R8)(BX*8), AX
+ RCRQ $1, DX
+ SBBQ (R9)(BX*8), AX
+ RCLQ $1, DX
+ MOVQ AX, (R10)(BX*8)
+ ADDL $1, BX // i++
+
+E2: CMPQ BX, R11 // i < n
+ JL L2
+
+ MOVQ DX, a+32(FP) // return c
+ RET
+
+
+// func addVW_s(z, x *Word, y Word, n int) (c Word)
+TEXT big·addVW_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), R8 // x
+ MOVQ a+16(FP), AX // c = y
+ MOVL a+24(FP), R11 // n
+ XORQ BX, BX // i = 0
+ JMP E3
+
+L3: ADDQ (R8)(BX*8), AX
+ MOVQ AX, (R10)(BX*8)
+ RCLQ $1, AX
+ ANDQ $1, AX
+ ADDL $1, BX // i++
+
+E3: CMPQ BX, R11 // i < n
+ JL L3
+
+ MOVQ AX, a+32(FP) // return c
+ RET
+
+
+// func subVW_s(z, x *Word, y Word, n int) (c Word)
+TEXT big·subVW_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), R8 // x
+ MOVQ a+16(FP), AX // c = y
+ MOVL a+24(FP), R11 // n
+ XORQ BX, BX // i = 0
+ JMP E4
+
+L4: MOVQ (R8)(BX*8), DX // TODO(gri) is there a reverse SUBQ?
+ SUBQ AX, DX
+ MOVQ DX, (R10)(BX*8)
+ RCLQ $1, AX
+ ANDQ $1, AX
+ ADDL $1, BX // i++
+
+E4: CMPQ BX, R11 // i < n
+ JL L4
+
+ MOVQ AX, a+32(FP) // return c
+ RET
+
+
+// func mulVW_s(z, x *Word, y Word, n int) (c Word)
+TEXT big·mulVW_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), R8 // x
+ MOVQ a+16(FP), R9 // y
+ MOVL a+24(FP), R11 // n
+ XORQ BX, BX // i = 0
+ XORQ CX, CX // c = 0
+ JMP E5
+
+L5: MOVQ (R8)(BX*8), AX
+ MULQ R9
+ ADDQ CX, AX
+ ADCQ $0, DX
+ MOVQ AX, (R10)(BX*8)
+ MOVQ DX, CX
+ ADDL $1, BX // i++
+
+E5: CMPQ BX, R11 // i < n
+ JL L5
+
+ MOVQ CX, a+32(FP) // return c
+ RET
+
+
+// divWVW_s(z* Word, xn Word, x *Word, y Word, n int) (r Word)
+TEXT big·divWVW_s(SB),7,$0
+ MOVQ a+0(FP), R10 // z
+ MOVQ a+8(FP), DX // r = xn
+ MOVQ a+16(FP), R8 // x
+ MOVQ a+24(FP), R9 // y
+ MOVL a+32(FP), BX // i = n
+ JMP E6
+
+L6: MOVQ (R8)(BX*8), AX
+ DIVQ R9
+ MOVQ AX, (R10)(BX*8)
+
+E6: SUBL $1, BX
+ JGE L6
+
+ MOVQ DX, a+40(FP) // return r
+ RET
diff --git a/src/pkg/big/arith_test.go b/src/pkg/big/arith_test.go
new file mode 100644
index 0000000..0544fa7
--- /dev/null
+++ b/src/pkg/big/arith_test.go
@@ -0,0 +1,213 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import "testing"
+
+
+type funWW func(x, y, c Word) (z1, z0 Word)
+type argWW struct { x, y, c, z1, z0 Word }
+
+var sumWW = []argWW{
+ argWW{0, 0, 0, 0, 0},
+ argWW{0, 1, 0, 0, 1},
+ argWW{0, 0, 1, 0, 1},
+ argWW{0, 1, 1, 0, 2},
+ argWW{12345, 67890, 0, 0, 80235},
+ argWW{12345, 67890, 1, 0, 80236},
+ argWW{_M, 1, 0, 1, 0},
+ argWW{_M, 0, 1, 1, 0},
+ argWW{_M, 1, 1, 1, 1},
+ argWW{_M, _M, 0, 1, _M-1},
+ argWW{_M, _M, 1, 1, _M},
+}
+
+
+func testFunWW(t *testing.T, msg string, f funWW, a argWW) {
+ z1, z0 := f(a.x, a.y, a.c);
+ if z1 != a.z1 || z0 != a.z0 {
+ t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0);
+ }
+}
+
+
+func TestFunWW(t *testing.T) {
+ for _, a := range sumWW {
+ arg := a;
+ testFunWW(t, "addWW", addWW, arg);
+ testFunWW(t, "addWW_s", addWW_s, arg);
+
+ arg = argWW{a.y, a.x, a.c, a.z1, a.z0};
+ testFunWW(t, "addWW symmetric", addWW, arg);
+ testFunWW(t, "addWW_s symmetric", addWW_s, arg);
+
+ arg = argWW{a.z0, a.x, a.c, a.z1, a.y};
+ testFunWW(t, "subWW", subWW, arg);
+ testFunWW(t, "subWW_s", subWW_s, arg);
+
+ arg = argWW{a.z0, a.y, a.c, a.z1, a.x};
+ testFunWW(t, "subWW symmetric", subWW, arg);
+ testFunWW(t, "subWW_s symmetric", subWW_s, arg);
+ }
+}
+
+
+func addr(x []Word) *Word {
+ if len(x) == 0 {
+ return nil;
+ }
+ return &x[0];
+}
+
+
+type funVV func(z, x, y *Word, n int) (c Word)
+type argVV struct { z, x, y []Word; c Word }
+
+var sumVV = []argVV{
+ argVV{},
+ argVV{[]Word{0}, []Word{0}, []Word{0}, 0},
+ argVV{[]Word{1}, []Word{1}, []Word{0}, 0},
+ argVV{[]Word{0}, []Word{_M}, []Word{1}, 1},
+ argVV{[]Word{80235}, []Word{12345}, []Word{67890}, 0},
+ argVV{[]Word{_M-1}, []Word{_M}, []Word{_M}, 1},
+ argVV{[]Word{0, 0, 0, 0}, []Word{_M, _M, _M, _M}, []Word{1, 0, 0, 0}, 1},
+ argVV{[]Word{0, 0, 0, _M}, []Word{_M, _M, _M, _M-1}, []Word{1, 0, 0, 0}, 0},
+ argVV{[]Word{0, 0, 0, 0}, []Word{_M, 0, _M, 0}, []Word{1, _M, 0, _M}, 1},
+}
+
+
+func testFunVV(t *testing.T, msg string, f funVV, a argVV) {
+ n := len(a.z);
+ z := make([]Word, n);
+ c := f(addr(z), addr(a.x), addr(a.y), n);
+ for i, zi := range z {
+ if zi != a.z[i] {
+ t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]);
+ break;
+ }
+ }
+ if c != a.c {
+ t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c);
+ }
+}
+
+
+func TestFunVV(t *testing.T) {
+ for _, a := range sumVV {
+ arg := a;
+ testFunVV(t, "addVV", addVV, arg);
+ testFunVV(t, "addVV_s", addVV_s, arg);
+
+ arg = argVV{a.z, a.y, a.x, a.c};
+ testFunVV(t, "addVV symmetric", addVV, arg);
+ testFunVV(t, "addVV_s symmetric", addVV_s, arg);
+
+ arg = argVV{a.x, a.z, a.y, a.c};
+ testFunVV(t, "subVV", subVV, arg);
+ testFunVV(t, "subVV_s", subVV_s, arg);
+
+ arg = argVV{a.y, a.z, a.x, a.c};
+ testFunVV(t, "subVV symmetric", subVV, arg);
+ testFunVV(t, "subVV_s symmetric", subVV_s, arg);
+ }
+}
+
+
+type funVW func(z, x *Word, y Word, n int) (c Word)
+type argVW struct { z, x []Word; y Word; c Word }
+
+var sumVW = []argVW{
+ argVW{},
+ argVW{[]Word{0}, []Word{0}, 0, 0},
+ argVW{[]Word{1}, []Word{0}, 1, 0},
+ argVW{[]Word{1}, []Word{1}, 0, 0},
+ argVW{[]Word{0}, []Word{_M}, 1, 1},
+ argVW{[]Word{0, 0, 0, 0}, []Word{_M, _M, _M, _M}, 1, 1},
+}
+
+var prodVW = []argVW{
+ argVW{},
+ argVW{[]Word{0}, []Word{0}, 0, 0},
+ argVW{[]Word{0}, []Word{_M}, 0, 0},
+ argVW{[]Word{0}, []Word{0}, _M, 0},
+ argVW{[]Word{1}, []Word{1}, 1, 0},
+ argVW{[]Word{22793}, []Word{991}, 23, 0},
+ argVW{[]Word{0, 0, 0, 22793}, []Word{0, 0, 0, 991}, 23, 0},
+ argVW{[]Word{0, 0, 0, 0}, []Word{7893475, 7395495, 798547395, 68943}, 0, 0},
+ argVW{[]Word{0, 0, 0, 0}, []Word{0, 0, 0, 0}, 894375984, 0},
+ argVW{[]Word{_M<<1 & _M}, []Word{_M}, 1<<1, _M>>(_W-1)},
+ argVW{[]Word{_M<<7 & _M}, []Word{_M}, 1<<7, _M>>(_W-7)},
+ argVW{[]Word{_M<<7 & _M, _M, _M, _M}, []Word{_M, _M, _M, _M}, 1<<7, _M>>(_W-7)},
+}
+
+
+func testFunVW(t *testing.T, msg string, f funVW, a argVW) {
+ n := len(a.z);
+ z := make([]Word, n);
+ c := f(addr(z), addr(a.x), a.y, n);
+ for i, zi := range z {
+ if zi != a.z[i] {
+ t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]);
+ break;
+ }
+ }
+ if c != a.c {
+ t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c);
+ }
+}
+
+
+func TestFunVW(t *testing.T) {
+ for _, a := range sumVW {
+ arg := a;
+ testFunVW(t, "addVW", addVW, arg);
+ testFunVW(t, "addVW_s", addVW_s, arg);
+
+ arg = argVW{a.x, a.z, a.y, a.c};
+ testFunVW(t, "subVW", subVW, arg);
+ testFunVW(t, "subVW_s", subVW_s, arg);
+ }
+
+ for _, a := range prodVW {
+ arg := a;
+ testFunVW(t, "mulVW", mulVW, arg);
+ testFunVW(t, "mulVW_s", mulVW_s, arg);
+ }
+}
+
+
+// TODO(gri) Vector mul and div are not quite symmetric.
+// make it symmetric, mulVW should become mulAddVWW.
+// Correct decision may become obvious after implementing
+// the higher-level routines.
+
+type funWVW func(z* Word, xn Word, x *Word, y Word, n int) (r Word)
+type argWVW struct { z []Word; xn Word; x []Word; y Word; r Word }
+
+func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) {
+ n := len(a.z);
+ z := make([]Word, n);
+ r := f(addr(z), a.xn, addr(a.x), a.y, n);
+ for i, zi := range z {
+ if zi != a.z[i] {
+ t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]);
+ break;
+ }
+ }
+ if r != a.r {
+ t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r);
+ }
+}
+
+
+func TestFunVWW(t *testing.T) {
+ for _, a := range prodVW {
+ if a.y != 0 {
+ arg := argWVW{a.x, a.c, a.z, a.y, 0};
+ testFunWVW(t, "divWVW", divWVW, arg);
+ testFunWVW(t, "divWVW_s", divWVW_s, arg);
+ }
+ }
+}
diff --git a/src/pkg/big/big.go b/src/pkg/big/big.go
new file mode 100644
index 0000000..4c175f2
--- /dev/null
+++ b/src/pkg/big/big.go
@@ -0,0 +1,28 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// A package for multi-precision arithmetic.
+// It implements the following numeric types:
+//
+// W unsigned single word with limited precision (uintptr)
+// Z signed integers
+// Q rational numbers
+//
+// Operations follow a regular naming scheme: The
+// operation name is followed by the type names of
+// the operands. Examples:
+//
+// AddWW implements W + W
+// SubZZ implements Z + Z
+// MulZW implements Z * W
+//
+// All operations returning a multi-precision result take the
+// result as the first argument; if it is one of the operands
+// it may be overwritten (and its memory reused). To enable
+// chaining of operations, the result is also returned.
+//
+package big
+
+// This file is intentionally left without declarations for now. It may
+// contain more documentation eventually; otherwise it should be removed.
diff --git a/src/pkg/big/bigN.go b/src/pkg/big/bigN.go
new file mode 100644
index 0000000..50d73a7
--- /dev/null
+++ b/src/pkg/big/bigN.go
@@ -0,0 +1,315 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains operations on unsigned multi-precision integers.
+// These are the building blocks for the operations on signed integers
+// and rationals.
+
+package big
+
+// 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]
+//
+// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n,
+// with the digits x[i] as the slice elements.
+//
+// A number is normalized if the slice contains no leading 0 digits.
+// During arithmetic operations, denormalized values may occur but are
+// always normalized before returning the final result. The normalized
+// representation of 0 is the empty or nil slice (length = 0).
+
+func normN(z []Word) []Word {
+ i := len(z);
+ for i > 0 && z[i-1] == 0 {
+ i--;
+ }
+ z = z[0 : i];
+ return z;
+}
+
+
+func makeN(z []Word, m int) []Word {
+ if len(z) > m {
+ z = z[0 : m]; // has at least one extra word for a carry, if any
+ return z; // reuse z
+ }
+ c := 4; // minimum capacity
+ if m > c {
+ c = m;
+ }
+ return make([]Word, m, c+1); // +1: extra word for a carry, if any
+}
+
+
+func newN(z []Word, x uint64) []Word {
+ if x == 0 {
+ return nil; // len == 0
+ }
+
+ // single-digit values
+ if x == uint64(Word(x)) {
+ z = makeN(z, 1);
+ z[0] = Word(x);
+ return z;
+ }
+
+ // compute number of words n required to represent x
+ n := 0;
+ for t := x; t > 0; t >>= _W {
+ n++;
+ }
+
+ // split x into n words
+ z = makeN(z, n);
+ for i := 0; i < n; i++ {
+ z[i] = Word(x & _M);
+ x >>= _W;
+ }
+
+ return z;
+}
+
+
+func setN(z, x []Word) []Word {
+ z = makeN(z, len(x));
+ for i, d := range x {
+ z[i] = d;
+ }
+ return z;
+}
+
+
+func addNN(z, x, y []Word) []Word {
+ m := len(x);
+ n := len(y);
+
+ switch {
+ case m < n:
+ return addNN(z, y, x);
+ case m == 0:
+ // n == 0 because m >= n; result is 0
+ return makeN(z, 0);
+ case n == 0:
+ // result is x
+ return setN(z, x);
+ }
+
+ z = makeN(z, m);
+ c := addVV(&z[0], &x[0], &y[0], n);
+ if m > n {
+ c = addVW(&z[n], &x[n], c, m-n);
+ }
+ if c > 0 {
+ z = z[0 : m+1];
+ z[m] = c;
+ }
+
+ return z;
+}
+
+
+func subNN(z, x, y []Word) []Word {
+ m := len(x);
+ n := len(y);
+
+ switch {
+ case m < n:
+ panic("underflow");
+ case m == 0:
+ // n == 0 because m >= n; result is 0
+ return makeN(z, 0);
+ case n == 0:
+ // result is x
+ return setN(z, x);
+ }
+
+ z = makeN(z, m);
+ c := subVV(&z[0], &x[0], &y[0], n);
+ if m > n {
+ c = subVW(&z[n], &x[n], c, m-n);
+ }
+ if c != 0 {
+ panic("underflow");
+ }
+
+ z = normN(z);
+ return z;
+}
+
+
+func cmpNN(x, y []Word) int {
+ m := len(x);
+ n := len(y);
+ if m != n || m == 0 {
+ return m-n;
+ }
+
+ i := m-1;
+ for i > 0 && x[i] == y[i] {
+ i--;
+ }
+
+ z := 0;
+ switch {
+ case x[i] < y[i]: z = -1;
+ case x[i] > y[i]: z = 1;
+ }
+ return z;
+}
+
+
+func mulNW(z, x []Word, y Word) []Word {
+ m := len(x);
+ switch {
+ case m == 0 || y == 0:
+ return setN(z, nil); // result is 0
+ case y == 1:
+ return setN(z, x); // result is x
+ }
+ // m > 0
+ z = makeN(z, m+1);
+ c := mulVW(&z[0], &x[0], y, m);
+ if c > 0 {
+ z = z[0 : m+1];
+ z[m] = c;
+ }
+ return z;
+}
+
+
+func mulNN(z, x, y []Word) []Word {
+ panic("mulNN unimplemented");
+ return z
+}
+
+
+// q = (x-r)/y, with 0 <= r < y
+func divNW(z, x []Word, y Word) (q []Word, r Word) {
+ m := len(x);
+ switch {
+ case y == 0:
+ panic("division by zero");
+ case y == 1:
+ q = setN(z, x); // result is x
+ return;
+ case m == 0:
+ q = setN(z, nil); // result is 0
+ return;
+ }
+ // m > 0
+ z = makeN(z, m);
+ r = divWVW(&z[0], 0, &x[0], y, m);
+ q = normN(z);
+ return;
+}
+
+
+// log2 computes the binary logarithm of x.
+// The result is the integer n for which 2^n <= x < 2^(n+1).
+// If x == 0, the result is < 0.
+func log2(x Word) int {
+ n := 0;
+ for ; x > 0; x >>= 1 {
+ n++;
+ }
+ return n-1;
+}
+
+
+// log2N computes the binary logarithm of x.
+// The result is the integer n for which 2^n <= x < 2^(n+1).
+// If x == 0, the result is < 0.
+func log2N(x []Word) int {
+ m := len(x);
+ if m > 0 {
+ return (m-1)*int(_W) + log2(x[m-1]);
+ }
+ return -1;
+}
+
+
+func hexValue(ch byte) int {
+ var d byte;
+ switch {
+ case '0' <= ch && ch <= '9': d = ch - '0';
+ case 'a' <= ch && ch <= 'f': d = ch - 'a' + 10;
+ case 'A' <= ch && ch <= 'F': d = ch - 'A' + 10;
+ default: return -1;
+ }
+ return int(d);
+}
+
+
+// scanN returns the natural number corresponding to the
+// longest possible prefix of s representing a natural number in a
+// given conversion base, the actual conversion base used, and the
+// prefix length. The syntax of natural numbers follows the syntax
+// of unsigned integer literals in Go.
+//
+// If the base argument is 0, the string prefix determines the actual
+// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the
+// ``0'' prefix selects base 8. Otherwise the selected base is 10.
+//
+func scanN(z []Word, s string, base int) ([]Word, int, int) {
+ // determine base if necessary
+ i, n := 0, len(s);
+ if base == 0 {
+ base = 10;
+ if n > 0 && s[0] == '0' {
+ if n > 1 && (s[1] == 'x' || s[1] == 'X') {
+ base, i = 16, 2;
+ } else {
+ base, i = 8, 1;
+ }
+ }
+ }
+ if base < 2 || 16 < base {
+ panic("illegal base");
+ }
+
+ // convert string
+ z = makeN(z, len(z));
+ for ; i < n; i++ {
+ d := hexValue(s[i]);
+ if 0 <= d && d < base {
+ panic("scanN needs mulAddVWW");
+ } else {
+ break;
+ }
+ }
+
+ return z, base, i;
+}
+
+
+// string converts x to a string for a given base, with 2 <= base <= 16.
+// TODO(gri) in the style of the other routines, perhaps this should take
+// a []byte buffer and return it
+func stringN(x []Word, base int) string {
+ if base < 2 || 16 < base {
+ panic("illegal base");
+ }
+
+ if len(x) == 0 {
+ return "0";
+ }
+
+ // allocate buffer for conversion
+ i := (log2N(x) + 1) / log2(Word(base)) + 1; // +1: round up
+ s := make([]byte, i);
+
+ // don't destroy x
+ q := setN(nil, x);
+
+ // convert
+ for len(q) > 0 {
+ i--;
+ var r Word;
+ q, r = divNW(q, q, 10);
+ s[i] = "0123456789abcdef"[r];
+ };
+
+ return string(s[i : len(s)]);
+}
diff --git a/src/pkg/big/bigN_test.go b/src/pkg/big/bigN_test.go
new file mode 100644
index 0000000..48b78c4
--- /dev/null
+++ b/src/pkg/big/bigN_test.go
@@ -0,0 +1,77 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import "testing"
+
+func TestCmpNN(t *testing.T) {
+ // TODO(gri) write this test - all other tests depends on it
+}
+
+
+type funNN func(z, x, y []Word) []Word
+type argNN struct { z, x, y []Word }
+
+var sumNN = []argNN{
+ argNN{},
+ argNN{[]Word{1}, nil, []Word{1}},
+ argNN{[]Word{1111111110}, []Word{123456789}, []Word{987654321}},
+ argNN{[]Word{0, 0, 0, 1}, nil, []Word{0, 0, 0, 1}},
+ argNN{[]Word{0, 0, 0, 1111111110}, []Word{0, 0, 0, 123456789}, []Word{0, 0, 0, 987654321}},
+ argNN{[]Word{0, 0, 0, 1}, []Word{0, 0, _M}, []Word{0, 0, 1}},
+}
+
+
+func TestSetN(t *testing.T) {
+ for _, a := range sumNN {
+ z := setN(nil, a.z);
+ if cmpNN(z, a.z) != 0 {
+ t.Errorf("got z = %v; want %v", z, a.z);
+ }
+ }
+}
+
+
+func testFunNN(t *testing.T, msg string, f funNN, a argNN) {
+ z := f(nil, a.x, a.y);
+ if cmpNN(z, a.z) != 0 {
+ t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z);
+ }
+}
+
+
+func TestFunNN(t *testing.T) {
+ for _, a := range sumNN {
+ arg := a;
+ testFunNN(t, "addNN", addNN, arg);
+
+ arg = argNN{a.z, a.y, a.x};
+ testFunNN(t, "addNN symmetric", addNN, arg);
+
+ arg = argNN{a.x, a.z, a.y};
+ testFunNN(t, "subNN", subNN, arg);
+
+ arg = argNN{a.y, a.z, a.x};
+ testFunNN(t, "subNN symmetric", subNN, arg);
+ }
+}
+
+
+type strN struct { x []Word; b int; s string }
+var tabN = []strN{
+ strN{nil, 10, "0"},
+ strN{[]Word{1}, 10, "1"},
+ strN{[]Word{10}, 10, "10"},
+ strN{[]Word{1234567890}, 10, "1234567890"},
+}
+
+func TestStringN(t *testing.T) {
+ for _, a := range tabN {
+ s := stringN(a.x, a.b);
+ if s != a.s {
+ t.Errorf("stringN%+v\n\tgot s = %s; want %s", a, s, a.s);
+ }
+ }
+}
diff --git a/src/pkg/big/bigZ.go b/src/pkg/big/bigZ.go
new file mode 100644
index 0000000..03534ec
--- /dev/null
+++ b/src/pkg/big/bigZ.go
@@ -0,0 +1,139 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements signed multi-precision integers.
+
+package big
+
+// A Z represents a signed multi-precision integer.
+// The zero value for a Z represents the value 0.
+type Z struct {
+ neg bool; // sign
+ m []Word; // mantissa
+}
+
+
+// NewZ sets z to x.
+func NewZ(z Z, x int64) Z {
+ z.neg = false;
+ if x < 0 {
+ z.neg = true;
+ x = -x;
+ }
+ z.m = newN(z.m, uint64(x));
+ return z;
+}
+
+
+// SetZ sets z to x.
+func SetZ(z, x Z) Z {
+ z.neg = x.neg;
+ z.m = setN(z.m, x.m);
+ return z;
+}
+
+
+// AddZZ computes z = x+y.
+func AddZZ(z, x, y Z) Z {
+ if x.neg == y.neg {
+ // x + y == x + y
+ // (-x) + (-y) == -(x + y)
+ z.neg = x.neg;
+ z.m = addNN(z.m, x.m, y.m);
+ } else {
+ // x + (-y) == x - y == -(y - x)
+ // (-x) + y == y - x == -(x - y)
+ if cmpNN(x.m, y.m) >= 0 {
+ z.neg = x.neg;
+ z.m = subNN(z.m, x.m, y.m);
+ } else {
+ z.neg = !x.neg;
+ z.m = subNN(z.m, y.m, x.m);
+ }
+ }
+ if len(z.m) == 0 {
+ z.neg = false; // 0 has no sign
+ }
+ return z
+}
+
+
+// AddZZ computes z = x-y.
+func SubZZ(z, x, y Z) Z {
+ if x.neg != y.neg {
+ // x - (-y) == x + y
+ // (-x) - y == -(x + y)
+ z.neg = x.neg;
+ z.m = addNN(z.m, x.m, y.m);
+ } else {
+ // x - y == x - y == -(y - x)
+ // (-x) - (-y) == y - x == -(x - y)
+ if cmpNN(x.m, y.m) >= 0 {
+ z.neg = x.neg;
+ z.m = subNN(z.m, x.m, y.m);
+ } else {
+ z.neg = !x.neg;
+ z.m = subNN(z.m, y.m, x.m);
+ }
+ }
+ if len(z.m) == 0 {
+ z.neg = false; // 0 has no sign
+ }
+ return z
+}
+
+
+// MulZZ computes z = x*y.
+func MulZZ(z, x, y Z) Z {
+ // x * y == x * y
+ // x * (-y) == -(x * y)
+ // (-x) * y == -(x * y)
+ // (-x) * (-y) == x * y
+ z.neg = x.neg != y.neg;
+ z.m = mulNN(z.m, x.m, y.m);
+ return z
+}
+
+
+// NegZ computes z = -x.
+func NegZ(z, x Z) Z {
+ z.neg = len(x.m) > 0 && !x.neg; // 0 has no sign
+ z.m = setN(z.m, x.m);
+ return z;
+}
+
+
+// Cmp compares x and y. The result is an int value that is
+//
+// < 0 if x < y
+// == 0 if x == y
+// > 0 if x > y
+//
+func CmpZZ(x, y Z) (r int) {
+ // x cmp y == x cmp y
+ // x cmp (-y) == x
+ // (-x) cmp y == y
+ // (-x) cmp (-y) == -(x cmp y)
+ switch {
+ case x.neg == y.neg:
+ r = cmpNN(x.m, y.m);
+ if x.neg {
+ r = -r;
+ }
+ case x.neg:
+ r = -1;
+ default:
+ r = 1;
+ }
+ return;
+}
+
+
+func (x Z) String() string {
+ s := "";
+ if x.neg {
+ s = "-";
+ }
+ return s + stringN(x.m, 10);
+}
diff --git a/src/pkg/big/bigZ_test.go b/src/pkg/big/bigZ_test.go
new file mode 100644
index 0000000..ed6f4ff
--- /dev/null
+++ b/src/pkg/big/bigZ_test.go
@@ -0,0 +1,63 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import "testing"
+
+
+func newZ(x int64) Z {
+ var z Z;
+ return NewZ(z, x);
+}
+
+
+type funZZ func(z, x, y Z) Z
+type argZZ struct { z, x, y Z }
+
+var sumZZ = []argZZ{
+ argZZ{newZ(0), newZ(0), newZ(0)},
+ argZZ{newZ(1), newZ(1), newZ(0)},
+ argZZ{newZ(1111111110), newZ(123456789), newZ(987654321)},
+ argZZ{newZ(-1), newZ(-1), newZ(0)},
+ argZZ{newZ(864197532), newZ(-123456789), newZ(987654321)},
+ argZZ{newZ(-1111111110), newZ(-123456789), newZ(-987654321)},
+}
+
+
+func TestSetZ(t *testing.T) {
+ for _, a := range sumZZ {
+ var z Z;
+ z = SetZ(z, a.z);
+ if CmpZZ(z, a.z) != 0 {
+ t.Errorf("got z = %v; want %v", z, a.z);
+ }
+ }
+}
+
+
+func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
+ var z Z;
+ z = f(z, a.x, a.y);
+ if CmpZZ(z, a.z) != 0 {
+ t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z);
+ }
+}
+
+
+func TestFunZZ(t *testing.T) {
+ for _, a := range sumZZ {
+ arg := a;
+ testFunZZ(t, "AddZZ", AddZZ, arg);
+
+ arg = argZZ{a.z, a.y, a.x};
+ testFunZZ(t, "AddZZ symmetric", AddZZ, arg);
+
+ arg = argZZ{a.x, a.z, a.y};
+ testFunZZ(t, "SubZZ", SubZZ, arg);
+
+ arg = argZZ{a.y, a.z, a.x};
+ testFunZZ(t, "SubZZ symmetric", SubZZ, arg);
+ }
+}
diff --git a/src/pkg/big/defs.go b/src/pkg/big/defs.go
new file mode 100644
index 0000000..5972fa6
--- /dev/null
+++ b/src/pkg/big/defs.go
@@ -0,0 +1,19 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import "unsafe"
+
+type Word uintptr
+
+const (
+ _S = uintptr(unsafe.Sizeof(Word)); // TODO(gri) should Sizeof return a uintptr?
+ _W = _S*8;
+ _B = 1<<_W;
+ _M = _B-1;
+ _W2 = _W/2;
+ _B2 = 1<<_W2;
+ _M2 = _B2-1;
+)