]> Cypherpunks repositories - gostls13.git/commitdiff
big: implemented format support for fmt library, MulRange
authorRobert Griesemer <gri@golang.org>
Sat, 15 May 2010 17:23:41 +0000 (10:23 -0700)
committerRobert Griesemer <gri@golang.org>
Sat, 15 May 2010 17:23:41 +0000 (10:23 -0700)
- support for binary prefix 0b (to match fmt.Format)
- renamed nat.new -> nat.setUint64 for consistency
- more tests

R=r
CC=golang-dev
https://golang.org/cl/1233041

src/pkg/big/int.go
src/pkg/big/int_test.go
src/pkg/big/nat.go
src/pkg/big/nat_test.go

index 4126ce62db867dc0df4ceda0bef90e7466b49ebe..cdf5a7d554258ccf702d6c61867ddfe9c7efe33b 100755 (executable)
@@ -6,6 +6,9 @@
 
 package big
 
+import "fmt"
+
+
 // An Int represents a signed multi-precision integer.
 // The zero value for an Int represents the value 0.
 type Int struct {
@@ -19,12 +22,13 @@ var intOne = &Int{false, natOne}
 
 // SetInt64 sets z to x and returns z.
 func (z *Int) SetInt64(x int64) *Int {
-       z.neg = false
+       neg := false
        if x < 0 {
-               z.neg = true
+               neg = true
                x = -x
        }
-       z.abs = z.abs.new(uint64(x))
+       z.abs = z.abs.setUint64(uint64(x))
+       z.neg = neg
        return z
 }
 
@@ -37,8 +41,8 @@ func NewInt(x int64) *Int {
 
 // Set sets z to x.
 func (z *Int) Set(x *Int) *Int {
-       z.neg = x.neg
        z.abs = z.abs.set(x.abs)
+       z.neg = x.neg
        return z
 }
 
@@ -99,6 +103,30 @@ func (z *Int) Mul(x, y *Int) *Int {
 }
 
 
+// MulRange sets z to the product of all integers
+// in the range [a, b] inclusively and returns z.
+// If a > b (empty range), the result is 1.
+func (z *Int) MulRange(a, b int64) *Int {
+       switch {
+       case a > b:
+               return z.SetInt64(1) // empty range
+       case a <= 0 && b >= 0:
+               return z.SetInt64(0) // range includes 0
+       }
+       // a <= b && (b < 0 || a > 0)
+
+       neg := false
+       if a < 0 {
+               neg = (b-a)&1 == 0
+               a, b = -b, -a
+       }
+
+       z.abs = z.abs.mulRange(uint64(a), uint64(b))
+       z.neg = neg
+       return z
+}
+
+
 // Quo sets z to the quotient x/y for y != 0 and returns z.
 // If y == 0, a division-by-zero run-time panic occurs.
 // See QuoRem for more details.
@@ -243,26 +271,53 @@ func (x *Int) Cmp(y *Int) (r int) {
 }
 
 
-func (z *Int) String() string {
+func (x *Int) String() string {
        s := ""
-       if z.neg {
+       if x.neg {
                s = "-"
        }
-       return s + z.abs.string(10)
+       return s + x.abs.string(10)
+}
+
+
+func fmtbase(ch int) int {
+       switch ch {
+       case 'b':
+               return 2
+       case 'o':
+               return 8
+       case 'd':
+               return 10
+       case 'x':
+               return 16
+       }
+       return 10
+}
+
+
+// Format is a support routine for fmt.Formatter. It accepts
+// the formats 'b' (binary), 'o' (octal), 'd' (decimal) and
+// 'x' (hexadecimal).
+//
+func (x *Int) Format(s fmt.State, ch int) {
+       if x.neg {
+               fmt.Fprint(s, "-")
+       }
+       fmt.Fprint(s, x.abs.string(fmtbase(ch)))
 }
 
 
 // Int64 returns the int64 representation of z.
 // If z cannot be represented in an int64, the result is undefined.
-func (z *Int) Int64() int64 {
-       if len(z.abs) == 0 {
+func (x *Int) Int64() int64 {
+       if len(x.abs) == 0 {
                return 0
        }
-       v := int64(z.abs[0])
-       if _W == 32 && len(z.abs) > 1 {
-               v |= int64(z.abs[1]) << 32
+       v := int64(x.abs[0])
+       if _W == 32 && len(x.abs) > 1 {
+               v |= int64(x.abs[1]) << 32
        }
-       if z.neg {
+       if x.neg {
                v = -v
        }
        return v
index fe2974308daf2222a2136691058963b6f037ae3c..064f467311f41017384132c5ff083f35ade09648 100755 (executable)
@@ -7,6 +7,7 @@ package big
 import (
        "bytes"
        "encoding/hex"
+       "fmt"
        "testing"
        "testing/quick"
 )
@@ -156,47 +157,143 @@ func TestMul(t *testing.T) {
 }
 
 
-type fromStringTest struct {
+type mulRangeZ struct {
+       a, b int64
+       prod string
+}
+
+
+var mulRangesZ = []mulRangeZ{
+       // entirely positive ranges are covered by mulRangesN
+       mulRangeZ{-1, 1, "0"},
+       mulRangeZ{-2, -1, "2"},
+       mulRangeZ{-3, -2, "6"},
+       mulRangeZ{-3, -1, "-6"},
+       mulRangeZ{1, 3, "6"},
+       mulRangeZ{-10, -10, "-10"},
+       mulRangeZ{0, -1, "1"},                      // empty range
+       mulRangeZ{-1, -100, "1"},                   // empty range
+       mulRangeZ{-1, 1, "0"},                      // range includes 0
+       mulRangeZ{-1e9, 0, "0"},                    // range includes 0
+       mulRangeZ{-1e9, 1e9, "0"},                  // range includes 0
+       mulRangeZ{-10, -1, "3628800"},              // 10!
+       mulRangeZ{-20, -2, "-2432902008176640000"}, // -20!
+       mulRangeZ{-99, -1,
+               "-933262154439441526816992388562667004907159682643816214685929" +
+                       "638952175999932299156089414639761565182862536979208272237582" +
+                       "511852109168640000000000000000000000", // -99!
+       },
+}
+
+
+func TestMulRangeZ(t *testing.T) {
+       var tmp Int
+       // test entirely positive ranges
+       for i, r := range mulRangesN {
+               prod := tmp.MulRange(int64(r.a), int64(r.b)).String()
+               if prod != r.prod {
+                       t.Errorf("#%da: got %s; want %s", i, prod, r.prod)
+               }
+       }
+       // test other ranges
+       for i, r := range mulRangesZ {
+               prod := tmp.MulRange(r.a, r.b).String()
+               if prod != r.prod {
+                       t.Errorf("#%db: got %s; want %s", i, prod, r.prod)
+               }
+       }
+}
+
+
+type stringTest struct {
        in   string
+       out  string
        base int
-       out  int64
+       val  int64
        ok   bool
 }
 
 
-var fromStringTests = []fromStringTest{
-       fromStringTest{in: "", ok: false},
-       fromStringTest{in: "a", ok: false},
-       fromStringTest{in: "z", ok: false},
-       fromStringTest{in: "+", ok: false},
-       fromStringTest{"0", 0, 0, true},
-       fromStringTest{"0", 10, 0, true},
-       fromStringTest{"0", 16, 0, true},
-       fromStringTest{"10", 0, 10, true},
-       fromStringTest{"10", 10, 10, true},
-       fromStringTest{"10", 16, 16, true},
-       fromStringTest{"-10", 16, -16, true},
-       fromStringTest{in: "0x", ok: false},
-       fromStringTest{"0x10", 0, 16, true},
-       fromStringTest{in: "0x10", base: 16, ok: false},
-       fromStringTest{"-0x10", 0, -16, true},
-       fromStringTest{"00", 0, 0, true},
-       fromStringTest{"0", 8, 0, true},
-       fromStringTest{"07", 0, 7, true},
-       fromStringTest{"7", 8, 7, true},
-       fromStringTest{in: "08", ok: false},
-       fromStringTest{in: "8", base: 8, ok: false},
-       fromStringTest{"023", 0, 19, true},
-       fromStringTest{"23", 8, 19, true},
+var stringTests = []stringTest{
+       stringTest{in: "", ok: false},
+       stringTest{in: "a", ok: false},
+       stringTest{in: "z", ok: false},
+       stringTest{in: "+", ok: false},
+       stringTest{in: "0b", ok: false},
+       stringTest{in: "0x", ok: false},
+       stringTest{in: "2", base: 2, ok: false},
+       stringTest{in: "0b2", base: 0, ok: false},
+       stringTest{in: "08", ok: false},
+       stringTest{in: "8", base: 8, ok: false},
+       stringTest{in: "0xg", base: 0, ok: false},
+       stringTest{in: "g", base: 16, ok: false},
+       stringTest{"0", "0", 0, 0, true},
+       stringTest{"0", "0", 10, 0, true},
+       stringTest{"0", "0", 16, 0, true},
+       stringTest{"10", "10", 0, 10, true},
+       stringTest{"10", "10", 10, 10, true},
+       stringTest{"10", "10", 16, 16, true},
+       stringTest{"-10", "-10", 16, -16, true},
+       stringTest{"0x10", "16", 0, 16, true},
+       stringTest{in: "0x10", base: 16, ok: false},
+       stringTest{"-0x10", "-16", 0, -16, true},
+       stringTest{"00", "0", 0, 0, true},
+       stringTest{"0", "0", 8, 0, true},
+       stringTest{"07", "7", 0, 7, true},
+       stringTest{"7", "7", 8, 7, true},
+       stringTest{"023", "19", 0, 19, true},
+       stringTest{"23", "23", 8, 19, true},
+       stringTest{"cafebabe", "cafebabe", 16, 0xcafebabe, true},
+       stringTest{"0b0", "0", 0, 0, true},
+       stringTest{"-111", "-111", 2, -7, true},
+       stringTest{"-0b111", "-7", 0, -7, true},
+       stringTest{"0b1001010111", "599", 0, 0x257, true},
+       stringTest{"1001010111", "1001010111", 2, 0x257, true},
+}
+
+
+func format(base int) string {
+       switch base {
+       case 2:
+               return "%b"
+       case 8:
+               return "%o"
+       case 16:
+               return "%x"
+       }
+       return "%d"
+}
+
+
+func TestGetString(t *testing.T) {
+       z := new(Int)
+       for i, test := range stringTests {
+               if !test.ok {
+                       continue
+               }
+               z.SetInt64(test.val)
+
+               if test.base == 10 {
+                       s := z.String()
+                       if s != test.out {
+                               t.Errorf("#%da got %s; want %s\n", i, s, test.out)
+                       }
+               }
+
+               s := fmt.Sprintf(format(test.base), z)
+               if s != test.out {
+                       t.Errorf("#%db got %s; want %s\n", i, s, test.out)
+               }
+       }
 }
 
 
 func TestSetString(t *testing.T) {
-       n2 := new(Int)
-       for i, test := range fromStringTests {
+       tmp := new(Int)
+       for i, test := range stringTests {
                n1, ok1 := new(Int).SetString(test.in, test.base)
-               n2, ok2 := n2.SetString(test.in, test.base)
-               expected := NewInt(test.out)
+               n2, ok2 := tmp.SetString(test.in, test.base)
+               expected := NewInt(test.val)
                if ok1 != test.ok || ok2 != test.ok {
                        t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok)
                        continue
@@ -213,10 +310,10 @@ func TestSetString(t *testing.T) {
                }
 
                if n1.Cmp(expected) != 0 {
-                       t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n1, test.out)
+                       t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n1, test.val)
                }
                if n2.Cmp(expected) != 0 {
-                       t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n2, test.out)
+                       t.Errorf("#%d (input '%s') got: %s want: %d\n", i, test.in, n2, test.val)
                }
        }
 }
index f752ce647771093c7a9e62ea1534605b0fac33b2..56f3c444ef410fec20de9af68a83fd35d7f5c81d 100755 (executable)
@@ -69,7 +69,7 @@ func (z nat) make(n int) nat {
 }
 
 
-func (z nat) new(x uint64) nat {
+func (z nat) setUint64(x uint64) nat {
        if x == 0 {
                return z.make(0)
        }
@@ -194,7 +194,7 @@ func (x nat) cmp(y nat) (r int) {
 func (z nat) mulAddWW(x nat, y, r Word) nat {
        m := len(x)
        if m == 0 || y == 0 {
-               return z.new(uint64(r)) // result is r
+               return z.setUint64(uint64(r)) // result is r
        }
        // m > 0
 
@@ -456,13 +456,13 @@ func (z nat) mulRange(a, b uint64) nat {
        switch {
        case a == 0:
                // cut long ranges short (optimization)
-               return z.new(0)
+               return z.setUint64(0)
        case a > b:
-               return z.new(1)
+               return z.setUint64(1)
        case a == b:
-               return z.new(a)
+               return z.setUint64(a)
        case a+1 == b:
-               return z.mul(nat(nil).new(a), nat(nil).new(b))
+               return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b))
        }
        m := (a + b) / 2
        return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b))
@@ -621,7 +621,8 @@ func hexValue(ch byte) int {
 //
 // If the base argument is 0, the string prefix determines the actual
 // conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the
-// ``0'' prefix selects base 8. Otherwise the selected base is 10.
+// ``0'' prefix selects base 8, and a ``0b'' or ``0B'' prefix selects
+// base 2. Otherwise the selected base is 10.
 //
 func (z nat) scan(s string, base int) (nat, int, int) {
        // determine base if necessary
@@ -629,23 +630,25 @@ func (z nat) scan(s string, base int) (nat, int, int) {
        if base == 0 {
                base = 10
                if n > 0 && s[0] == '0' {
-                       if n > 1 && (s[1] == 'x' || s[1] == 'X') {
-                               if n == 2 {
-                                       // Reject a string which is just '0x' as nonsense.
-                                       return nil, 0, 0
+                       base, i = 8, 1
+                       if n > 1 {
+                               switch s[1] {
+                               case 'x', 'X':
+                                       base, i = 16, 2
+                               case 'b', 'B':
+                                       base, i = 2, 2
                                }
-                               base, i = 16, 2
-                       } else {
-                               base, i = 8, 1
                        }
                }
        }
-       if base < 2 || 16 < base {
-               panic("illegal base")
+
+       // reject illegal bases or strings consisting only of prefix
+       if base < 2 || 16 < base || (base != 8 && i >= n) {
+               return nil, 0, 0
        }
 
        // convert string
-       z = z[0:0]
+       z = z.make(0)
        for ; i < n; i++ {
                d := hexValue(s[i])
                if 0 <= d && d < base {
index f353822f0bbe8a54b76cc2240230f871f85b0bd1..8545981c03e669fe3fc7cedb36393f85377834bd 100755 (executable)
@@ -111,25 +111,25 @@ func TestFunNN(t *testing.T) {
 }
 
 
-type mulRange struct {
+type mulRangeN struct {
        a, b uint64
        prod string
 }
 
 
-var mulRanges = []mulRange{
-       mulRange{0, 0, "0"},
-       mulRange{1, 1, "1"},
-       mulRange{1, 2, "2"},
-       mulRange{1, 3, "6"},
-       mulRange{1, 3, "6"},
-       mulRange{10, 10, "10"},
-       mulRange{0, 100, "0"},
-       mulRange{0, 1e9, "0"},
-       mulRange{100, 1, "1"},                  // empty range
-       mulRange{1, 10, "3628800"},             // 10!
-       mulRange{1, 20, "2432902008176640000"}, // 20!
-       mulRange{1, 100,
+var mulRangesN = []mulRangeN{
+       mulRangeN{0, 0, "0"},
+       mulRangeN{1, 1, "1"},
+       mulRangeN{1, 2, "2"},
+       mulRangeN{1, 3, "6"},
+       mulRangeN{10, 10, "10"},
+       mulRangeN{0, 100, "0"},
+       mulRangeN{0, 1e9, "0"},
+       mulRangeN{1, 0, "1"},                    // empty range
+       mulRangeN{100, 1, "1"},                  // empty range
+       mulRangeN{1, 10, "3628800"},             // 10!
+       mulRangeN{1, 20, "2432902008176640000"}, // 20!
+       mulRangeN{1, 100,
                "933262154439441526816992388562667004907159682643816214685929" +
                        "638952175999932299156089414639761565182862536979208272237582" +
                        "51185210916864000000000000000000000000", // 100!
@@ -137,11 +137,11 @@ var mulRanges = []mulRange{
 }
 
 
-func TestMulRange(t *testing.T) {
-       for i, r := range mulRanges {
+func TestMulRangeN(t *testing.T) {
+       for i, r := range mulRangesN {
                prod := nat(nil).mulRange(r.a, r.b).string(10)
                if prod != r.prod {
-                       t.Errorf("%d: got %s; want %s", i, prod, r.prod)
+                       t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
                }
        }
 }