1
0
mirror of https://github.com/golang/go synced 2024-11-20 05:44:44 -07:00

big: implemented format support for fmt library, MulRange

- 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
This commit is contained in:
Robert Griesemer 2010-05-15 10:23:41 -07:00
parent cffdb1e830
commit dbb6232724
4 changed files with 234 additions and 79 deletions

View File

@ -6,6 +6,9 @@
package big package big
import "fmt"
// An Int represents a signed multi-precision integer. // An Int represents a signed multi-precision integer.
// The zero value for an Int represents the value 0. // The zero value for an Int represents the value 0.
type Int struct { type Int struct {
@ -19,12 +22,13 @@ var intOne = &Int{false, natOne}
// SetInt64 sets z to x and returns z. // SetInt64 sets z to x and returns z.
func (z *Int) SetInt64(x int64) *Int { func (z *Int) SetInt64(x int64) *Int {
z.neg = false neg := false
if x < 0 { if x < 0 {
z.neg = true neg = true
x = -x x = -x
} }
z.abs = z.abs.new(uint64(x)) z.abs = z.abs.setUint64(uint64(x))
z.neg = neg
return z return z
} }
@ -37,8 +41,8 @@ func NewInt(x int64) *Int {
// Set sets z to x. // Set sets z to x.
func (z *Int) Set(x *Int) *Int { func (z *Int) Set(x *Int) *Int {
z.neg = x.neg
z.abs = z.abs.set(x.abs) z.abs = z.abs.set(x.abs)
z.neg = x.neg
return z 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. // 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. // If y == 0, a division-by-zero run-time panic occurs.
// See QuoRem for more details. // 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 := "" s := ""
if z.neg { if x.neg {
s = "-" 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. // Int64 returns the int64 representation of z.
// If z cannot be represented in an int64, the result is undefined. // If z cannot be represented in an int64, the result is undefined.
func (z *Int) Int64() int64 { func (x *Int) Int64() int64 {
if len(z.abs) == 0 { if len(x.abs) == 0 {
return 0 return 0
} }
v := int64(z.abs[0]) v := int64(x.abs[0])
if _W == 32 && len(z.abs) > 1 { if _W == 32 && len(x.abs) > 1 {
v |= int64(z.abs[1]) << 32 v |= int64(x.abs[1]) << 32
} }
if z.neg { if x.neg {
v = -v v = -v
} }
return v return v

View File

@ -7,6 +7,7 @@ package big
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt"
"testing" "testing"
"testing/quick" "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 in string
out string
base int base int
out int64 val int64
ok bool ok bool
} }
var fromStringTests = []fromStringTest{ var stringTests = []stringTest{
fromStringTest{in: "", ok: false}, stringTest{in: "", ok: false},
fromStringTest{in: "a", ok: false}, stringTest{in: "a", ok: false},
fromStringTest{in: "z", ok: false}, stringTest{in: "z", ok: false},
fromStringTest{in: "+", ok: false}, stringTest{in: "+", ok: false},
fromStringTest{"0", 0, 0, true}, stringTest{in: "0b", ok: false},
fromStringTest{"0", 10, 0, true}, stringTest{in: "0x", ok: false},
fromStringTest{"0", 16, 0, true}, stringTest{in: "2", base: 2, ok: false},
fromStringTest{"10", 0, 10, true}, stringTest{in: "0b2", base: 0, ok: false},
fromStringTest{"10", 10, 10, true}, stringTest{in: "08", ok: false},
fromStringTest{"10", 16, 16, true}, stringTest{in: "8", base: 8, ok: false},
fromStringTest{"-10", 16, -16, true}, stringTest{in: "0xg", base: 0, ok: false},
fromStringTest{in: "0x", ok: false}, stringTest{in: "g", base: 16, ok: false},
fromStringTest{"0x10", 0, 16, true}, stringTest{"0", "0", 0, 0, true},
fromStringTest{in: "0x10", base: 16, ok: false}, stringTest{"0", "0", 10, 0, true},
fromStringTest{"-0x10", 0, -16, true}, stringTest{"0", "0", 16, 0, true},
fromStringTest{"00", 0, 0, true}, stringTest{"10", "10", 0, 10, true},
fromStringTest{"0", 8, 0, true}, stringTest{"10", "10", 10, 10, true},
fromStringTest{"07", 0, 7, true}, stringTest{"10", "10", 16, 16, true},
fromStringTest{"7", 8, 7, true}, stringTest{"-10", "-10", 16, -16, true},
fromStringTest{in: "08", ok: false}, stringTest{"0x10", "16", 0, 16, true},
fromStringTest{in: "8", base: 8, ok: false}, stringTest{in: "0x10", base: 16, ok: false},
fromStringTest{"023", 0, 19, true}, stringTest{"-0x10", "-16", 0, -16, true},
fromStringTest{"23", 8, 19, 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) { func TestSetString(t *testing.T) {
n2 := new(Int) tmp := new(Int)
for i, test := range fromStringTests { for i, test := range stringTests {
n1, ok1 := new(Int).SetString(test.in, test.base) n1, ok1 := new(Int).SetString(test.in, test.base)
n2, ok2 := n2.SetString(test.in, test.base) n2, ok2 := tmp.SetString(test.in, test.base)
expected := NewInt(test.out) expected := NewInt(test.val)
if ok1 != test.ok || ok2 != test.ok { if ok1 != test.ok || ok2 != test.ok {
t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok) t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok)
continue continue
@ -213,10 +310,10 @@ func TestSetString(t *testing.T) {
} }
if n1.Cmp(expected) != 0 { 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 { 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)
} }
} }
} }

View File

@ -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 { if x == 0 {
return z.make(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 { func (z nat) mulAddWW(x nat, y, r Word) nat {
m := len(x) m := len(x)
if m == 0 || y == 0 { if m == 0 || y == 0 {
return z.new(uint64(r)) // result is r return z.setUint64(uint64(r)) // result is r
} }
// m > 0 // m > 0
@ -456,13 +456,13 @@ func (z nat) mulRange(a, b uint64) nat {
switch { switch {
case a == 0: case a == 0:
// cut long ranges short (optimization) // cut long ranges short (optimization)
return z.new(0) return z.setUint64(0)
case a > b: case a > b:
return z.new(1) return z.setUint64(1)
case a == b: case a == b:
return z.new(a) return z.setUint64(a)
case a+1 == b: 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 m := (a + b) / 2
return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) 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 // If the base argument is 0, the string prefix determines the actual
// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the // 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) { func (z nat) scan(s string, base int) (nat, int, int) {
// determine base if necessary // determine base if necessary
@ -629,23 +630,25 @@ func (z nat) scan(s string, base int) (nat, int, int) {
if base == 0 { if base == 0 {
base = 10 base = 10
if n > 0 && s[0] == '0' { if n > 0 && s[0] == '0' {
if n > 1 && (s[1] == 'x' || s[1] == 'X') { base, i = 8, 1
if n == 2 { if n > 1 {
// Reject a string which is just '0x' as nonsense. switch s[1] {
return nil, 0, 0 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 // convert string
z = z[0:0] z = z.make(0)
for ; i < n; i++ { for ; i < n; i++ {
d := hexValue(s[i]) d := hexValue(s[i])
if 0 <= d && d < base { if 0 <= d && d < base {

View File

@ -111,25 +111,25 @@ func TestFunNN(t *testing.T) {
} }
type mulRange struct { type mulRangeN struct {
a, b uint64 a, b uint64
prod string prod string
} }
var mulRanges = []mulRange{ var mulRangesN = []mulRangeN{
mulRange{0, 0, "0"}, mulRangeN{0, 0, "0"},
mulRange{1, 1, "1"}, mulRangeN{1, 1, "1"},
mulRange{1, 2, "2"}, mulRangeN{1, 2, "2"},
mulRange{1, 3, "6"}, mulRangeN{1, 3, "6"},
mulRange{1, 3, "6"}, mulRangeN{10, 10, "10"},
mulRange{10, 10, "10"}, mulRangeN{0, 100, "0"},
mulRange{0, 100, "0"}, mulRangeN{0, 1e9, "0"},
mulRange{0, 1e9, "0"}, mulRangeN{1, 0, "1"}, // empty range
mulRange{100, 1, "1"}, // empty range mulRangeN{100, 1, "1"}, // empty range
mulRange{1, 10, "3628800"}, // 10! mulRangeN{1, 10, "3628800"}, // 10!
mulRange{1, 20, "2432902008176640000"}, // 20! mulRangeN{1, 20, "2432902008176640000"}, // 20!
mulRange{1, 100, mulRangeN{1, 100,
"933262154439441526816992388562667004907159682643816214685929" + "933262154439441526816992388562667004907159682643816214685929" +
"638952175999932299156089414639761565182862536979208272237582" + "638952175999932299156089414639761565182862536979208272237582" +
"51185210916864000000000000000000000000", // 100! "51185210916864000000000000000000000000", // 100!
@ -137,11 +137,11 @@ var mulRanges = []mulRange{
} }
func TestMulRange(t *testing.T) { func TestMulRangeN(t *testing.T) {
for i, r := range mulRanges { for i, r := range mulRangesN {
prod := nat(nil).mulRange(r.a, r.b).string(10) prod := nat(nil).mulRange(r.a, r.b).string(10)
if prod != r.prod { 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)
} }
} }
} }