Commit a0c5d284 authored by Robert Griesemer's avatar Robert Griesemer

math/big: split int conversion routines and tests into separate files

No other functional changes.

Change-Id: If0d9e6208d53478e70d991b6926ea196b2cccf2e
Reviewed-on: https://go-review.googlesource.com/3672Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent d135966e
......@@ -321,221 +321,6 @@ func (x *Int) Cmp(y *Int) (r int) {
return
}
func (x *Int) String() string {
switch {
case x == nil:
return "<nil>"
case x.neg:
return "-" + x.abs.decimalString()
}
return x.abs.decimalString()
}
func charset(ch rune) string {
switch ch {
case 'b':
return lowercaseDigits[0:2]
case 'o':
return lowercaseDigits[0:8]
case 'd', 's', 'v':
return lowercaseDigits[0:10]
case 'x':
return lowercaseDigits[0:16]
case 'X':
return uppercaseDigits[0:16]
}
return "" // unknown format
}
// write count copies of text to s
func writeMultiple(s fmt.State, text string, count int) {
if len(text) > 0 {
b := []byte(text)
for ; count > 0; count-- {
s.Write(b)
}
}
}
// Format is a support routine for fmt.Formatter. It accepts
// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
// (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
// Also supported are the full suite of package fmt's format
// verbs for integral types, including '+', '-', and ' '
// for sign control, '#' for leading zero in octal and for
// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X"
// respectively, specification of minimum digits precision,
// output field width, space or zero padding, and left or
// right justification.
//
func (x *Int) Format(s fmt.State, ch rune) {
cs := charset(ch)
// special cases
switch {
case cs == "":
// unknown format
fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String())
return
case x == nil:
fmt.Fprint(s, "<nil>")
return
}
// determine sign character
sign := ""
switch {
case x.neg:
sign = "-"
case s.Flag('+'): // supersedes ' ' when both specified
sign = "+"
case s.Flag(' '):
sign = " "
}
// determine prefix characters for indicating output base
prefix := ""
if s.Flag('#') {
switch ch {
case 'o': // octal
prefix = "0"
case 'x': // hexadecimal
prefix = "0x"
case 'X':
prefix = "0X"
}
}
// determine digits with base set by len(cs) and digit characters from cs
digits := x.abs.string(cs)
// number of characters for the three classes of number padding
var left int // space characters to left of digits for right justification ("%8d")
var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d")
var right int // space characters to right of digits for left justification ("%-8d")
// determine number padding from precision: the least number of digits to output
precision, precisionSet := s.Precision()
if precisionSet {
switch {
case len(digits) < precision:
zeroes = precision - len(digits) // count of zero padding
case digits == "0" && precision == 0:
return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
}
}
// determine field pad from width: the least number of characters to output
length := len(sign) + len(prefix) + zeroes + len(digits)
if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
switch d := width - length; {
case s.Flag('-'):
// pad on the right with spaces; supersedes '0' when both specified
right = d
case s.Flag('0') && !precisionSet:
// pad with zeroes unless precision also specified
zeroes = d
default:
// pad on the left with spaces
left = d
}
}
// print number as [left pad][sign][prefix][zero pad][digits][right pad]
writeMultiple(s, " ", left)
writeMultiple(s, sign, 1)
writeMultiple(s, prefix, 1)
writeMultiple(s, "0", zeroes)
writeMultiple(s, digits, 1)
writeMultiple(s, " ", right)
}
// scan sets z to the integer value corresponding to the longest possible prefix
// read from r representing a signed integer number in a given conversion base.
// It returns z, the actual conversion base used, and an error, if any. In the
// error case, the value of z is undefined but the returned value is nil. The
// syntax follows the syntax of integer literals in Go.
//
// The base argument must be 0 or a value from 2 through MaxBase. If the base
// 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, and a
// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
//
func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
// determine sign
neg, err := scanSign(r)
if err != nil {
return nil, 0, err
}
// determine mantissa
z.abs, base, _, err = z.abs.scan(r, base, false)
if err != nil {
return nil, base, err
}
z.neg = len(z.abs) > 0 && neg // 0 has no sign
return z, base, nil
}
func scanSign(r io.ByteScanner) (neg bool, err error) {
var ch byte
if ch, err = r.ReadByte(); err != nil {
return false, err
}
switch ch {
case '-':
neg = true
case '+':
// nothing to do
default:
r.UnreadByte()
}
return
}
// byteReader is a local wrapper around fmt.ScanState;
// it implements the ByteReader interface.
type byteReader struct {
fmt.ScanState
}
func (r byteReader) ReadByte() (byte, error) {
ch, size, err := r.ReadRune()
if size != 1 && err == nil {
err = fmt.Errorf("invalid rune %#U", ch)
}
return byte(ch), err
}
func (r byteReader) UnreadByte() error {
return r.UnreadRune()
}
// Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
func (z *Int) Scan(s fmt.ScanState, ch rune) error {
s.SkipSpace() // skip leading space characters
base := 0
switch ch {
case 'b':
base = 2
case 'o':
base = 8
case 'd':
base = 10
case 'x', 'X':
base = 16
case 's', 'v':
// let scan determine the base
default:
return errors.New("Int.Scan: invalid verb")
}
_, _, err := z.scan(byteReader{s}, base)
return err
}
// low32 returns the least significant 32 bits of z.
func low32(z nat) uint32 {
if len(z) == 0 {
......
......@@ -219,337 +219,6 @@ func TestMulRangeZ(t *testing.T) {
}
}
var stringTests = []struct {
in string
out string
base int
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},
{"0", "0", 0, 0, true},
{"0", "0", 10, 0, true},
{"0", "0", 16, 0, true},
{"+0", "0", 0, 0, true},
{"-0", "0", 0, 0, true},
{"10", "10", 0, 10, true},
{"10", "10", 10, 10, true},
{"10", "10", 16, 16, true},
{"-10", "-10", 16, -16, true},
{"+10", "10", 16, 16, true},
{"0x10", "16", 0, 16, true},
{in: "0x10", base: 16, ok: false},
{"-0x10", "-16", 0, -16, true},
{"+0x10", "16", 0, 16, true},
{"00", "0", 0, 0, true},
{"0", "0", 8, 0, true},
{"07", "7", 0, 7, true},
{"7", "7", 8, 7, true},
{"023", "19", 0, 19, true},
{"23", "23", 8, 19, true},
{"cafebabe", "cafebabe", 16, 0xcafebabe, true},
{"0b0", "0", 0, 0, true},
{"-111", "-111", 2, -7, true},
{"-0b111", "-7", 0, -7, true},
{"0b1001010111", "599", 0, 0x257, true},
{"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", i, s, test.out)
}
}
s := fmt.Sprintf(format(test.base), z)
if s != test.out {
t.Errorf("#%db got %s; want %s", i, s, test.out)
}
}
}
func TestSetString(t *testing.T) {
tmp := new(Int)
for i, test := range stringTests {
// initialize to a non-zero value so that issues with parsing
// 0 are detected
tmp.SetInt64(1234567890)
n1, ok1 := new(Int).SetString(test.in, test.base)
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
}
if !ok1 {
if n1 != nil {
t.Errorf("#%d (input '%s') n1 != nil", i, test.in)
}
continue
}
if !ok2 {
if n2 != nil {
t.Errorf("#%d (input '%s') n2 != nil", i, test.in)
}
continue
}
if ok1 && !isNormalized(n1) {
t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1)
}
if ok2 && !isNormalized(n2) {
t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2)
}
if n1.Cmp(expected) != 0 {
t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
}
if n2.Cmp(expected) != 0 {
t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
}
}
}
var formatTests = []struct {
input string
format string
output string
}{
{"<nil>", "%x", "<nil>"},
{"<nil>", "%#x", "<nil>"},
{"<nil>", "%#y", "%!y(big.Int=<nil>)"},
{"10", "%b", "1010"},
{"10", "%o", "12"},
{"10", "%d", "10"},
{"10", "%v", "10"},
{"10", "%x", "a"},
{"10", "%X", "A"},
{"-10", "%X", "-A"},
{"10", "%y", "%!y(big.Int=10)"},
{"-10", "%y", "%!y(big.Int=-10)"},
{"10", "%#b", "1010"},
{"10", "%#o", "012"},
{"10", "%#d", "10"},
{"10", "%#v", "10"},
{"10", "%#x", "0xa"},
{"10", "%#X", "0XA"},
{"-10", "%#X", "-0XA"},
{"10", "%#y", "%!y(big.Int=10)"},
{"-10", "%#y", "%!y(big.Int=-10)"},
{"1234", "%d", "1234"},
{"1234", "%3d", "1234"},
{"1234", "%4d", "1234"},
{"-1234", "%d", "-1234"},
{"1234", "% 5d", " 1234"},
{"1234", "%+5d", "+1234"},
{"1234", "%-5d", "1234 "},
{"1234", "%x", "4d2"},
{"1234", "%X", "4D2"},
{"-1234", "%3x", "-4d2"},
{"-1234", "%4x", "-4d2"},
{"-1234", "%5x", " -4d2"},
{"-1234", "%-5x", "-4d2 "},
{"1234", "%03d", "1234"},
{"1234", "%04d", "1234"},
{"1234", "%05d", "01234"},
{"1234", "%06d", "001234"},
{"-1234", "%06d", "-01234"},
{"1234", "%+06d", "+01234"},
{"1234", "% 06d", " 01234"},
{"1234", "%-6d", "1234 "},
{"1234", "%-06d", "1234 "},
{"-1234", "%-06d", "-1234 "},
{"1234", "%.3d", "1234"},
{"1234", "%.4d", "1234"},
{"1234", "%.5d", "01234"},
{"1234", "%.6d", "001234"},
{"-1234", "%.3d", "-1234"},
{"-1234", "%.4d", "-1234"},
{"-1234", "%.5d", "-01234"},
{"-1234", "%.6d", "-001234"},
{"1234", "%8.3d", " 1234"},
{"1234", "%8.4d", " 1234"},
{"1234", "%8.5d", " 01234"},
{"1234", "%8.6d", " 001234"},
{"-1234", "%8.3d", " -1234"},
{"-1234", "%8.4d", " -1234"},
{"-1234", "%8.5d", " -01234"},
{"-1234", "%8.6d", " -001234"},
{"1234", "%+8.3d", " +1234"},
{"1234", "%+8.4d", " +1234"},
{"1234", "%+8.5d", " +01234"},
{"1234", "%+8.6d", " +001234"},
{"-1234", "%+8.3d", " -1234"},
{"-1234", "%+8.4d", " -1234"},
{"-1234", "%+8.5d", " -01234"},
{"-1234", "%+8.6d", " -001234"},
{"1234", "% 8.3d", " 1234"},
{"1234", "% 8.4d", " 1234"},
{"1234", "% 8.5d", " 01234"},
{"1234", "% 8.6d", " 001234"},
{"-1234", "% 8.3d", " -1234"},
{"-1234", "% 8.4d", " -1234"},
{"-1234", "% 8.5d", " -01234"},
{"-1234", "% 8.6d", " -001234"},
{"1234", "%.3x", "4d2"},
{"1234", "%.4x", "04d2"},
{"1234", "%.5x", "004d2"},
{"1234", "%.6x", "0004d2"},
{"-1234", "%.3x", "-4d2"},
{"-1234", "%.4x", "-04d2"},
{"-1234", "%.5x", "-004d2"},
{"-1234", "%.6x", "-0004d2"},
{"1234", "%8.3x", " 4d2"},
{"1234", "%8.4x", " 04d2"},
{"1234", "%8.5x", " 004d2"},
{"1234", "%8.6x", " 0004d2"},
{"-1234", "%8.3x", " -4d2"},
{"-1234", "%8.4x", " -04d2"},
{"-1234", "%8.5x", " -004d2"},
{"-1234", "%8.6x", " -0004d2"},
{"1234", "%+8.3x", " +4d2"},
{"1234", "%+8.4x", " +04d2"},
{"1234", "%+8.5x", " +004d2"},
{"1234", "%+8.6x", " +0004d2"},
{"-1234", "%+8.3x", " -4d2"},
{"-1234", "%+8.4x", " -04d2"},
{"-1234", "%+8.5x", " -004d2"},
{"-1234", "%+8.6x", " -0004d2"},
{"1234", "% 8.3x", " 4d2"},
{"1234", "% 8.4x", " 04d2"},
{"1234", "% 8.5x", " 004d2"},
{"1234", "% 8.6x", " 0004d2"},
{"1234", "% 8.7x", " 00004d2"},
{"1234", "% 8.8x", " 000004d2"},
{"-1234", "% 8.3x", " -4d2"},
{"-1234", "% 8.4x", " -04d2"},
{"-1234", "% 8.5x", " -004d2"},
{"-1234", "% 8.6x", " -0004d2"},
{"-1234", "% 8.7x", "-00004d2"},
{"-1234", "% 8.8x", "-000004d2"},
{"1234", "%-8.3d", "1234 "},
{"1234", "%-8.4d", "1234 "},
{"1234", "%-8.5d", "01234 "},
{"1234", "%-8.6d", "001234 "},
{"1234", "%-8.7d", "0001234 "},
{"1234", "%-8.8d", "00001234"},
{"-1234", "%-8.3d", "-1234 "},
{"-1234", "%-8.4d", "-1234 "},
{"-1234", "%-8.5d", "-01234 "},
{"-1234", "%-8.6d", "-001234 "},
{"-1234", "%-8.7d", "-0001234"},
{"-1234", "%-8.8d", "-00001234"},
{"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1
{"0", "%.d", ""},
{"0", "%.0d", ""},
{"0", "%3.d", ""},
}
func TestFormat(t *testing.T) {
for i, test := range formatTests {
var x *Int
if test.input != "<nil>" {
var ok bool
x, ok = new(Int).SetString(test.input, 0)
if !ok {
t.Errorf("#%d failed reading input %s", i, test.input)
}
}
output := fmt.Sprintf(test.format, x)
if output != test.output {
t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output)
}
}
}
var scanTests = []struct {
input string
format string
output string
remaining int
}{
{"1010", "%b", "10", 0},
{"0b1010", "%v", "10", 0},
{"12", "%o", "10", 0},
{"012", "%v", "10", 0},
{"10", "%d", "10", 0},
{"10", "%v", "10", 0},
{"a", "%x", "10", 0},
{"0xa", "%v", "10", 0},
{"A", "%X", "10", 0},
{"-A", "%X", "-10", 0},
{"+0b1011001", "%v", "89", 0},
{"0xA", "%v", "10", 0},
{"0 ", "%v", "0", 1},
{"2+3", "%v", "2", 2},
{"0XABC 12", "%v", "2748", 3},
}
func TestScan(t *testing.T) {
var buf bytes.Buffer
for i, test := range scanTests {
x := new(Int)
buf.Reset()
buf.WriteString(test.input)
if _, err := fmt.Fscanf(&buf, test.format, x); err != nil {
t.Errorf("#%d error: %s", i, err)
}
if x.String() != test.output {
t.Errorf("#%d got %s; want %s", i, x.String(), test.output)
}
if buf.Len() != test.remaining {
t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining)
}
}
}
// Examples from the Go Language Spec, section "Arithmetic operators"
var divisionSignsTests = []struct {
x, y int64
......
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements int-to-string conversion functions.
package big
import (
"errors"
"fmt"
"io"
)
func (x *Int) String() string {
switch {
case x == nil:
return "<nil>"
case x.neg:
return "-" + x.abs.decimalString()
}
return x.abs.decimalString()
}
func charset(ch rune) string {
switch ch {
case 'b':
return lowercaseDigits[0:2]
case 'o':
return lowercaseDigits[0:8]
case 'd', 's', 'v':
return lowercaseDigits[0:10]
case 'x':
return lowercaseDigits[0:16]
case 'X':
return uppercaseDigits[0:16]
}
return "" // unknown format
}
// write count copies of text to s
func writeMultiple(s fmt.State, text string, count int) {
if len(text) > 0 {
b := []byte(text)
for ; count > 0; count-- {
s.Write(b)
}
}
}
// Format is a support routine for fmt.Formatter. It accepts
// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
// (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
// Also supported are the full suite of package fmt's format
// verbs for integral types, including '+', '-', and ' '
// for sign control, '#' for leading zero in octal and for
// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X"
// respectively, specification of minimum digits precision,
// output field width, space or zero padding, and left or
// right justification.
//
func (x *Int) Format(s fmt.State, ch rune) {
cs := charset(ch)
// special cases
switch {
case cs == "":
// unknown format
fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String())
return
case x == nil:
fmt.Fprint(s, "<nil>")
return
}
// determine sign character
sign := ""
switch {
case x.neg:
sign = "-"
case s.Flag('+'): // supersedes ' ' when both specified
sign = "+"
case s.Flag(' '):
sign = " "
}
// determine prefix characters for indicating output base
prefix := ""
if s.Flag('#') {
switch ch {
case 'o': // octal
prefix = "0"
case 'x': // hexadecimal
prefix = "0x"
case 'X':
prefix = "0X"
}
}
// determine digits with base set by len(cs) and digit characters from cs
digits := x.abs.string(cs)
// number of characters for the three classes of number padding
var left int // space characters to left of digits for right justification ("%8d")
var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d")
var right int // space characters to right of digits for left justification ("%-8d")
// determine number padding from precision: the least number of digits to output
precision, precisionSet := s.Precision()
if precisionSet {
switch {
case len(digits) < precision:
zeroes = precision - len(digits) // count of zero padding
case digits == "0" && precision == 0:
return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
}
}
// determine field pad from width: the least number of characters to output
length := len(sign) + len(prefix) + zeroes + len(digits)
if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
switch d := width - length; {
case s.Flag('-'):
// pad on the right with spaces; supersedes '0' when both specified
right = d
case s.Flag('0') && !precisionSet:
// pad with zeroes unless precision also specified
zeroes = d
default:
// pad on the left with spaces
left = d
}
}
// print number as [left pad][sign][prefix][zero pad][digits][right pad]
writeMultiple(s, " ", left)
writeMultiple(s, sign, 1)
writeMultiple(s, prefix, 1)
writeMultiple(s, "0", zeroes)
writeMultiple(s, digits, 1)
writeMultiple(s, " ", right)
}
// scan sets z to the integer value corresponding to the longest possible prefix
// read from r representing a signed integer number in a given conversion base.
// It returns z, the actual conversion base used, and an error, if any. In the
// error case, the value of z is undefined but the returned value is nil. The
// syntax follows the syntax of integer literals in Go.
//
// The base argument must be 0 or a value from 2 through MaxBase. If the base
// 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, and a
// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
//
func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
// determine sign
neg, err := scanSign(r)
if err != nil {
return nil, 0, err
}
// determine mantissa
z.abs, base, _, err = z.abs.scan(r, base, false)
if err != nil {
return nil, base, err
}
z.neg = len(z.abs) > 0 && neg // 0 has no sign
return z, base, nil
}
func scanSign(r io.ByteScanner) (neg bool, err error) {
var ch byte
if ch, err = r.ReadByte(); err != nil {
return false, err
}
switch ch {
case '-':
neg = true
case '+':
// nothing to do
default:
r.UnreadByte()
}
return
}
// byteReader is a local wrapper around fmt.ScanState;
// it implements the ByteReader interface.
type byteReader struct {
fmt.ScanState
}
func (r byteReader) ReadByte() (byte, error) {
ch, size, err := r.ReadRune()
if size != 1 && err == nil {
err = fmt.Errorf("invalid rune %#U", ch)
}
return byte(ch), err
}
func (r byteReader) UnreadByte() error {
return r.UnreadRune()
}
// Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
func (z *Int) Scan(s fmt.ScanState, ch rune) error {
s.SkipSpace() // skip leading space characters
base := 0
switch ch {
case 'b':
base = 2
case 'o':
base = 8
case 'd':
base = 10
case 'x', 'X':
base = 16
case 's', 'v':
// let scan determine the base
default:
return errors.New("Int.Scan: invalid verb")
}
_, _, err := z.scan(byteReader{s}, base)
return err
}
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package big
import (
"bytes"
"fmt"
"testing"
)
var stringTests = []struct {
in string
out string
base int
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},
{"0", "0", 0, 0, true},
{"0", "0", 10, 0, true},
{"0", "0", 16, 0, true},
{"+0", "0", 0, 0, true},
{"-0", "0", 0, 0, true},
{"10", "10", 0, 10, true},
{"10", "10", 10, 10, true},
{"10", "10", 16, 16, true},
{"-10", "-10", 16, -16, true},
{"+10", "10", 16, 16, true},
{"0x10", "16", 0, 16, true},
{in: "0x10", base: 16, ok: false},
{"-0x10", "-16", 0, -16, true},
{"+0x10", "16", 0, 16, true},
{"00", "0", 0, 0, true},
{"0", "0", 8, 0, true},
{"07", "7", 0, 7, true},
{"7", "7", 8, 7, true},
{"023", "19", 0, 19, true},
{"23", "23", 8, 19, true},
{"cafebabe", "cafebabe", 16, 0xcafebabe, true},
{"0b0", "0", 0, 0, true},
{"-111", "-111", 2, -7, true},
{"-0b111", "-7", 0, -7, true},
{"0b1001010111", "599", 0, 0x257, true},
{"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", i, s, test.out)
}
}
s := fmt.Sprintf(format(test.base), z)
if s != test.out {
t.Errorf("#%db got %s; want %s", i, s, test.out)
}
}
}
func TestSetString(t *testing.T) {
tmp := new(Int)
for i, test := range stringTests {
// initialize to a non-zero value so that issues with parsing
// 0 are detected
tmp.SetInt64(1234567890)
n1, ok1 := new(Int).SetString(test.in, test.base)
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
}
if !ok1 {
if n1 != nil {
t.Errorf("#%d (input '%s') n1 != nil", i, test.in)
}
continue
}
if !ok2 {
if n2 != nil {
t.Errorf("#%d (input '%s') n2 != nil", i, test.in)
}
continue
}
if ok1 && !isNormalized(n1) {
t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1)
}
if ok2 && !isNormalized(n2) {
t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2)
}
if n1.Cmp(expected) != 0 {
t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
}
if n2.Cmp(expected) != 0 {
t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
}
}
}
var formatTests = []struct {
input string
format string
output string
}{
{"<nil>", "%x", "<nil>"},
{"<nil>", "%#x", "<nil>"},
{"<nil>", "%#y", "%!y(big.Int=<nil>)"},
{"10", "%b", "1010"},
{"10", "%o", "12"},
{"10", "%d", "10"},
{"10", "%v", "10"},
{"10", "%x", "a"},
{"10", "%X", "A"},
{"-10", "%X", "-A"},
{"10", "%y", "%!y(big.Int=10)"},
{"-10", "%y", "%!y(big.Int=-10)"},
{"10", "%#b", "1010"},
{"10", "%#o", "012"},
{"10", "%#d", "10"},
{"10", "%#v", "10"},
{"10", "%#x", "0xa"},
{"10", "%#X", "0XA"},
{"-10", "%#X", "-0XA"},
{"10", "%#y", "%!y(big.Int=10)"},
{"-10", "%#y", "%!y(big.Int=-10)"},
{"1234", "%d", "1234"},
{"1234", "%3d", "1234"},
{"1234", "%4d", "1234"},
{"-1234", "%d", "-1234"},
{"1234", "% 5d", " 1234"},
{"1234", "%+5d", "+1234"},
{"1234", "%-5d", "1234 "},
{"1234", "%x", "4d2"},
{"1234", "%X", "4D2"},
{"-1234", "%3x", "-4d2"},
{"-1234", "%4x", "-4d2"},
{"-1234", "%5x", " -4d2"},
{"-1234", "%-5x", "-4d2 "},
{"1234", "%03d", "1234"},
{"1234", "%04d", "1234"},
{"1234", "%05d", "01234"},
{"1234", "%06d", "001234"},
{"-1234", "%06d", "-01234"},
{"1234", "%+06d", "+01234"},
{"1234", "% 06d", " 01234"},
{"1234", "%-6d", "1234 "},
{"1234", "%-06d", "1234 "},
{"-1234", "%-06d", "-1234 "},
{"1234", "%.3d", "1234"},
{"1234", "%.4d", "1234"},
{"1234", "%.5d", "01234"},
{"1234", "%.6d", "001234"},
{"-1234", "%.3d", "-1234"},
{"-1234", "%.4d", "-1234"},
{"-1234", "%.5d", "-01234"},
{"-1234", "%.6d", "-001234"},
{"1234", "%8.3d", " 1234"},
{"1234", "%8.4d", " 1234"},
{"1234", "%8.5d", " 01234"},
{"1234", "%8.6d", " 001234"},
{"-1234", "%8.3d", " -1234"},
{"-1234", "%8.4d", " -1234"},
{"-1234", "%8.5d", " -01234"},
{"-1234", "%8.6d", " -001234"},
{"1234", "%+8.3d", " +1234"},
{"1234", "%+8.4d", " +1234"},
{"1234", "%+8.5d", " +01234"},
{"1234", "%+8.6d", " +001234"},
{"-1234", "%+8.3d", " -1234"},
{"-1234", "%+8.4d", " -1234"},
{"-1234", "%+8.5d", " -01234"},
{"-1234", "%+8.6d", " -001234"},
{"1234", "% 8.3d", " 1234"},
{"1234", "% 8.4d", " 1234"},
{"1234", "% 8.5d", " 01234"},
{"1234", "% 8.6d", " 001234"},
{"-1234", "% 8.3d", " -1234"},
{"-1234", "% 8.4d", " -1234"},
{"-1234", "% 8.5d", " -01234"},
{"-1234", "% 8.6d", " -001234"},
{"1234", "%.3x", "4d2"},
{"1234", "%.4x", "04d2"},
{"1234", "%.5x", "004d2"},
{"1234", "%.6x", "0004d2"},
{"-1234", "%.3x", "-4d2"},
{"-1234", "%.4x", "-04d2"},
{"-1234", "%.5x", "-004d2"},
{"-1234", "%.6x", "-0004d2"},
{"1234", "%8.3x", " 4d2"},
{"1234", "%8.4x", " 04d2"},
{"1234", "%8.5x", " 004d2"},
{"1234", "%8.6x", " 0004d2"},
{"-1234", "%8.3x", " -4d2"},
{"-1234", "%8.4x", " -04d2"},
{"-1234", "%8.5x", " -004d2"},
{"-1234", "%8.6x", " -0004d2"},
{"1234", "%+8.3x", " +4d2"},
{"1234", "%+8.4x", " +04d2"},
{"1234", "%+8.5x", " +004d2"},
{"1234", "%+8.6x", " +0004d2"},
{"-1234", "%+8.3x", " -4d2"},
{"-1234", "%+8.4x", " -04d2"},
{"-1234", "%+8.5x", " -004d2"},
{"-1234", "%+8.6x", " -0004d2"},
{"1234", "% 8.3x", " 4d2"},
{"1234", "% 8.4x", " 04d2"},
{"1234", "% 8.5x", " 004d2"},
{"1234", "% 8.6x", " 0004d2"},
{"1234", "% 8.7x", " 00004d2"},
{"1234", "% 8.8x", " 000004d2"},
{"-1234", "% 8.3x", " -4d2"},
{"-1234", "% 8.4x", " -04d2"},
{"-1234", "% 8.5x", " -004d2"},
{"-1234", "% 8.6x", " -0004d2"},
{"-1234", "% 8.7x", "-00004d2"},
{"-1234", "% 8.8x", "-000004d2"},
{"1234", "%-8.3d", "1234 "},
{"1234", "%-8.4d", "1234 "},
{"1234", "%-8.5d", "01234 "},
{"1234", "%-8.6d", "001234 "},
{"1234", "%-8.7d", "0001234 "},
{"1234", "%-8.8d", "00001234"},
{"-1234", "%-8.3d", "-1234 "},
{"-1234", "%-8.4d", "-1234 "},
{"-1234", "%-8.5d", "-01234 "},
{"-1234", "%-8.6d", "-001234 "},
{"-1234", "%-8.7d", "-0001234"},
{"-1234", "%-8.8d", "-00001234"},
{"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1
{"0", "%.d", ""},
{"0", "%.0d", ""},
{"0", "%3.d", ""},
}
func TestFormat(t *testing.T) {
for i, test := range formatTests {
var x *Int
if test.input != "<nil>" {
var ok bool
x, ok = new(Int).SetString(test.input, 0)
if !ok {
t.Errorf("#%d failed reading input %s", i, test.input)
}
}
output := fmt.Sprintf(test.format, x)
if output != test.output {
t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output)
}
}
}
var scanTests = []struct {
input string
format string
output string
remaining int
}{
{"1010", "%b", "10", 0},
{"0b1010", "%v", "10", 0},
{"12", "%o", "10", 0},
{"012", "%v", "10", 0},
{"10", "%d", "10", 0},
{"10", "%v", "10", 0},
{"a", "%x", "10", 0},
{"0xa", "%v", "10", 0},
{"A", "%X", "10", 0},
{"-A", "%X", "-10", 0},
{"+0b1011001", "%v", "89", 0},
{"0xA", "%v", "10", 0},
{"0 ", "%v", "0", 1},
{"2+3", "%v", "2", 2},
{"0XABC 12", "%v", "2748", 3},
}
func TestScan(t *testing.T) {
var buf bytes.Buffer
for i, test := range scanTests {
x := new(Int)
buf.Reset()
buf.WriteString(test.input)
if _, err := fmt.Fscanf(&buf, test.format, x); err != nil {
t.Errorf("#%d error: %s", i, err)
}
if x.String() != test.output {
t.Errorf("#%d got %s; want %s", i, x.String(), test.output)
}
if buf.Len() != test.remaining {
t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining)
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment