math/big: implement Int.Text, Int.Append

This makes the Int conversion routines match the respective strconv
and big.Float conversion routines.

Change-Id: I5cfcda1632ee52fe87c5bb75892bdda76cc3af15
Reviewed-on: https://go-review.googlesource.com/14994
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go
index 4693b17..524f775 100644
--- a/src/math/big/intconv.go
+++ b/src/math/big/intconv.go
@@ -12,14 +12,41 @@
 	"io"
 )
 
-func (x *Int) String() string {
-	switch {
-	case x == nil:
+// TODO(gri) Should rename itoa to utoa (there's no sign). That
+// would permit the introduction of itoa which is like utoa but
+// reserves a byte for a possible sign that's passed in. That
+// would permit Int.Text to be implemented w/o the need for
+// string copy if the number is negative.
+
+// Text returns the string representation of x in the given base.
+// Base must be between 2 and 36, inclusive. The result uses the
+// lower-case letters 'a' to 'z' for digit values >= 10. No base
+// prefix (such as "0x") is added to the string.
+func (x *Int) Text(base int) string {
+	if x == nil {
 		return "<nil>"
-	case x.neg:
-		return "-" + string(x.abs.itoa(10))
 	}
-	return string(x.abs.itoa(10))
+	s := string(x.abs.itoa(base))
+	if x.neg {
+		s = "-" + s
+	}
+	return s
+}
+
+// Append appends the string representation of x, as generated by
+// x.Text(base), to buf and returns the extended buffer.
+func (x *Int) Append(buf []byte, base int) []byte {
+	if x == nil {
+		return append(buf, "<nil>"...)
+	}
+	if x.neg {
+		buf = append(buf, '-')
+	}
+	return append(buf, x.abs.itoa(base)...)
+}
+
+func (x *Int) String() string {
+	return x.Text(10)
 }
 
 // write count copies of text to s
diff --git a/src/math/big/intconv_test.go b/src/math/big/intconv_test.go
index 2deb84b..5142081 100644
--- a/src/math/big/intconv_test.go
+++ b/src/math/big/intconv_test.go
@@ -17,19 +17,19 @@
 	val  int64
 	ok   bool
 }{
-	{in: "", ok: false},
-	{in: "a", ok: false},
-	{in: "z", ok: false},
-	{in: "+", ok: false},
-	{in: "-", ok: false},
-	{in: "0b", ok: false},
-	{in: "0x", ok: false},
-	{in: "2", base: 2, ok: false},
-	{in: "0b2", base: 0, ok: false},
-	{in: "08", ok: false},
-	{in: "8", base: 8, ok: false},
-	{in: "0xg", base: 0, ok: false},
-	{in: "g", base: 16, ok: false},
+	{in: ""},
+	{in: "a"},
+	{in: "z"},
+	{in: "+"},
+	{in: "-"},
+	{in: "0b"},
+	{in: "0x"},
+	{in: "2", base: 2},
+	{in: "0b2", base: 0},
+	{in: "08"},
+	{in: "8", base: 8},
+	{in: "0xg", base: 0},
+	{in: "g", base: 16},
 	{"0", "0", 0, 0, true},
 	{"0", "0", 10, 0, true},
 	{"0", "0", 16, 0, true},
@@ -41,7 +41,7 @@
 	{"-10", "-10", 16, -16, true},
 	{"+10", "10", 16, 16, true},
 	{"0x10", "16", 0, 16, true},
-	{in: "0x10", base: 16, ok: false},
+	{in: "0x10", base: 16},
 	{"-0x10", "-16", 0, -16, true},
 	{"+0x10", "16", 0, 16, true},
 	{"00", "0", 0, 0, true},
@@ -58,6 +58,57 @@
 	{"1001010111", "1001010111", 2, 0x257, true},
 }
 
+func TestIntText(t *testing.T) {
+	z := new(Int)
+	for _, test := range stringTests {
+		if !test.ok {
+			continue
+		}
+
+		_, ok := z.SetString(test.in, test.base)
+		if !ok {
+			t.Errorf("%v: failed to parse", test)
+			continue
+		}
+
+		base := test.base
+		if base == 0 {
+			base = 10
+		}
+
+		if got := z.Text(base); got != test.out {
+			t.Errorf("%v: got %s; want %s", test, got, test.out)
+		}
+	}
+}
+
+func TestAppendText(t *testing.T) {
+	z := new(Int)
+	var buf []byte
+	for _, test := range stringTests {
+		if !test.ok {
+			continue
+		}
+
+		_, ok := z.SetString(test.in, test.base)
+		if !ok {
+			t.Errorf("%v: failed to parse", test)
+			continue
+		}
+
+		base := test.base
+		if base == 0 {
+			base = 10
+		}
+
+		i := len(buf)
+		buf = z.Append(buf, base)
+		if got := string(buf[i:]); got != test.out {
+			t.Errorf("%v: got %s; want %s", test, got, test.out)
+		}
+	}
+}
+
 func format(base int) string {
 	switch base {
 	case 2:
@@ -79,15 +130,13 @@
 		z.SetInt64(test.val)
 
 		if test.base == 10 {
-			s := z.String()
-			if s != test.out {
-				t.Errorf("#%da got %s; want %s", i, s, test.out)
+			if got := z.String(); got != test.out {
+				t.Errorf("#%da got %s; want %s", i, got, test.out)
 			}
 		}
 
-		s := fmt.Sprintf(format(test.base), z)
-		if s != test.out {
-			t.Errorf("#%db got %s; want %s", i, s, test.out)
+		if got := fmt.Sprintf(format(test.base), z); got != test.out {
+			t.Errorf("#%db got %s; want %s", i, got, test.out)
 		}
 	}
 }