]> Cypherpunks repositories - gostls13.git/commitdiff
- added large integer arithmetic package and test cases
authorRobert Griesemer <gri@golang.org>
Thu, 19 Jun 2008 00:09:20 +0000 (17:09 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 19 Jun 2008 00:09:20 +0000 (17:09 -0700)
SVN=123486

test/golden.out
test/integer.go [new file with mode: 0755]
test/test_integer.go [new file with mode: 0644]

index 7d0892897dac81ffa492e310a58dc9e2a69ac228..c62405bf384044ee3f7f6eca90e543d2edb36d0e 100644 (file)
@@ -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 (executable)
index 0000000..42c8016
--- /dev/null
@@ -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 (file)
index 0000000..504299b
--- /dev/null
@@ -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";
+}