From: Robert Griesemer Date: Thu, 19 Jun 2008 00:09:20 +0000 (-0700) Subject: - added large integer arithmetic package and test cases X-Git-Tag: weekly.2009-11-06~3653 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=543226e8fd1fa14f12e99e5570095bf0624d6164;p=gostls13.git - added large integer arithmetic package and test cases SVN=123486 --- diff --git a/test/golden.out b/test/golden.out index 7d0892897d..c62405bf38 100644 --- a/test/golden.out +++ b/test/golden.out @@ -21,6 +21,8 @@ hello, world =========== ./int_lit.go +=========== ./integer.go + =========== ./iota.go =========== ./literal.go @@ -53,6 +55,16 @@ test0.go:47: illegal types for operand: AS ({}) BUG: known to fail incorrectly +=========== ./test_integer.go +TestConv +TestAdd +TestSub +TestMul +TestDiv +TestMod +TestFact +PASSED + =========== ./turing.go Hello World! diff --git a/test/integer.go b/test/integer.go new file mode 100755 index 0000000000..42c80162e5 --- /dev/null +++ b/test/integer.go @@ -0,0 +1,618 @@ +// 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 Integer + +const ValueLen = 1000; +type Word uint32 +type Value *[ValueLen]Word +type IntegerImpl struct { + val Value +} +type Integer *IntegerImpl + +export IntegerImpl, Integer + +const N = 4; +const H = 1 +const L = 28; +const M = 1 << L - 1; + + +// ---------------------------------------------------------------------------- +// Support + +// TODO What are we going to about asserts? +func ASSERT(p bool) { + if !p { + panic "ASSERT failed"; + } +} + + +func CHECK(p bool) { + if !p { + panic "CHECK failed"; + } +} + + +func UNIMPLEMENTED(s string) { + panic "UNIMPLEMENTED: ", s; +} + + +// ---------------------------------------------------------------------------- +// + +// TODO "len" is a reserved word at the moment - I think that's wrong +func len_(x Value) int { + l := int(x[0]); + if l < 0 { return -l; } + return l; +} + + +func set_len(x Value, len_ int) { + x[0] = Word(len_); +} + + +func alloc(len_ int) Value { + ASSERT(len_ >= 0); + z := new([ValueLen] Word); + set_len(z, len_); + return z; +} + + +func sign(x Value) bool { + return int(x[0]) < 0; +} + + +func zero(x Value) bool { + return x[0] == 0; +} + + +func neg(x Value) { + x[0] = Word(-int(x[0])); +} + + +// ---------------------------------------------------------------------------- +// Unsigned ops + +func make(x int) Value; + + +func Update(x Word) (z, c Word) { + // z = x & M; + // c = x >> L; + return x & M, x >> L; +} + + +func uadd(x, y Value) Value { + xl := len_(x); + yl := len_(y); + if xl < yl { + return uadd(y, x); + } + ASSERT(xl >= yl); + z := alloc(xl + 1); + + i := 0; + c := Word(0); + for i < yl { z[i + H], c = Update(x[i + H] + y[i + H] + c); i++; } + for i < xl { z[i + H], c = Update(x[i + H] + c); i++; } + if c != 0 { z[i + H] = c; i++; } + set_len(z, i); + + return z; +} + + +func usub(x, y Value) Value { + xl := len_(x); + yl := len_(y); + if xl < yl { + return uadd(y, x); + } + ASSERT(xl >= yl); + z := alloc(xl + 1); + + i := 0; + c := Word(0); + for i < yl { z[i + H], c = Update(x[i + H] - y[i + H] + c); i++; } + for i < xl { z[i + H], c = Update(x[i + H] + c); i++; } + ASSERT(c == 0); // usub(x, y) must be called with x >= y + for i > 0 && z[i - 1 + H] == 0 { i--; } + set_len(z, i); + + return z; +} + + +// Computes x = x*a + c (in place) for "small" a's. +func umul_add(x Value, a, c Word) Value { + CHECK(0 <= a && a < (1 << N)); + CHECK(0 <= c && c < (1 << N)); + if (zero(x) || a == 0) && c == 0 { + return make(0); + } + xl := len_(x); + + z := alloc(xl + 1); + i := 0; + for i < xl { z[i + H], c = Update(x[i + H] * a + c); i++; } + if c != 0 { z[i + H] = c; i++; } + set_len(z, i); + + return z; +} + + +// Computes x = x div d (in place) for "small" d's. Returns x mod d. +func umod(x Value, d Word) Word { + CHECK(0 < d && d < (1 << N)); + xl := len_(x); + c := Word(0); + + i := xl; + for i > 0 { + i--; + c = c << L + x[i + H]; + + q := c / d; + x[i + H] = q; + + //x[i + H] = c / d; // BUG + + c = c % d; + } + if xl > 0 && x[xl - 1 + H] == 0 { + set_len(x, xl - 1); + } + + return c; +} + + +// Returns z = (x * y) div B, c = (x * y) mod B. +func umul1(x, y Word) (z Word, c Word) { + const L2 = (L + 1) >> 1; + const B2 = 1 << L2; + const M2 = B2 - 1; + + x0 := x & M2; + x1 := x >> L2; + + y0 := y & M2; + y1 := y >> L2; + + z10 := x0*y0; + z21 := x1*y0 + x0*y1 + (z10 >> L2); + + cc := x1*y1 + (z21 >> L2); + zz := ((z21 & M2) << L2) | (z10 & M2); + return zz, cc +} + + +func umul(x Value, y Value) Value { + if zero(x) || zero(y) { + return make(0); + } + xl := len_(x); + yl := len_(y); + if xl < yl { + return umul(y, x); // for speed + } + ASSERT(xl >= yl && yl > 0); + + // initialize z + zl := xl + yl; + z := alloc(zl); + for i := 0; i < zl; i++ { z[i + H] = 0; } + + k := 0; + for j := 0; j < yl; j++ { + d := y[j + H]; + if d != 0 { + k = j; + c := Word(0); + for i := 0; i < xl; i++ { + // compute z[k + H] += x[i + H] * d + c; + t := z[k + H] + c; + var z1 Word; + z1, c = umul1(x[i + H], d); + t += z1; + z[k + H] = t & M; + c += t >> L; + k++; + } + if c != 0 { + z[k + H] = Word(c); + k++; + } + } + } + set_len(z, k); + + return z; +} + + +func ucmp(x Value, y Value) int { + xl := len_(x); + yl := len_(y); + + if xl != yl || xl == 0 { + return xl - yl; + } + + i := xl - 1; + for i > 0 && x[i + H] == y[i + H] { i--; } + return int(x[i + H]) - int(y[i + H]); +} + + +func ulog(x Value) int { + xl := len_(x); + if xl == 0 { return 0; } + + n := (xl - 1) * L; + for t := x[xl - 1 + H]; t != 0; t >>= 1 { n++ }; + + return n; +} + + +func make(x int) Value { + if x == 0 { + z := alloc(0); + set_len(z, 0); + return z; + } + + if x == -x { // smallest int + z := alloc(2); + z[0 + H] = 0; + z[1 + H] = 1; + set_len(z, -2); + return z; + } + + z := alloc(1); + if x < 0 { + z[0 + H] = Word(-x); + set_len(z, -1); + } else { + z[0 + H] = Word(x); + set_len(z, 1); + } + + return z; +} + + +func make_from_string(s string) Value { + // skip sign, if any + i := 0; + if len(s) > 0 && (s[i] == '-' || s[i] == '+') { + i = 1; + } + + // read digits + x := make(0); + for i < len(s) && '0' <= s[i] && s[i] <= '9' { + x = umul_add(x, 10, Word(s[i] - '0')); + i++; + } + + // read sign + if len(s) > 0 && s[0] == '-' { + neg(x); + } + + return x; +} + + +// ---------------------------------------------------------------------------- +// Creation + + +func (x Integer) Init(val Value) Integer { + x.val = val; + return x; +} + + +// ---------------------------------------------------------------------------- +// Signed ops + +func add(x Value, y Value) Value { + var z Value; + if sign(x) == sign(y) { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z = uadd(x, y); + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if ucmp(x, y) >= 0 { + z = usub(x, y); + } else { + z = usub(y, x); + neg(z); + } + } + if sign(x) { + neg(z); + } + return z; +} + + +func sub(x Value, y Value) Value { + var z Value; + if sign(x) != sign(y) { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z = uadd(x, y); + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if ucmp(x, y) >= 0 { + z = usub(x, y); + } else { + z = usub(y, x); + neg(z); + } + } + if sign(x) { + neg(z); + } + return z; +} + + +func mul(x Value, y Value) Value { + // x * y == x * y + // x * (-y) == -(x * y) + // (-x) * y == -(x * y) + // (-x) * (-y) == x * y + z := umul(x, y); + if sign(x) != sign(y) { + neg(z); + } + return z; +} + + +func mul_range(a, b int) Value { + if a > b { return make(1) }; + if a == b { return make(a) }; + if a + 1 == b { return mul(make(a), make(b)) }; + m := (a + b) >> 1; + ASSERT(a <= m && m < b); + return mul(mul_range(a, m), mul_range(m + 1, b)); +} + + +func fact(n int) Value { + return mul_range(2, n); +} + + +// Returns a copy of x with space for one extra digit. +func copy(x Value) Value { + xl := len_(x); + + z := alloc(xl + 1); // add space for one extra digit + for i := 0; i < xl; i++ { z[i + H] = x[i + H]; } + set_len(z, xl); + + return z; +} + + +func tostring(x Value) string { + // allocate string + // approx. length: 1 char for 3 bits + n := ulog(x)/3 + 3; // +1 (round up) +1 (sign) +1 (0 termination) + //s := new([]byte, n); + s := new([100000]byte); + + // convert + z := copy(x); + i := 0; + for i == 0 || !zero(z) { + s[i] = byte(umod(z, 10) + '0'); + i++; + }; + if sign(x) { + s[i] = '-'; + i++; + } + s[i] = 0; + ASSERT(0 < i && i < n); + + // reverse in place + i--; + for j := 0; j < i; j++ { + t := s[j]; + s[j] = s[i]; + s[i] = t; + i--; + } + + return string(s); +} + + +// ---------------------------------------------------------------------------- +// Creation + +export FromInt +func FromInt(v int) Integer { + return new(IntegerImpl).Init(make(v)); +} + + +export FromString +func FromString(s string) Integer { + return new(IntegerImpl).Init(make_from_string(s)); +} + + +// ---------------------------------------------------------------------------- +// Arithmetic ops + +func (x Integer) neg () Integer { + if (zero(x.val)) { + return new(IntegerImpl).Init(make(0)); + } + + z := copy(x.val); + neg(z); + return new(IntegerImpl).Init(z); +} + + +func (x Integer) add (y Integer) Integer { + return new(IntegerImpl).Init(add(x.val, y.val)); +} + + +func (x Integer) sub (y Integer) Integer { + return new(IntegerImpl).Init(sub(x.val, y.val)); +} + + +func (x Integer) mul (y Integer) Integer { + return new(IntegerImpl).Init(mul(x.val, y.val)); +} + + +func (x Integer) quo (y Integer) Integer { + UNIMPLEMENTED("quo"); + return nil; +} + + +func (x Integer) rem (y Integer) Integer { + UNIMPLEMENTED("rem"); + return nil; +} + + +func (x Integer) div (y Integer) Integer { + UNIMPLEMENTED("div"); + return nil; +} + + +func (x Integer) mod (y Integer) Integer { + UNIMPLEMENTED("mod"); + return nil; +} + + +// ---------------------------------------------------------------------------- +// Arithmetic shifts + +func (x Integer) shl (s uint) Integer { + UNIMPLEMENTED("shl"); + return nil; +} + + +func (x Integer) shr (s uint) Integer { + UNIMPLEMENTED("shr"); + return nil; +} + + +// ---------------------------------------------------------------------------- +// Logical ops + +func (x Integer) inv () Integer { + UNIMPLEMENTED("inv"); + return nil; +} + + +func (x Integer) and (y Integer) Integer { + UNIMPLEMENTED("and"); + return nil; +} + + +func (x Integer) or (y Integer) Integer { + UNIMPLEMENTED("or"); + return nil; +} + + +func (x Integer) xor (y Integer) Integer { + UNIMPLEMENTED("xor"); + return nil; +} + + +// ---------------------------------------------------------------------------- +// Comparisons + +func (x Integer) cmp (y Integer) int { + return 0; +} + + +func (x Integer) eql (y Integer) bool { + return x.cmp(y) == 0; +} + + +func (x Integer) neq (y Integer) bool { + return x.cmp(y) != 0; +} + + +func (x Integer) lss (y Integer) bool { + return x.cmp(y) < 0; +} + + +func (x Integer) leq (y Integer) bool { + return x.cmp(y) <= 0; +} + + +func (x Integer) gtr (y Integer) bool { + return x.cmp(y) > 0; +} + + +func (x Integer) geq (y Integer) bool { + return x.cmp(y) >= 0; +} + + +// ---------------------------------------------------------------------------- +// Specials + +export Fact +func Fact(n int) Integer { + return new(IntegerImpl).Init(fact(n)); +} + + +func (x Integer) ToString() string { + return tostring(x.val); +} + diff --git a/test/test_integer.go b/test/test_integer.go new file mode 100644 index 0000000000..504299ba58 --- /dev/null +++ b/test/test_integer.go @@ -0,0 +1,137 @@ +// 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 main + +import Integer "integer" + +type Int Integer.Integer; + +const ( + sa = "991"; + sb = "2432902008176640000"; // 20! + sc = "93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000"; // 100! +) + +var ( + m, z, p, + a, b, c, + a_a, a_b, a_c, b_b, b_c, c_c, a_b_c + Int +) + + +func CHECK(p bool) { + if !p { + panic "CHECK failed\n"; + } +} + + +func Init() { + m = Integer.FromInt(-1); + z = Integer.FromInt(0); + p = Integer.FromInt(1); + + a = Integer.FromString(sa); + b = Integer.FromString(sb); + c = Integer.FromString(sc); + + a_a = Integer.FromInt(991 + 991); + a_b = Integer.FromString("2432902008176640991"); + a_c = Integer.FromString("93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000991"); +} + + +func TestConv() { + print "TestConv\n"; + CHECK(a.eql(Integer.FromInt(991))); + CHECK(b.eql(Integer.Fact(20))); + CHECK(c.eql(Integer.Fact(100))); + + CHECK(a.ToString() == sa); + CHECK(b.ToString() == sb); + CHECK(c.ToString() == sc); + + // also tested much via TestFact +} + + +func TestAdd() { + print "TestAdd\n"; + CHECK(z.add(z).eql(z)); + CHECK(a.add(z).eql(a)); + CHECK(z.add(a).eql(a)); + + CHECK(c.add(z).eql(c)); + CHECK(z.add(c).eql(c)); + + CHECK(m.add(p).eql(z)); + + CHECK(a.add(a).eql(a_a)); + CHECK(a.add(b).eql(a_b)); + CHECK(a.add(c).eql(a_c)); + + // needs more +} + + +func TestSub() { + print "TestSub\n"; + CHECK(z.sub(z).eql(z)); + CHECK(a.sub(z).eql(a)); + CHECK(z.sub(a).eql(a.neg())); + + CHECK(c.sub(z).eql(c)); + CHECK(z.sub(c).eql(c.neg())); + + CHECK(p.sub(m).eql(p.add(p))); + + CHECK(a.sub(a).eql(z)); + + // needs more +} + + +func TestMul() { + print "TestMul\n"; + // tested much via TestFact for now +} + + +func TestDiv() { + print "TestDiv\n"; + // no div implemented yet +} + + +func TestMod() { + print "TestMod\n"; + // no mod implemented yet +} + + +func TestFact() { + print "TestFact\n"; + for n := 990; n < 1010; n++ { + f := Integer.Fact(n); + CHECK(Integer.FromString(f.ToString()).eql(f)); + } +} + + +func main() { + Init(); + + TestConv(); + TestAdd(); + TestSub(); + TestMul(); + TestDiv(); + TestMod(); + + TestFact(); + + print "PASSED\n"; +}