1
0
mirror of https://github.com/golang/go synced 2024-11-26 02:27:56 -07:00

snapshot:

- renamed Z -> Int
- made Int ops methods on *Int
- "install" assembly routines dynamically
- replace mulVW functions with mulAddVWW
  of equivalent performance but symmetric functionality
  to divWVW
- implemented scanN

status:
- need mulNN (trivial)
- need division/modulo after which the set of
  elementary operations is complete
- to/from string conversion working

R=rsc
DELTA=320  (124 added, 50 deleted, 146 changed)
OCL=33308
CL=33341
This commit is contained in:
Robert Griesemer 2009-08-15 11:43:54 -07:00
parent b21425ddee
commit e5874223ef
7 changed files with 248 additions and 174 deletions

View File

@ -13,11 +13,13 @@ import "unsafe"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Elementary operations on words // Elementary operations on words
//
// These operations are used by the vector operations below.
func addWW_s(x, y, c Word) (z1, z0 Word) func addWW_s(x, y, c Word) (z1, z0 Word)
// z1<<_W + z0 = x+y+c, with c == 0 or 1 // z1<<_W + z0 = x+y+c, with c == 0 or 1
func addWW(x, y, c Word) (z1, z0 Word) { func addWW_g(x, y, c Word) (z1, z0 Word) {
yc := y+c; yc := y+c;
z0 = x+yc; z0 = x+yc;
if z0 < x || yc < y { if z0 < x || yc < y {
@ -30,7 +32,7 @@ func addWW(x, y, c Word) (z1, z0 Word) {
func subWW_s(x, y, c Word) (z1, z0 Word) func subWW_s(x, y, c Word) (z1, z0 Word)
// z1<<_W + z0 = x-y-c, with c == 0 or 1 // z1<<_W + z0 = x-y-c, with c == 0 or 1
func subWW(x, y, c Word) (z1, z0 Word) { func subWW_g(x, y, c Word) (z1, z0 Word) {
yc := y+c; yc := y+c;
z0 = x-yc; z0 = x-yc;
if z0 > x || yc < y { if z0 > x || yc < y {
@ -40,8 +42,12 @@ func subWW(x, y, c Word) (z1, z0 Word) {
} }
// TODO(gri) mulWW_g is not needed anymore. Keep around for
// now since mulAddWWW_g should use some of the
// optimizations from mulWW_g eventually.
// z1<<_W + z0 = x*y // z1<<_W + z0 = x*y
func mulW(x, y Word) (z1, z0 Word) { func mulWW_g(x, y Word) (z1, z0 Word) {
// Split x and y into 2 halfWords each, multiply // Split x and y into 2 halfWords each, multiply
// the halfWords separately while avoiding overflow, // the halfWords separately while avoiding overflow,
// and return the product as 2 Words. // and return the product as 2 Words.
@ -96,7 +102,7 @@ func mulW(x, y Word) (z1, z0 Word) {
// z1<<_W + z0 = x*y + c // z1<<_W + z0 = x*y + c
func mulAddWW(x, y, c Word) (z1, z0 Word) { func mulAddWWW_g(x, y, c Word) (z1, z0 Word) {
// Split x and y into 2 halfWords each, multiply // Split x and y into 2 halfWords each, multiply
// the halfWords separately while avoiding overflow, // the halfWords separately while avoiding overflow,
// and return the product as 2 Words. // and return the product as 2 Words.
@ -124,17 +130,17 @@ func mulAddWW(x, y, c Word) (z1, z0 Word) {
} }
func divWW_s(x1, x0, y Word) (q, r Word) func divWWW_s(x1, x0, y Word) (q, r Word)
// q = (x1<<_W + x0 - r)/y // q = (x1<<_W + x0 - r)/y
func divWW(x1, x0, y Word) (q, r Word) { func divWW_g(x1, x0, y Word) (q, r Word) {
if x1 == 0 { if x1 == 0 {
q, r = x0/y, x0%y; q, r = x0/y, x0%y;
return; return;
} }
// TODO(gri) implement general case w/o assembly code // TODO(gri) implement general case w/o assembly code
q, r = divWW_s(x1, x0, y); q, r = divWWW_s(x1, x0, y);
return; return;
} }
@ -142,98 +148,105 @@ func divWW(x1, x0, y Word) (q, r Word) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Elementary operations on vectors // Elementary operations on vectors
// For each function f there is a corresponding function f_s which // All higher-level functions use these elementary vector operations.
// implements the same functionality as f but is written in assembly. // The function pointers f are initialized with default implementations
// f_g, written in Go for portability. The corresponding assembly routines
// f_s should be installed if they exist.
var (
// addVV sets z and returns c such that z+c = x+y.
addVV func(z, x, y *Word, n int) (c Word) = addVV_g;
// subVV sets z and returns c such that z-c = x-y.
subVV func(z, x, y *Word, n int) (c Word) = subVV_g;
// addVW sets z and returns c such that z+c = x-y.
addVW func(z, x *Word, y Word, n int) (c Word) = addVW_g;
// subVW sets z and returns c such that z-c = x-y.
subVW func(z, x *Word, y Word, n int) (c Word) = subVW_g;
// mulAddVWW sets z and returns c such that z+c = x*y + r.
mulAddVWW func(z, x *Word, y, r Word, n int) (c Word) = mulAddVWW_g;
// divWVW sets z and returns r such that z-r = (xn<<(n*_W) + x) / y.
divWVW func(z* Word, xn Word, x *Word, y Word, n int) (r Word) = divWVW_g;
)
func useAsm() bool
func init() {
if useAsm() {
// Install assemby routines.
// TODO(gri) This should only be done if the assembly routines are present.
addVV = addVV_s;
subVV = subVV_s;
addVW = addVW_s;
subVW = subVW_s;
mulAddVWW = mulAddVWW_s;
divWVW = divWVW_s;
}
}
func (p *Word) at(i int) *Word {
return (*Word)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(i)*_S));
}
func addVV_s(z, x, y *Word, n int) (c Word) func addVV_s(z, x, y *Word, n int) (c Word)
func addVV_g(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++ { for i := 0; i < n; i++ {
c, *z = addWW(*x, *y, c); c, *z.at(i) = addWW_g(*x.at(i), *y.at(i), 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 return
} }
func subVV_s(z, x, y *Word, n int) (c Word) func subVV_s(z, x, y *Word, n int) (c Word)
func subVV_g(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++ { for i := 0; i < n; i++ {
c, *z = subWW(*x, *y, c); c, *z.at(i) = subWW_g(*x.at(i), *y.at(i), 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 return
} }
func addVW_s(z, x *Word, y Word, n int) (c Word) func addVW_s(z, x *Word, y Word, n int) (c Word)
func addVW_g(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; c = y;
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
c, *z = addWW(*x, c, 0); c, *z.at(i) = addWW_g(*x.at(i), c, 0);
x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
} }
return return
} }
func subVW_s(z, x *Word, y Word, n int) (c Word) func subVW_s(z, x *Word, y Word, n int) (c Word)
func subVW_g(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; c = y;
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
c, *z = subWW(*x, c, 0); c, *z.at(i) = subWW_g(*x.at(i), c, 0);
x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
} }
return return
} }
func mulVW_s(z, x *Word, y Word, n int) (c Word) func mulAddVWW_s(z, x *Word, y, r Word, n int) (c Word)
func mulAddVWW_g(z, x *Word, y, r Word, n int) (c Word) {
// mulVW sets z and returns c such that z+c = x*y. c = r;
// z, x are n-word vectors.
func mulVW(z, x *Word, y Word, n int) (c Word) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
c, *z = mulAddWW(*x, y, c); c, *z.at(i) = mulAddWWW_g(*x.at(i), y, c);
x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) + _S)));
z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) + _S)));
} }
return return
} }
func divWVW_s(z* Word, xn Word, x *Word, y Word, n int) (r Word) func divWVW_s(z* Word, xn Word, x *Word, y Word, n int) (r Word)
func divWVW_g(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; 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-- { for i := n-1; i >= 0; i-- {
*z, r = divWW(r, *x, y); *z.at(i), r = divWW_g(r, *x.at(i), y);
x = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(x)) - _S)));
z = (*Word)(unsafe.Pointer((uintptr(unsafe.Pointer(z)) - _S)));
} }
return; return;
} }

View File

@ -3,10 +3,11 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file provides fast assembly versions of the routines in arith.go. // This file provides fast assembly versions of the routines in arith.go.
//
// Note: Eventually, these functions should be named like their corresponding TEXT big·useAsm(SB),7,$0
// Go implementations. For now their names have "_s" appended so that MOVB $1, 8(SP)
// they can be linked and tested together. RET
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Elementary operations on words // Elementary operations on words
@ -39,21 +40,10 @@ TEXT big·subWW_s(SB),7,$0
RET RET
// func mulWW_s(x, y Word) (z1, z0 Word) // func mulAddWWW_s(x, y, c 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 // z1<<64 + z0 = x*y + c
// //
TEXT big·mulAddWW_s(SB),7,$0 TEXT big·mulAddWWW_s(SB),7,$0
MOVQ a+0(FP), AX MOVQ a+0(FP), AX
MULQ a+8(FP) MULQ a+8(FP)
ADDQ a+16(FP), AX ADDQ a+16(FP), AX
@ -63,10 +53,10 @@ TEXT big·mulAddWW_s(SB),7,$0
RET RET
// func divWW_s(x1, x0, y Word) (q, r Word) // func divWWW_s(x1, x0, y Word) (q, r Word)
// q = (x1<<64 + x0)/y + r // q = (x1<<64 + x0)/y + r
// //
TEXT big·divWW_s(SB),7,$0 TEXT big·divWWW_s(SB),7,$0
MOVQ a+0(FP), DX MOVQ a+0(FP), DX
MOVQ a+8(FP), AX MOVQ a+8(FP), AX
DIVQ a+16(FP) DIVQ a+16(FP)
@ -174,17 +164,17 @@ E4: CMPQ BX, R11 // i < n
RET RET
// func mulVW_s(z, x *Word, y Word, n int) (c Word) // func mulAddVWW_s(z, x *Word, y, r Word, n int) (c Word)
TEXT big·mulVW_s(SB),7,$0 TEXT big·mulAddVWW_s(SB),7,$0
MOVQ a+0(FP), R10 // z MOVQ a+0(FP), R10 // z
MOVQ a+8(FP), R8 // x MOVQ a+8(FP), R8 // x
MOVQ a+16(FP), R9 // y MOVQ a+16(FP), R9 // y
MOVL a+24(FP), R11 // n MOVQ a+24(FP), CX // c = r
MOVL a+32(FP), R11 // n
XORQ BX, BX // i = 0 XORQ BX, BX // i = 0
XORQ CX, CX // c = 0 JMP E6
JMP E5
L5: MOVQ (R8)(BX*8), AX L6: MOVQ (R8)(BX*8), AX
MULQ R9 MULQ R9
ADDQ CX, AX ADDQ CX, AX
ADCQ $0, DX ADCQ $0, DX
@ -192,10 +182,10 @@ L5: MOVQ (R8)(BX*8), AX
MOVQ DX, CX MOVQ DX, CX
ADDL $1, BX // i++ ADDL $1, BX // i++
E5: CMPQ BX, R11 // i < n E6: CMPQ BX, R11 // i < n
JL L5 JL L6
MOVQ CX, a+32(FP) // return c MOVQ CX, a+40(FP) // return c
RET RET
@ -206,14 +196,14 @@ TEXT big·divWVW_s(SB),7,$0
MOVQ a+16(FP), R8 // x MOVQ a+16(FP), R8 // x
MOVQ a+24(FP), R9 // y MOVQ a+24(FP), R9 // y
MOVL a+32(FP), BX // i = n MOVL a+32(FP), BX // i = n
JMP E6 JMP E7
L6: MOVQ (R8)(BX*8), AX L7: MOVQ (R8)(BX*8), AX
DIVQ R9 DIVQ R9
MOVQ AX, (R10)(BX*8) MOVQ AX, (R10)(BX*8)
E6: SUBL $1, BX E7: SUBL $1, BX // i--
JGE L6 JGE L7 // i >= 0
MOVQ DX, a+40(FP) // return r MOVQ DX, a+40(FP) // return r
RET RET

View File

@ -36,19 +36,19 @@ func testFunWW(t *testing.T, msg string, f funWW, a argWW) {
func TestFunWW(t *testing.T) { func TestFunWW(t *testing.T) {
for _, a := range sumWW { for _, a := range sumWW {
arg := a; arg := a;
testFunWW(t, "addWW", addWW, arg); testFunWW(t, "addWW_g", addWW_g, arg);
testFunWW(t, "addWW_s", addWW_s, arg); testFunWW(t, "addWW_s", addWW_s, arg);
arg = argWW{a.y, a.x, a.c, a.z1, a.z0}; arg = argWW{a.y, a.x, a.c, a.z1, a.z0};
testFunWW(t, "addWW symmetric", addWW, arg); testFunWW(t, "addWW_g symmetric", addWW_g, arg);
testFunWW(t, "addWW_s symmetric", addWW_s, arg); testFunWW(t, "addWW_s symmetric", addWW_s, arg);
arg = argWW{a.z0, a.x, a.c, a.z1, a.y}; arg = argWW{a.z0, a.x, a.c, a.z1, a.y};
testFunWW(t, "subWW", subWW, arg); testFunWW(t, "subWW_g", subWW_g, arg);
testFunWW(t, "subWW_s", subWW_s, arg); testFunWW(t, "subWW_s", subWW_s, arg);
arg = argWW{a.z0, a.y, a.c, a.z1, a.x}; arg = argWW{a.z0, a.y, a.c, a.z1, a.x};
testFunWW(t, "subWW symmetric", subWW, arg); testFunWW(t, "subWW_g symmetric", subWW_g, arg);
testFunWW(t, "subWW_s symmetric", subWW_s, arg); testFunWW(t, "subWW_s symmetric", subWW_s, arg);
} }
} }
@ -97,19 +97,19 @@ func testFunVV(t *testing.T, msg string, f funVV, a argVV) {
func TestFunVV(t *testing.T) { func TestFunVV(t *testing.T) {
for _, a := range sumVV { for _, a := range sumVV {
arg := a; arg := a;
testFunVV(t, "addVV", addVV, arg); testFunVV(t, "addVV_g", addVV_g, arg);
testFunVV(t, "addVV_s", addVV_s, arg); testFunVV(t, "addVV_s", addVV_s, arg);
arg = argVV{a.z, a.y, a.x, a.c}; arg = argVV{a.z, a.y, a.x, a.c};
testFunVV(t, "addVV symmetric", addVV, arg); testFunVV(t, "addVV_g symmetric", addVV_g, arg);
testFunVV(t, "addVV_s symmetric", addVV_s, arg); testFunVV(t, "addVV_s symmetric", addVV_s, arg);
arg = argVV{a.x, a.z, a.y, a.c}; arg = argVV{a.x, a.z, a.y, a.c};
testFunVV(t, "subVV", subVV, arg); testFunVV(t, "subVV_g", subVV_g, arg);
testFunVV(t, "subVV_s", subVV_s, arg); testFunVV(t, "subVV_s", subVV_s, arg);
arg = argVV{a.y, a.z, a.x, a.c}; arg = argVV{a.y, a.z, a.x, a.c};
testFunVV(t, "subVV symmetric", subVV, arg); testFunVV(t, "subVV_g symmetric", subVV_g, arg);
testFunVV(t, "subVV_s symmetric", subVV_s, arg); testFunVV(t, "subVV_s symmetric", subVV_s, arg);
} }
} }
@ -162,26 +162,64 @@ func testFunVW(t *testing.T, msg string, f funVW, a argVW) {
func TestFunVW(t *testing.T) { func TestFunVW(t *testing.T) {
for _, a := range sumVW { for _, a := range sumVW {
arg := a; arg := a;
testFunVW(t, "addVW", addVW, arg); testFunVW(t, "addVW_g", addVW_g, arg);
testFunVW(t, "addVW_s", addVW_s, arg); testFunVW(t, "addVW_s", addVW_s, arg);
arg = argVW{a.x, a.z, a.y, a.c}; arg = argVW{a.x, a.z, a.y, a.c};
testFunVW(t, "subVW", subVW, arg); testFunVW(t, "subVW_g", subVW_g, arg);
testFunVW(t, "subVW_s", subVW_s, 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. type funVWW func(z, x *Word, y, r Word, n int) (c Word)
// make it symmetric, mulVW should become mulAddVWW. type argVWW struct { z, x []Word; y, r Word; c Word }
// Correct decision may become obvious after implementing
// the higher-level routines. var prodVWW = []argVWW{
argVWW{},
argVWW{[]Word{0}, []Word{0}, 0, 0, 0},
argVWW{[]Word{991}, []Word{0}, 0, 991, 0},
argVWW{[]Word{0}, []Word{_M}, 0, 0, 0},
argVWW{[]Word{991}, []Word{_M}, 0, 991, 0},
argVWW{[]Word{0}, []Word{0}, _M, 0, 0},
argVWW{[]Word{991}, []Word{0}, _M, 991, 0},
argVWW{[]Word{1}, []Word{1}, 1, 0, 0},
argVWW{[]Word{992}, []Word{1}, 1, 991, 0},
argVWW{[]Word{22793}, []Word{991}, 23, 0, 0},
argVWW{[]Word{22800}, []Word{991}, 23, 7, 0},
argVWW{[]Word{0, 0, 0, 22793}, []Word{0, 0, 0, 991}, 23, 0, 0},
argVWW{[]Word{7, 0, 0, 22793}, []Word{0, 0, 0, 991}, 23, 7, 0},
argVWW{[]Word{0, 0, 0, 0}, []Word{7893475, 7395495, 798547395, 68943}, 0, 0, 0},
argVWW{[]Word{991, 0, 0, 0}, []Word{7893475, 7395495, 798547395, 68943}, 0, 991, 0},
argVWW{[]Word{0, 0, 0, 0}, []Word{0, 0, 0, 0}, 894375984, 0, 0},
argVWW{[]Word{991, 0, 0, 0}, []Word{0, 0, 0, 0}, 894375984, 991, 0},
argVWW{[]Word{_M<<1&_M}, []Word{_M}, 1<<1, 0, _M>>(_W-1)},
argVWW{[]Word{_M<<1&_M + 1}, []Word{_M}, 1<<1, 1, _M>>(_W-1)},
argVWW{[]Word{_M<<7&_M}, []Word{_M}, 1<<7, 0, _M>>(_W-7)},
argVWW{[]Word{_M<<7&_M + 1<<6}, []Word{_M}, 1<<7, 1<<6, _M>>(_W-7)},
argVWW{[]Word{_M<<7&_M, _M, _M, _M}, []Word{_M, _M, _M, _M}, 1<<7, 0, _M>>(_W-7)},
argVWW{[]Word{_M<<7&_M + 1<<6, _M, _M, _M}, []Word{_M, _M, _M, _M}, 1<<7, 1<<6, _M>>(_W-7)},
}
func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) {
n := len(a.z);
z := make([]Word, n);
c := f(addr(z), addr(a.x), a.y, a.r, 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);
}
}
// TODO(gri) mulAddVWW and divWVW are symmetric operations but
// their signature is not symmetric. Try to unify.
type funWVW func(z* Word, xn Word, x *Word, y Word, n int) (r Word) 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 } type argWVW struct { z []Word; xn Word; x []Word; y Word; r Word }
@ -203,10 +241,14 @@ func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) {
func TestFunVWW(t *testing.T) { func TestFunVWW(t *testing.T) {
for _, a := range prodVW { for _, a := range prodVWW {
if a.y != 0 { arg := a;
arg := argWVW{a.x, a.c, a.z, a.y, 0}; testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg);
testFunWVW(t, "divWVW", divWVW, arg); testFunVWW(t, "mulAddVWW_s", mulAddVWW_s, arg);
if a.y != 0 && a.r < a.y {
arg := argWVW{a.x, a.c, a.z, a.y, a.r};
testFunWVW(t, "divWVW_g", divWVW_g, arg);
testFunWVW(t, "divWVW_s", divWVW_s, arg); testFunWVW(t, "divWVW_s", divWVW_s, arg);
} }
} }

View File

@ -20,6 +20,9 @@ package big
// always normalized before returning the final result. The normalized // always normalized before returning the final result. The normalized
// representation of 0 is the empty or nil slice (length = 0). // representation of 0 is the empty or nil slice (length = 0).
// TODO(gri) - convert these routines into methods for type 'nat'
// - decide if type 'nat' should be exported
func normN(z []Word) []Word { func normN(z []Word) []Word {
i := len(z); i := len(z);
for i > 0 && z[i-1] == 0 { for i > 0 && z[i-1] == 0 {
@ -45,7 +48,7 @@ func makeN(z []Word, m int) []Word {
func newN(z []Word, x uint64) []Word { func newN(z []Word, x uint64) []Word {
if x == 0 { if x == 0 {
return nil; // len == 0 return makeN(z, 0);
} }
// single-digit values // single-digit values
@ -95,6 +98,7 @@ func addNN(z, x, y []Word) []Word {
// result is x // result is x
return setN(z, x); return setN(z, x);
} }
// m > 0
z = makeN(z, m); z = makeN(z, m);
c := addVV(&z[0], &x[0], &y[0], n); c := addVV(&z[0], &x[0], &y[0], n);
@ -124,6 +128,7 @@ func subNN(z, x, y []Word) []Word {
// result is x // result is x
return setN(z, x); return setN(z, x);
} }
// m > 0
z = makeN(z, m); z = makeN(z, m);
c := subVV(&z[0], &x[0], &y[0], n); c := subVV(&z[0], &x[0], &y[0], n);
@ -133,8 +138,8 @@ func subNN(z, x, y []Word) []Word {
if c != 0 { if c != 0 {
panic("underflow"); panic("underflow");
} }
z = normN(z); z = normN(z);
return z; return z;
} }
@ -160,27 +165,38 @@ func cmpNN(x, y []Word) int {
} }
func mulNW(z, x []Word, y Word) []Word { func mulAddNWW(z, x []Word, y, r Word) []Word {
m := len(x); m := len(x);
switch { if m == 0 || y == 0 {
case m == 0 || y == 0: return newN(z, uint64(r)); // result is r
return setN(z, nil); // result is 0
case y == 1:
return setN(z, x); // result is x
} }
// m > 0 // m > 0
z = makeN(z, m+1);
c := mulVW(&z[0], &x[0], y, m); z = makeN(z, m);
c := mulAddVWW(&z[0], &x[0], y, r, m);
if c > 0 { if c > 0 {
z = z[0 : m+1]; z = z[0 : m+1];
z[m] = c; z[m] = c;
} }
return z; return z;
} }
func mulNN(z, x, y []Word) []Word { func mulNN(z, x, y []Word) []Word {
m := len(x);
n := len(y);
switch {
case m < n:
return mulNN(z, x, y);
case m == 0 || n == 0:
return makeN(z, 0);
}
// m > 0 && n > 0 && m >= n
panic("mulNN unimplemented"); panic("mulNN unimplemented");
return z return z
} }
@ -274,7 +290,7 @@ func scanN(z []Word, s string, base int) ([]Word, int, int) {
for ; i < n; i++ { for ; i < n; i++ {
d := hexValue(s[i]); d := hexValue(s[i]);
if 0 <= d && d < base { if 0 <= d && d < base {
panic("scanN needs mulAddVWW"); z = mulAddNWW(z, z, Word(base), Word(d));
} else { } else {
break; break;
} }

View File

@ -73,5 +73,16 @@ func TestStringN(t *testing.T) {
if s != a.s { if s != a.s {
t.Errorf("stringN%+v\n\tgot s = %s; want %s", a, s, a.s); t.Errorf("stringN%+v\n\tgot s = %s; want %s", a, s, a.s);
} }
x, b, n := scanN(nil, a.s, a.b);
if cmpNN(x, a.x) != 0 {
t.Errorf("scanN%+v\n\tgot z = %v; want %v", a, x, a.x);
}
if b != a.b {
t.Errorf("scanN%+v\n\tgot b = %d; want %d", a, b, a.b);
}
if n != len(a.s) {
t.Errorf("scanN%+v\n\tgot n = %d; want %d", a, n, len(a.s));
}
} }
} }

View File

@ -6,118 +6,118 @@
package big package big
// A Z represents a signed multi-precision integer. // An Int represents a signed multi-precision integer.
// The zero value for a Z represents the value 0. // The zero value for an Int represents the value 0.
type Z struct { type Int struct {
neg bool; // sign neg bool; // sign
m []Word; // mantissa abs []Word; // absolute value of the integer
} }
// NewZ sets z to x. // New sets z to x.
func NewZ(z Z, x int64) Z { func (z *Int) New(x int64) *Int {
z.neg = false; z.neg = false;
if x < 0 { if x < 0 {
z.neg = true; z.neg = true;
x = -x; x = -x;
} }
z.m = newN(z.m, uint64(x)); z.abs = newN(z.abs, uint64(x));
return z; return z;
} }
// SetZ sets z to x. // Set sets z to x.
func SetZ(z, x Z) Z { func (z *Int) Set(x *Int) *Int {
z.neg = x.neg; z.neg = x.neg;
z.m = setN(z.m, x.m); z.abs = setN(z.abs, x.abs);
return z; return z;
} }
// AddZZ computes z = x+y. // Add computes z = x+y.
func AddZZ(z, x, y Z) Z { func (z *Int) Add(x, y *Int) *Int {
if x.neg == y.neg { if x.neg == y.neg {
// x + y == x + y // x + y == x + y
// (-x) + (-y) == -(x + y) // (-x) + (-y) == -(x + y)
z.neg = x.neg; z.neg = x.neg;
z.m = addNN(z.m, x.m, y.m); z.abs = addNN(z.abs, x.abs, y.abs);
} else { } else {
// x + (-y) == x - y == -(y - x) // x + (-y) == x - y == -(y - x)
// (-x) + y == y - x == -(x - y) // (-x) + y == y - x == -(x - y)
if cmpNN(x.m, y.m) >= 0 { if cmpNN(x.abs, y.abs) >= 0 {
z.neg = x.neg; z.neg = x.neg;
z.m = subNN(z.m, x.m, y.m); z.abs = subNN(z.abs, x.abs, y.abs);
} else { } else {
z.neg = !x.neg; z.neg = !x.neg;
z.m = subNN(z.m, y.m, x.m); z.abs = subNN(z.abs, y.abs, x.abs);
} }
} }
if len(z.m) == 0 { if len(z.abs) == 0 {
z.neg = false; // 0 has no sign z.neg = false; // 0 has no sign
} }
return z return z
} }
// AddZZ computes z = x-y. // Sub computes z = x-y.
func SubZZ(z, x, y Z) Z { func (z *Int) Sub(x, y *Int) *Int {
if x.neg != y.neg { if x.neg != y.neg {
// x - (-y) == x + y // x - (-y) == x + y
// (-x) - y == -(x + y) // (-x) - y == -(x + y)
z.neg = x.neg; z.neg = x.neg;
z.m = addNN(z.m, x.m, y.m); z.abs = addNN(z.abs, x.abs, y.abs);
} else { } else {
// x - y == x - y == -(y - x) // x - y == x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y) // (-x) - (-y) == y - x == -(x - y)
if cmpNN(x.m, y.m) >= 0 { if cmpNN(x.abs, y.abs) >= 0 {
z.neg = x.neg; z.neg = x.neg;
z.m = subNN(z.m, x.m, y.m); z.abs = subNN(z.abs, x.abs, y.abs);
} else { } else {
z.neg = !x.neg; z.neg = !x.neg;
z.m = subNN(z.m, y.m, x.m); z.abs = subNN(z.abs, y.abs, x.abs);
} }
} }
if len(z.m) == 0 { if len(z.abs) == 0 {
z.neg = false; // 0 has no sign z.neg = false; // 0 has no sign
} }
return z return z
} }
// MulZZ computes z = x*y. // Mul computes z = x*y.
func MulZZ(z, x, y Z) Z { func (z *Int) Mul(x, y *Int) *Int {
// x * y == x * y // x * y == x * y
// x * (-y) == -(x * y) // x * (-y) == -(x * y)
// (-x) * y == -(x * y) // (-x) * y == -(x * y)
// (-x) * (-y) == x * y // (-x) * (-y) == x * y
z.neg = x.neg != y.neg; z.neg = x.neg != y.neg;
z.m = mulNN(z.m, x.m, y.m); z.abs = mulNN(z.abs, x.abs, y.abs);
return z return z
} }
// NegZ computes z = -x. // Neg computes z = -x.
func NegZ(z, x Z) Z { func (z *Int) Neg(x *Int) *Int {
z.neg = len(x.m) > 0 && !x.neg; // 0 has no sign z.neg = len(x.abs) > 0 && !x.neg; // 0 has no sign
z.m = setN(z.m, x.m); z.abs = setN(z.abs, x.abs);
return z; return z;
} }
// Cmp compares x and y. The result is an int value that is // CmpInt compares x and y. The result is an int value that is
// //
// < 0 if x < y // < 0 if x < y
// == 0 if x == y // == 0 if x == y
// > 0 if x > y // > 0 if x > y
// //
func CmpZZ(x, y Z) (r int) { func CmpInt(x, y *Int) (r int) {
// x cmp y == x cmp y // x cmp y == x cmp y
// x cmp (-y) == x // x cmp (-y) == x
// (-x) cmp y == y // (-x) cmp y == y
// (-x) cmp (-y) == -(x cmp y) // (-x) cmp (-y) == -(x cmp y)
switch { switch {
case x.neg == y.neg: case x.neg == y.neg:
r = cmpNN(x.m, y.m); r = cmpNN(x.abs, y.abs);
if x.neg { if x.neg {
r = -r; r = -r;
} }
@ -130,10 +130,10 @@ func CmpZZ(x, y Z) (r int) {
} }
func (x Z) String() string { func (x *Int) String() string {
s := ""; s := "";
if x.neg { if x.neg {
s = "-"; s = "-";
} }
return s + stringN(x.m, 10); return s + stringN(x.abs, 10);
} }

View File

@ -7,14 +7,14 @@ package big
import "testing" import "testing"
func newZ(x int64) Z { func newZ(x int64) *Int {
var z Z; var z Int;
return NewZ(z, x); return z.New(x);
} }
type funZZ func(z, x, y Z) Z type funZZ func(z, x, y *Int) *Int
type argZZ struct { z, x, y Z } type argZZ struct { z, x, y *Int }
var sumZZ = []argZZ{ var sumZZ = []argZZ{
argZZ{newZ(0), newZ(0), newZ(0)}, argZZ{newZ(0), newZ(0), newZ(0)},
@ -28,9 +28,9 @@ var sumZZ = []argZZ{
func TestSetZ(t *testing.T) { func TestSetZ(t *testing.T) {
for _, a := range sumZZ { for _, a := range sumZZ {
var z Z; var z Int;
z = SetZ(z, a.z); z.Set(a.z);
if CmpZZ(z, a.z) != 0 { if CmpInt(&z, a.z) != 0 {
t.Errorf("got z = %v; want %v", z, a.z); t.Errorf("got z = %v; want %v", z, a.z);
} }
} }
@ -38,15 +38,17 @@ func TestSetZ(t *testing.T) {
func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
var z Z; var z Int;
z = f(z, a.x, a.y); f(&z, a.x, a.y);
if CmpZZ(z, a.z) != 0 { if CmpInt(&z, a.z) != 0 {
t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z); t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z);
} }
} }
func TestFunZZ(t *testing.T) { func TestFunZZ(t *testing.T) {
AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) };
SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) };
for _, a := range sumZZ { for _, a := range sumZZ {
arg := a; arg := a;
testFunZZ(t, "AddZZ", AddZZ, arg); testFunZZ(t, "AddZZ", AddZZ, arg);