From 8afeb52cace7236687b3a7a14753b30b02d1977e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 7 Jul 2009 10:03:42 -0700 Subject: [PATCH] - removed implementation restrictions for creation of small 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 | 139 +++++++++++++++++++++++++--------- src/pkg/bignum/bignum_test.go | 73 +++++++++++++++--- 2 files changed, 164 insertions(+), 48 deletions(-) diff --git a/src/pkg/bignum/bignum.go b/src/pkg/bignum/bignum.go index 665ab9f06e..4fe6d04442 100755 --- a/src/pkg/bignum/bignum.go +++ b/src/pkg/bignum/bignum.go @@ -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<= 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; } diff --git a/src/pkg/bignum/bignum_test.go b/src/pkg/bignum/bignum_test.go index 9351c2ebfb..4f6f3f6f66 100644 --- a/src/pkg/bignum/bignum_test.go +++ b/src/pkg/bignum/bignum_test.go @@ -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},