]> Cypherpunks repositories - gostls13.git/commitdiff
- removed implementation restrictions for creation of small
authorRobert Griesemer <gri@golang.org>
Tue, 7 Jul 2009 17:03:42 +0000 (10:03 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 7 Jul 2009 17:03:42 +0000 (10:03 -0700)
  Natural, Integer, and Rational numbers
- added Value() methods to access small Natural and Integers
  as uint64 or int64 respectively, and to get the components
  of Rational numbers
- fixed a bug with Integer creation
- removed some _'s from names
- added more comments in places
- added test cases

R=rsc
DELTA=184  (127 added, 11 deleted, 46 changed)
OCL=31210
CL=31265

src/pkg/bignum/bignum.go
src/pkg/bignum/bignum_test.go

index 665ab9f06ef48de2f98a6e1934badd614d1ce649..4fe6d044425dd0d8671b2c25ce0132da7eed87d0 100755 (executable)
@@ -59,12 +59,12 @@ type (
 
 
 const (
-       _LogW = 64;
-       _LogH = 4;  // bits for a hex digit (= small number)
-       _LogB = _LogW - _LogH;  // largest bit-width available
+       logW = 64;
+       logH = 4;  // bits for a hex digit (= small number)
+       logB = logW - logH;  // largest bit-width available
 
        // half-digits
-       _W2 = _LogB / 2;  // width
+       _W2 = logB / 2;   // width
        _B2 = 1 << _W2;   // base
        _M2 = _B2 - 1;    // mask
 
@@ -86,11 +86,12 @@ func assert(p bool) {
 
 
 func isSmall(x digit) bool {
-       return x < 1<<_LogH;
+       return x < 1<<logH;
 }
 
 
-// For debugging.
+// For debugging. Keep around.
+/*
 func dump(x []digit) {
        print("[", len(x), "]");
        for i := len(x) - 1; i >= 0; i-- {
@@ -98,6 +99,7 @@ func dump(x []digit) {
        }
        println();
 }
+*/
 
 
 // ----------------------------------------------------------------------------
@@ -116,21 +118,66 @@ var (
 
 
 // Nat creates a small natural number with value x.
-// Implementation restriction: At the moment, only values
-// x < (1<<60) are supported.
 //
-func Nat(x uint) Natural {
+func Nat(x uint64) Natural {
+       // avoid allocation for common small values
        switch x {
        case 0: return natZero;
        case 1: return natOne;
        case 2: return natTwo;
        case 10: return natTen;
        }
-       assert(digit(x) < _B);
-       return Natural{digit(x)};
+
+       // single-digit values
+       if x < _B {
+               return Natural{digit(x)};
+       }
+
+       // compute number of digits required to represent x
+       // (this is usually 1 or 2, but the algorithm works
+       // for any base)
+       n := 0;
+       for t := x; t > 0; t >>= _W {
+               n++;
+       }
+
+       // split x into digits
+       z := make(Natural, n);
+       for i := 0; i < n; i++ {
+               z[i] = digit(x & _M);
+               x >>= _W;
+       }
+
+       return z;
 }
 
 
+// Value returns the lowest 64bits of x.
+//
+func (x Natural) Value() uint64 {
+       // single-digit values
+       n := len(x);
+       switch n {
+       case 0: return 0;
+       case 1: return uint64(x[0]);
+       }
+
+       // multi-digit values
+       // (this is usually 1 or 2, but the algorithm works
+       // for any base)
+       z := uint64(0);
+       s := uint(0);
+       for i := 0; i < n && s < 64; i++ {
+               z += uint64(x[i]) << s;
+               s += _W;
+       }
+
+       return z;
+}
+
+
+// Predicates
+
 // IsEven returns true iff x is divisible by 2.
 //
 func (x Natural) IsEven() bool {
@@ -632,7 +679,11 @@ func (x Natural) Cmp(y Natural) int {
 }
 
 
-func log2(x digit) uint {
+// log2 computes the binary logarithm of x for x > 0.
+// The result is the integer n for which 2^n <= x < 2^(n+1).
+// If x == 0 a run-time error occurs.
+//
+func log2(x uint64) uint {
        assert(x > 0);
        n := uint(0);
        for x > 0 {
@@ -650,7 +701,7 @@ func log2(x digit) uint {
 func (x Natural) Log2() uint {
        n := len(x);
        if n > 0 {
-               return (uint(n) - 1)*_W + log2(x[n - 1]);
+               return (uint(n) - 1)*_W + log2(uint64(x[n - 1]));
        }
        panic("Log2(0)");
 }
@@ -681,7 +732,7 @@ func (x Natural) ToString(base uint) string {
 
        // allocate buffer for conversion
        assert(2 <= base && base <= 16);
-       n := (x.Log2() + 1) / log2(digit(base)) + 1;  // +1: round up
+       n := (x.Log2() + 1) / log2(uint64(base)) + 1;  // +1: round up
        s := make([]byte, n);
 
        // don't destroy x
@@ -728,7 +779,7 @@ func (x Natural) Format(h fmt.State, c int) {
 
 
 func hexvalue(ch byte) uint {
-       d := uint(1 << _LogH);
+       d := uint(1 << logH);
        switch {
        case '0' <= ch && ch <= '9': d = uint(ch - '0');
        case 'a' <= ch && ch <= 'f': d = uint(ch - 'a') + 10;
@@ -839,8 +890,8 @@ func (xp Natural) Pow(n uint) Natural {
 func MulRange(a, b uint) Natural {
        switch {
        case a > b: return Nat(1);
-       case a == b: return Nat(a);
-       case a + 1 == b: return Nat(a).Mul(Nat(b));
+       case a == b: return Nat(uint64(a));
+       case a + 1 == b: return Nat(uint64(a)).Mul(Nat(uint64(b)));
        }
        m := (a + b)>>1;
        assert(a <= m && m < b);
@@ -903,25 +954,36 @@ func MakeInt(sign bool, mant Natural) *Integer {
 
 
 // Int creates a small integer with value x.
-// Implementation restriction: At the moment, only values
-// with an absolute value |x| < (1<<60) are supported.
 //
-func Int(x int) *Integer {
-       sign := false;
-       var ux uint;
+func Int(x int64) *Integer {
+       var ux uint64;
        if x < 0 {
-               sign = true;
-               if -x == x {
-                       // smallest negative integer
-                       t := ^0;
-                       ux = ^(uint(t) >> 1);
-               } else {
-                       ux = uint(-x);
-               }
+               // For the most negative x, -x == x, and
+               // the bit pattern has the correct value.
+               ux = uint64(-x);
        } else {
-               ux = uint(x);
+               ux = uint64(x);
        }
-       return MakeInt(sign, Nat(ux));
+       return MakeInt(x < 0, Nat(ux));
+}
+
+
+// Value returns the value of x, if x fits into an int64;
+// otherwise the result is undefined.
+//
+func (x *Integer) Value() int64 {
+       z := int64(x.mant.Value());
+       if x.sign {
+               z = -z;
+       }
+       return z;
+}
+
+
+// Abs returns the absolute value of x.
+//
+func (x *Integer) Abs() Natural {
+       return x.mant;
 }
 
 
@@ -1303,10 +1365,8 @@ func MakeRat(a *Integer, b Natural) *Rational {
 
 
 // Rat creates a small rational number with value a0/b0.
-// Implementation restriction: At the moment, only values a0, b0
-// with an absolute value |a0|, |b0| < (1<<60) are supported.
 //
-func Rat(a0 int, b0 int) *Rational {
+func Rat(a0 int64, b0 int64) *Rational {
        a, b := Int(a0), Int(b0);
        if b.sign {
                a = a.Neg();
@@ -1315,6 +1375,13 @@ func Rat(a0 int, b0 int) *Rational {
 }
 
 
+// Value returns the numerator and denominator of x.
+//
+func (x *Rational) Value() (numerator *Integer, denominator Natural) {
+       return x.a, x.b;
+}
+
+
 // Predicates
 
 // IsZero returns true iff x == 0.
@@ -1454,7 +1521,7 @@ func RatFromString(s string, base uint) (*Rational, uint, int) {
                        alen++;
                        b, base, blen = NatFromString(s[alen : len(s)], abase);
                        assert(base == abase);
-                       f := Nat(base).Pow(uint(blen));
+                       f := Nat(uint64(base)).Pow(uint(blen));
                        a = MakeInt(a.sign, a.mant.Mul(f).Add(b));
                        b = f;
                }
index 9351c2ebfb8454966bed115ed6cffabf6144e648..4f6f3f6f66f3ff6acd5521379b70e46bc5534d70 100644 (file)
@@ -99,9 +99,31 @@ func rat_eq(n uint, x, y *bignum.Rational) {
        }
 }
 
+
 func TestNatConv(t *testing.T) {
        tester = t;
        test_msg = "NatConvA";
+       type entry1 struct { x uint64; s string };
+       tab := []entry1{
+               entry1{0, "0"},
+               entry1{255, "255"},
+               entry1{65535, "65535"},
+               entry1{4294967295, "4294967295"},
+               entry1{18446744073709551615, "18446744073709551615"},
+       };
+       for i, e := range tab {
+               test(100 + uint(i), bignum.Nat(e.x).String() == e.s);
+               test(200 + uint(i), natFromString(e.s, 0, nil).Value() == e.x);
+       }
+
+       test_msg = "NatConvC";
+       z := uint64(7);
+       for i := uint(0); i <= 64; i++ {
+               test(i, bignum.Nat(z).Value() == z);
+               z <<= 1;
+       }
+
+       test_msg = "NatConvD";
        nat_eq(0, a, bignum.Nat(991));
        nat_eq(1, b, bignum.Fact(20));
        nat_eq(2, c, bignum.Fact(100));
@@ -109,7 +131,7 @@ func TestNatConv(t *testing.T) {
        test(4, b.String() == sb);
        test(5, c.String() == sc);
 
-       test_msg = "NatConvB";
+       test_msg = "NatConvE";
        var slen int;
        nat_eq(10, natFromString("0", 0, nil), nat_zero);
        nat_eq(11, natFromString("123", 0, nil), bignum.Nat(123));
@@ -118,22 +140,49 @@ func TestNatConv(t *testing.T) {
        nat_eq(14, natFromString("0x1fg", 0, &slen), bignum.Nat(1*16 + 15));
        test(4, slen == 4);
 
-       test_msg = "NatConvC";
+       test_msg = "NatConvF";
        tmp := c.Mul(c);
        for base := uint(2); base <= 16; base++ {
                nat_eq(base, natFromString(tmp.ToString(base), base, nil), tmp);
        }
 
-       test_msg = "NatConvD";
+       test_msg = "NatConvG";
        x := bignum.Nat(100);
        y, b, _ := bignum.NatFromString(fmt.Sprintf("%b", &x), 2);
        nat_eq(100, y, x);
 }
 
 
+func abs(x int64) uint64 {
+       if x < 0 {
+               x = -x;
+       }
+       return uint64(x);
+}
+
+
 func TestIntConv(t *testing.T) {
        tester = t;
-       test_msg = "IntConv";
+       test_msg = "IntConvA";
+       type entry2 struct { x int64; s string };
+       tab := []entry2{
+               entry2{0, "0"},
+               entry2{-128, "-128"},
+               entry2{127, "127"},
+               entry2{-32768, "-32768"},
+               entry2{32767, "32767"},
+               entry2{-2147483648, "-2147483648"},
+               entry2{2147483647, "2147483647"},
+               entry2{-9223372036854775808, "-9223372036854775808"},
+               entry2{9223372036854775807, "9223372036854775807"},
+       };
+       for i, e := range tab {
+               test(100 + uint(i), bignum.Int(e.x).String() == e.s);
+               test(200 + uint(i), intFromString(e.s, 0, nil).Value() == e.x);
+               test(300 + uint(i), bignum.Int(e.x).Abs().Value() == abs(e.x));
+       }
+
+       test_msg = "IntConvB";
        var slen int;
        int_eq(0, intFromString("0", 0, nil), int_zero);
        int_eq(1, intFromString("-0", 0, nil), int_zero);
@@ -180,7 +229,7 @@ func add(x, y bignum.Natural) bignum.Natural {
 }
 
 
-func sum(n uint, scale bignum.Natural) bignum.Natural {
+func sum(n uint64, scale bignum.Natural) bignum.Natural {
        s := nat_zero;
        for ; n > 0; n-- {
                s = add(s, bignum.Nat(n).Mul(scale));
@@ -196,9 +245,9 @@ func TestNatAdd(t *testing.T) {
        nat_eq(1, add(nat_zero, c), c);
 
        test_msg = "NatAddB";
-       for i := uint(0); i < 100; i++ {
+       for i := uint64(0); i < 100; i++ {
                t := bignum.Nat(i);
-               nat_eq(i, sum(i, c), t.Mul(t).Add(t).Shr(1).Mul(c));
+               nat_eq(uint(i), sum(i, c), t.Mul(t).Add(t).Shr(1).Mul(c));
        }
 }
 
@@ -226,12 +275,12 @@ func TestNatSub(t *testing.T) {
        nat_eq(1, c.Sub(nat_zero), c);
 
        test_msg = "NatSubB";
-       for i := uint(0); i < 100; i++ {
+       for i := uint64(0); i < 100; i++ {
                t := sum(i, c);
-               for j := uint(0); j <= i; j++ {
+               for j := uint64(0); j <= i; j++ {
                        t = t.Sub(mul(bignum.Nat(j), c));
                }
-               nat_eq(i, t, nat_zero);
+               nat_eq(uint(i), t, nat_zero);
        }
 }
 
@@ -276,7 +325,7 @@ func TestNatDiv(t *testing.T) {
 func TestIntQuoRem(t *testing.T) {
        tester = t;
        test_msg = "IntQuoRem";
-       type T struct { x, y, q, r int };
+       type T struct { x, y, q, r int64 };
        a := []T{
                T{+8, +3, +2, +2},
                T{+8, -3, -2, +2},
@@ -303,7 +352,7 @@ func TestIntQuoRem(t *testing.T) {
 func TestIntDivMod(t *testing.T) {
        tester = t;
        test_msg = "IntDivMod";
-       type T struct { x, y, q, r int };
+       type T struct { x, y, q, r int64 };
        a := []T{
                T{+8, +3, +2, +2},
                T{+8, -3, -2, +2},