mirror of https://github.com/golang/go.git
math/big: split nat conversion routines and tests into separate files
No functional changes. Change-Id: Ibbb705b167603d30467f3ebb83a3bb39845306a5 Reviewed-on: https://go-review.googlesource.com/3671 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
9f22de7aaf
commit
d135966ef6
|
|
@ -28,13 +28,7 @@ package big
|
|||
// These are the building blocks for the operations on signed integers
|
||||
// and rationals.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
)
|
||||
import "math/rand"
|
||||
|
||||
// An unsigned integer x of the form
|
||||
//
|
||||
|
|
@ -610,481 +604,6 @@ func (x nat) bitLen() int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// MaxBase is the largest number base accepted for string conversions.
|
||||
const MaxBase = 'z' - 'a' + 10 + 1
|
||||
|
||||
// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M.
|
||||
// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word.
|
||||
// In other words, at most n digits in base b fit into a Word.
|
||||
// TODO(gri) replace this with a table, generated at build time.
|
||||
func maxPow(b Word) (p Word, n int) {
|
||||
p, n = b, 1 // assuming b <= _M
|
||||
for max := _M / b; p <= max; {
|
||||
// p == b**n && p <= max
|
||||
p *= b
|
||||
n++
|
||||
}
|
||||
// p == b**n && p <= _M
|
||||
return
|
||||
}
|
||||
|
||||
// pow returns x**n for n > 0, and 1 otherwise.
|
||||
func pow(x Word, n int) (p Word) {
|
||||
// n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1
|
||||
// thus x**n == product of x**(2**i) for all i where bi == 1
|
||||
// (Russian Peasant Method for exponentiation)
|
||||
p = 1
|
||||
for n > 0 {
|
||||
if n&1 != 0 {
|
||||
p *= x
|
||||
}
|
||||
x *= x
|
||||
n >>= 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// scan scans the number corresponding to the longest possible prefix
|
||||
// from r representing an unsigned number in a given conversion base.
|
||||
// It returns the corresponding natural number res, the actual base b,
|
||||
// a digit count, and an error err, if any.
|
||||
//
|
||||
// number = [ prefix ] mantissa .
|
||||
// prefix = "0" [ "x" | "X" | "b" | "B" ] .
|
||||
// mantissa = digits | digits "." [ digits ] | "." digits .
|
||||
// digits = digit { digit } .
|
||||
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
|
||||
//
|
||||
// The base argument must be 0 or a value between 0 through MaxBase.
|
||||
//
|
||||
// For base 0, the number prefix determines the actual base: A prefix of
|
||||
// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix
|
||||
// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise
|
||||
// the selected base is 10 and no prefix is permitted.
|
||||
//
|
||||
// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply
|
||||
// stands for a zero digit), and a period followed by a fractional part
|
||||
// is permitted. The result value is computed as if there were no period
|
||||
// present; and the count value is used to determine the fractional part.
|
||||
//
|
||||
// A result digit count > 0 corresponds to the number of (non-prefix) digits
|
||||
// parsed. A digit count <= 0 indicates the presence of a period (if fracOk
|
||||
// is set, only), and -count is the number of fractional digits found.
|
||||
// In this case, the value of the scanned number is res * 10**count.
|
||||
//
|
||||
func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) {
|
||||
// reject illegal bases
|
||||
if base != 0 && base < 2 || base > MaxBase {
|
||||
err = errors.New("illegal number base")
|
||||
return
|
||||
}
|
||||
|
||||
// one char look-ahead
|
||||
ch, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// determine actual base
|
||||
b = base
|
||||
if base == 0 {
|
||||
// actual base is 10 unless there's a base prefix
|
||||
b = 10
|
||||
if ch == '0' {
|
||||
count = 1
|
||||
switch ch, err = r.ReadByte(); err {
|
||||
case nil:
|
||||
// possibly one of 0x, 0X, 0b, 0B
|
||||
if !fracOk {
|
||||
b = 8
|
||||
}
|
||||
switch ch {
|
||||
case 'x', 'X':
|
||||
b = 16
|
||||
case 'b', 'B':
|
||||
b = 2
|
||||
}
|
||||
switch b {
|
||||
case 16, 2:
|
||||
count = 0 // prefix is not counted
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
// io.EOF is also an error in this case
|
||||
return
|
||||
}
|
||||
case 8:
|
||||
count = 0 // prefix is not counted
|
||||
}
|
||||
case io.EOF:
|
||||
// input is "0"
|
||||
res = z[:0]
|
||||
err = nil
|
||||
return
|
||||
default:
|
||||
// read error
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert string
|
||||
// Algorithm: Collect digits in groups of at most n digits in di
|
||||
// and then use mulAddWW for every such group to add them to the
|
||||
// result.
|
||||
z = z[:0]
|
||||
b1 := Word(b)
|
||||
bn, n := maxPow(b1) // at most n digits in base b1 fit into Word
|
||||
di := Word(0) // 0 <= di < b1**i < bn
|
||||
i := 0 // 0 <= i < n
|
||||
dp := -1 // position of decimal point
|
||||
for {
|
||||
if fracOk && ch == '.' {
|
||||
fracOk = false
|
||||
dp = count
|
||||
// advance
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// convert rune into digit value d1
|
||||
var d1 Word
|
||||
switch {
|
||||
case '0' <= ch && ch <= '9':
|
||||
d1 = Word(ch - '0')
|
||||
case 'a' <= ch && ch <= 'z':
|
||||
d1 = Word(ch - 'a' + 10)
|
||||
case 'A' <= ch && ch <= 'Z':
|
||||
d1 = Word(ch - 'A' + 10)
|
||||
default:
|
||||
d1 = MaxBase + 1
|
||||
}
|
||||
if d1 >= b1 {
|
||||
r.UnreadByte() // ch does not belong to number anymore
|
||||
break
|
||||
}
|
||||
count++
|
||||
|
||||
// collect d1 in di
|
||||
di = di*b1 + d1
|
||||
i++
|
||||
|
||||
// if di is "full", add it to the result
|
||||
if i == n {
|
||||
z = z.mulAddWW(z, bn, di)
|
||||
di = 0
|
||||
i = 0
|
||||
}
|
||||
|
||||
// advance
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
// no digits found
|
||||
switch {
|
||||
case base == 0 && b == 8:
|
||||
// there was only the octal prefix 0 (possibly followed by digits > 7);
|
||||
// count as one digit and return base 10, not 8
|
||||
count = 1
|
||||
b = 10
|
||||
case base != 0 || b != 8:
|
||||
// there was neither a mantissa digit nor the octal prefix 0
|
||||
err = errors.New("syntax error scanning number")
|
||||
}
|
||||
return
|
||||
}
|
||||
// count > 0
|
||||
|
||||
// add remaining digits to result
|
||||
if i > 0 {
|
||||
z = z.mulAddWW(z, pow(b1, i), di)
|
||||
}
|
||||
res = z.norm()
|
||||
|
||||
// adjust for fraction, if any
|
||||
if dp >= 0 {
|
||||
// 0 <= dp <= count > 0
|
||||
count = dp - count
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Character sets for string conversion.
|
||||
const (
|
||||
lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
)
|
||||
|
||||
// decimalString returns a decimal representation of x.
|
||||
// It calls x.string with the charset "0123456789".
|
||||
func (x nat) decimalString() string {
|
||||
return x.string(lowercaseDigits[:10])
|
||||
}
|
||||
|
||||
// hexString returns a hexadecimal representation of x.
|
||||
// It calls x.string with the charset "0123456789abcdef".
|
||||
func (x nat) hexString() string {
|
||||
return x.string(lowercaseDigits[:16])
|
||||
}
|
||||
|
||||
// string converts x to a string using digits from a charset; a digit with
|
||||
// value d is represented by charset[d]. The conversion base is determined
|
||||
// by len(charset), which must be >= 2 and <= 256.
|
||||
func (x nat) string(charset string) string {
|
||||
b := Word(len(charset))
|
||||
|
||||
// special cases
|
||||
switch {
|
||||
case b < 2 || b > 256:
|
||||
panic("invalid character set length")
|
||||
case len(x) == 0:
|
||||
return string(charset[0])
|
||||
}
|
||||
|
||||
// allocate buffer for conversion
|
||||
i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most
|
||||
s := make([]byte, i)
|
||||
|
||||
// convert power of two and non power of two bases separately
|
||||
if b == b&-b {
|
||||
// shift is base-b digit size in bits
|
||||
shift := trailingZeroBits(b) // shift > 0 because b >= 2
|
||||
mask := Word(1)<<shift - 1
|
||||
w := x[0]
|
||||
nbits := uint(_W) // number of unprocessed bits in w
|
||||
|
||||
// convert less-significant words
|
||||
for k := 1; k < len(x); k++ {
|
||||
// convert full digits
|
||||
for nbits >= shift {
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
w >>= shift
|
||||
nbits -= shift
|
||||
}
|
||||
|
||||
// convert any partial leading digit and advance to next word
|
||||
if nbits == 0 {
|
||||
// no partial digit remaining, just advance
|
||||
w = x[k]
|
||||
nbits = _W
|
||||
} else {
|
||||
// partial digit in current (k-1) and next (k) word
|
||||
w |= x[k] << nbits
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
|
||||
// advance
|
||||
w = x[k] >> (shift - nbits)
|
||||
nbits = _W - (shift - nbits)
|
||||
}
|
||||
}
|
||||
|
||||
// convert digits of most-significant word (omit leading zeros)
|
||||
for nbits >= 0 && w != 0 {
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
w >>= shift
|
||||
nbits -= shift
|
||||
}
|
||||
|
||||
} else {
|
||||
bb, ndigits := maxPow(Word(b))
|
||||
|
||||
// construct table of successive squares of bb*leafSize to use in subdivisions
|
||||
// result (table != nil) <=> (len(x) > leafSize > 0)
|
||||
table := divisors(len(x), b, ndigits, bb)
|
||||
|
||||
// preserve x, create local copy for use by convertWords
|
||||
q := nat(nil).set(x)
|
||||
|
||||
// convert q to string s in base b
|
||||
q.convertWords(s, charset, b, ndigits, bb, table)
|
||||
|
||||
// strip leading zeros
|
||||
// (x != 0; thus s must contain at least one non-zero digit
|
||||
// and the loop will terminate)
|
||||
i = 0
|
||||
for zero := charset[0]; s[i] == zero; {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return string(s[i:])
|
||||
}
|
||||
|
||||
// Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
|
||||
// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
|
||||
// repeated nat/Word division.
|
||||
//
|
||||
// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
|
||||
// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
|
||||
// Recursive conversion divides q by its approximate square root, yielding two parts, each half
|
||||
// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s
|
||||
// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and
|
||||
// is made better by splitting the subblocks recursively. Best is to split blocks until one more
|
||||
// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the
|
||||
// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the
|
||||
// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and
|
||||
// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
|
||||
// specific hardware.
|
||||
//
|
||||
func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) {
|
||||
// split larger blocks recursively
|
||||
if table != nil {
|
||||
// len(q) > leafSize > 0
|
||||
var r nat
|
||||
index := len(table) - 1
|
||||
for len(q) > leafSize {
|
||||
// find divisor close to sqrt(q) if possible, but in any case < q
|
||||
maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length
|
||||
minLength := maxLength >> 1 // ~= log2 sqrt(q)
|
||||
for index > 0 && table[index-1].nbits > minLength {
|
||||
index-- // desired
|
||||
}
|
||||
if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 {
|
||||
index--
|
||||
if index < 0 {
|
||||
panic("internal inconsistency")
|
||||
}
|
||||
}
|
||||
|
||||
// split q into the two digit number (q'*bbb + r) to form independent subblocks
|
||||
q, r = q.div(r, q, table[index].bbb)
|
||||
|
||||
// convert subblocks and collect results in s[:h] and s[h:]
|
||||
h := len(s) - table[index].ndigits
|
||||
r.convertWords(s[h:], charset, b, ndigits, bb, table[0:index])
|
||||
s = s[:h] // == q.convertWords(s, charset, b, ndigits, bb, table[0:index+1])
|
||||
}
|
||||
}
|
||||
|
||||
// having split any large blocks now process the remaining (small) block iteratively
|
||||
i := len(s)
|
||||
var r Word
|
||||
if b == 10 {
|
||||
// hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants)
|
||||
for len(q) > 0 {
|
||||
// extract least significant, base bb "digit"
|
||||
q, r = q.divW(q, bb)
|
||||
for j := 0; j < ndigits && i > 0; j++ {
|
||||
i--
|
||||
// avoid % computation since r%10 == r - int(r/10)*10;
|
||||
// this appears to be faster for BenchmarkString10000Base10
|
||||
// and smaller strings (but a bit slower for larger ones)
|
||||
t := r / 10
|
||||
s[i] = charset[r-t<<3-t-t] // TODO(gri) replace w/ t*10 once compiler produces better code
|
||||
r = t
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for len(q) > 0 {
|
||||
// extract least significant, base bb "digit"
|
||||
q, r = q.divW(q, bb)
|
||||
for j := 0; j < ndigits && i > 0; j++ {
|
||||
i--
|
||||
s[i] = charset[r%b]
|
||||
r /= b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepend high-order zeroes
|
||||
zero := charset[0]
|
||||
for i > 0 { // while need more leading zeroes
|
||||
i--
|
||||
s[i] = zero
|
||||
}
|
||||
}
|
||||
|
||||
// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion)
|
||||
// Benchmark and configure leafSize using: go test -bench="Leaf"
|
||||
// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
|
||||
// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
|
||||
var leafSize int = 8 // number of Word-size binary values treat as a monolithic block
|
||||
|
||||
type divisor struct {
|
||||
bbb nat // divisor
|
||||
nbits int // bit length of divisor (discounting leading zeroes) ~= log2(bbb)
|
||||
ndigits int // digit length of divisor in terms of output base digits
|
||||
}
|
||||
|
||||
var cacheBase10 struct {
|
||||
sync.Mutex
|
||||
table [64]divisor // cached divisors for base 10
|
||||
}
|
||||
|
||||
// expWW computes x**y
|
||||
func (z nat) expWW(x, y Word) nat {
|
||||
return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil)
|
||||
}
|
||||
|
||||
// construct table of powers of bb*leafSize to use in subdivisions
|
||||
func divisors(m int, b Word, ndigits int, bb Word) []divisor {
|
||||
// only compute table when recursive conversion is enabled and x is large
|
||||
if leafSize == 0 || m <= leafSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
// determine k where (bb**leafSize)**(2**k) >= sqrt(x)
|
||||
k := 1
|
||||
for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 {
|
||||
k++
|
||||
}
|
||||
|
||||
// reuse and extend existing table of divisors or create new table as appropriate
|
||||
var table []divisor // for b == 10, table overlaps with cacheBase10.table
|
||||
if b == 10 {
|
||||
cacheBase10.Lock()
|
||||
table = cacheBase10.table[0:k] // reuse old table for this conversion
|
||||
} else {
|
||||
table = make([]divisor, k) // create new table for this conversion
|
||||
}
|
||||
|
||||
// extend table
|
||||
if table[k-1].ndigits == 0 {
|
||||
// add new entries as needed
|
||||
var larger nat
|
||||
for i := 0; i < k; i++ {
|
||||
if table[i].ndigits == 0 {
|
||||
if i == 0 {
|
||||
table[0].bbb = nat(nil).expWW(bb, Word(leafSize))
|
||||
table[0].ndigits = ndigits * leafSize
|
||||
} else {
|
||||
table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
|
||||
table[i].ndigits = 2 * table[i-1].ndigits
|
||||
}
|
||||
|
||||
// optimization: exploit aggregated extra bits in macro blocks
|
||||
larger = nat(nil).set(table[i].bbb)
|
||||
for mulAddVWW(larger, larger, b, 0) == 0 {
|
||||
table[i].bbb = table[i].bbb.set(larger)
|
||||
table[i].ndigits++
|
||||
}
|
||||
|
||||
table[i].nbits = table[i].bbb.bitLen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b == 10 {
|
||||
cacheBase10.Unlock()
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
const deBruijn32 = 0x077CB531
|
||||
|
||||
var deBruijn32Lookup = []byte{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
package big
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -206,424 +205,6 @@ func BenchmarkMul(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func toString(x nat, charset string) string {
|
||||
base := len(charset)
|
||||
|
||||
// special cases
|
||||
switch {
|
||||
case base < 2:
|
||||
panic("illegal base")
|
||||
case len(x) == 0:
|
||||
return string(charset[0])
|
||||
}
|
||||
|
||||
// allocate buffer for conversion
|
||||
i := x.bitLen()/log2(Word(base)) + 1 // +1: round up
|
||||
s := make([]byte, i)
|
||||
|
||||
// don't destroy x
|
||||
q := nat(nil).set(x)
|
||||
|
||||
// convert
|
||||
for len(q) > 0 {
|
||||
i--
|
||||
var r Word
|
||||
q, r = q.divW(q, Word(base))
|
||||
s[i] = charset[r]
|
||||
}
|
||||
|
||||
return string(s[i:])
|
||||
}
|
||||
|
||||
var strTests = []struct {
|
||||
x nat // nat value to be converted
|
||||
c string // conversion charset
|
||||
s string // expected result
|
||||
}{
|
||||
{nil, "01", "0"},
|
||||
{nat{1}, "01", "1"},
|
||||
{nat{0xc5}, "01", "11000101"},
|
||||
{nat{03271}, lowercaseDigits[:8], "3271"},
|
||||
{nat{10}, lowercaseDigits[:10], "10"},
|
||||
{nat{1234567890}, uppercaseDigits[:10], "1234567890"},
|
||||
{nat{0xdeadbeef}, lowercaseDigits[:16], "deadbeef"},
|
||||
{nat{0xdeadbeef}, uppercaseDigits[:16], "DEADBEEF"},
|
||||
{nat{0x229be7}, lowercaseDigits[:17], "1a2b3c"},
|
||||
{nat{0x309663e6}, uppercaseDigits[:32], "O9COV6"},
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
// test invalid character set explicitly
|
||||
var panicStr string
|
||||
func() {
|
||||
defer func() {
|
||||
panicStr = recover().(string)
|
||||
}()
|
||||
natOne.string("0")
|
||||
}()
|
||||
if panicStr != "invalid character set length" {
|
||||
t.Errorf("expected panic for invalid character set")
|
||||
}
|
||||
|
||||
for _, a := range strTests {
|
||||
s := a.x.string(a.c)
|
||||
if s != a.s {
|
||||
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
|
||||
}
|
||||
|
||||
x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false)
|
||||
if x.cmp(a.x) != 0 {
|
||||
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
|
||||
}
|
||||
if b != len(a.c) {
|
||||
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("scan%+v\n\tgot error = %s", a, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var natScanTests = []struct {
|
||||
s string // string to be scanned
|
||||
base int // input base
|
||||
frac bool // fraction ok
|
||||
x nat // expected nat
|
||||
b int // expected base
|
||||
count int // expected digit count
|
||||
ok bool // expected success
|
||||
next rune // next character (or 0, if at EOF)
|
||||
}{
|
||||
// error: illegal base
|
||||
{base: -1},
|
||||
{base: 37},
|
||||
|
||||
// error: no mantissa
|
||||
{},
|
||||
{s: "?"},
|
||||
{base: 10},
|
||||
{base: 36},
|
||||
{s: "?", base: 10},
|
||||
{s: "0x"},
|
||||
{s: "345", base: 2},
|
||||
|
||||
// error: incorrect use of decimal point
|
||||
{s: ".0"},
|
||||
{s: ".0", base: 10},
|
||||
{s: ".", base: 1},
|
||||
{s: "0x.0"},
|
||||
|
||||
// no errors
|
||||
{"0", 0, false, nil, 10, 1, true, 0},
|
||||
{"0", 10, false, nil, 10, 1, true, 0},
|
||||
{"0", 36, false, nil, 36, 1, true, 0},
|
||||
{"1", 0, false, nat{1}, 10, 1, true, 0},
|
||||
{"1", 10, false, nat{1}, 10, 1, true, 0},
|
||||
{"0 ", 0, false, nil, 10, 1, true, ' '},
|
||||
{"08", 0, false, nil, 10, 1, true, '8'},
|
||||
{"08", 10, false, nat{8}, 10, 2, true, 0},
|
||||
{"018", 0, false, nat{1}, 8, 1, true, '8'},
|
||||
{"0b1", 0, false, nat{1}, 2, 1, true, 0},
|
||||
{"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0},
|
||||
{"03271", 0, false, nat{03271}, 8, 4, true, 0},
|
||||
{"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
|
||||
{"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
|
||||
{"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
|
||||
{"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
|
||||
{"0x", 16, false, nil, 16, 1, true, 'x'},
|
||||
{"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
|
||||
{"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
|
||||
|
||||
// no errors, decimal point
|
||||
{"0.", 0, false, nil, 10, 1, true, '.'},
|
||||
{"0.", 10, true, nil, 10, 0, true, 0},
|
||||
{"0.1.2", 10, true, nat{1}, 10, -1, true, '.'},
|
||||
{".000", 10, true, nil, 10, -3, true, 0},
|
||||
{"12.3", 10, true, nat{123}, 10, -1, true, 0},
|
||||
{"012.345", 10, true, nat{12345}, 10, -3, true, 0},
|
||||
}
|
||||
|
||||
func TestScanBase(t *testing.T) {
|
||||
for _, a := range natScanTests {
|
||||
r := strings.NewReader(a.s)
|
||||
x, b, count, err := nat(nil).scan(r, a.base, a.frac)
|
||||
if err == nil && !a.ok {
|
||||
t.Errorf("scan%+v\n\texpected error", a)
|
||||
}
|
||||
if err != nil {
|
||||
if a.ok {
|
||||
t.Errorf("scan%+v\n\tgot error = %s", a, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if x.cmp(a.x) != 0 {
|
||||
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
|
||||
}
|
||||
if b != a.b {
|
||||
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base)
|
||||
}
|
||||
if count != a.count {
|
||||
t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count)
|
||||
}
|
||||
next, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
next = 0
|
||||
err = nil
|
||||
}
|
||||
if err == nil && next != a.next {
|
||||
t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pi = "3" +
|
||||
"14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" +
|
||||
"32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" +
|
||||
"28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" +
|
||||
"96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" +
|
||||
"31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" +
|
||||
"60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" +
|
||||
"22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" +
|
||||
"29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" +
|
||||
"81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" +
|
||||
"21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" +
|
||||
"55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" +
|
||||
"63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" +
|
||||
"75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" +
|
||||
"45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" +
|
||||
"34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" +
|
||||
"16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" +
|
||||
"04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" +
|
||||
"26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" +
|
||||
"99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" +
|
||||
"53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" +
|
||||
"68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" +
|
||||
"13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" +
|
||||
"88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" +
|
||||
"79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" +
|
||||
"68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" +
|
||||
"21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" +
|
||||
"06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" +
|
||||
"14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" +
|
||||
"21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" +
|
||||
"05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" +
|
||||
"23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" +
|
||||
"90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" +
|
||||
"31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" +
|
||||
"20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" +
|
||||
"97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" +
|
||||
"44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" +
|
||||
"44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" +
|
||||
"85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" +
|
||||
"58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" +
|
||||
"27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" +
|
||||
"09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" +
|
||||
"79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" +
|
||||
"06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" +
|
||||
"91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" +
|
||||
"94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" +
|
||||
"78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" +
|
||||
"24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" +
|
||||
"59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" +
|
||||
"34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" +
|
||||
"88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" +
|
||||
"94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337"
|
||||
|
||||
// Test case for BenchmarkScanPi.
|
||||
func TestScanPi(t *testing.T) {
|
||||
var x nat
|
||||
z, _, _, err := x.scan(strings.NewReader(pi), 10, false)
|
||||
if err != nil {
|
||||
t.Errorf("scanning pi: %s", err)
|
||||
}
|
||||
if s := z.decimalString(); s != pi {
|
||||
t.Errorf("scanning pi: got %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanPiParallel(t *testing.T) {
|
||||
const n = 2
|
||||
c := make(chan int)
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
TestScanPi(t)
|
||||
c <- 0
|
||||
}()
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScanPi(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var x nat
|
||||
x.scan(strings.NewReader(pi), 10, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringPiParallel(b *testing.B) {
|
||||
var x nat
|
||||
x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
|
||||
if x.decimalString() != pi {
|
||||
panic("benchmark incorrect: conversion failed")
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
x.decimalString()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) }
|
||||
func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) }
|
||||
func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) }
|
||||
func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) }
|
||||
func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) }
|
||||
func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) }
|
||||
func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) }
|
||||
func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) }
|
||||
func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) }
|
||||
func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) }
|
||||
func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) }
|
||||
func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) }
|
||||
func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) }
|
||||
func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) }
|
||||
func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) }
|
||||
func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) }
|
||||
func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) }
|
||||
|
||||
func ScanHelper(b *testing.B, base int, x, y Word) {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(x, y)
|
||||
|
||||
var s string
|
||||
s = z.string(lowercaseDigits[:base])
|
||||
if t := toString(z, lowercaseDigits[:base]); t != s {
|
||||
b.Fatalf("scanning: got %s; want %s", s, t)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.scan(strings.NewReader(s), base, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) }
|
||||
func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) }
|
||||
func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) }
|
||||
func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) }
|
||||
func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) }
|
||||
func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) }
|
||||
func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) }
|
||||
func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) }
|
||||
func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) }
|
||||
func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) }
|
||||
func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) }
|
||||
func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) }
|
||||
func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) }
|
||||
func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) }
|
||||
func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) }
|
||||
func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) }
|
||||
func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) }
|
||||
|
||||
func StringHelper(b *testing.B, base int, x, y Word) {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(x, y)
|
||||
z.string(lowercaseDigits[:base]) // warm divisor cache
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = z.string(lowercaseDigits[:base])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting
|
||||
func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) }
|
||||
func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) }
|
||||
func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) }
|
||||
func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) }
|
||||
func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) }
|
||||
func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) }
|
||||
func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) }
|
||||
func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) }
|
||||
func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) }
|
||||
func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) }
|
||||
func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) }
|
||||
func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) }
|
||||
func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) }
|
||||
func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) }
|
||||
func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) }
|
||||
func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
|
||||
func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
|
||||
func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
|
||||
|
||||
func LeafSizeHelper(b *testing.B, base Word, size int) {
|
||||
b.StopTimer()
|
||||
originalLeafSize := leafSize
|
||||
resetTable(cacheBase10.table[:])
|
||||
leafSize = size
|
||||
b.StartTimer()
|
||||
|
||||
for d := 1; d <= 10000; d *= 10 {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(base, Word(d)) // build target number
|
||||
_ = z.string(lowercaseDigits[:base]) // warm divisor cache
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = z.string(lowercaseDigits[:base])
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
resetTable(cacheBase10.table[:])
|
||||
leafSize = originalLeafSize
|
||||
b.StartTimer()
|
||||
}
|
||||
|
||||
func resetTable(table []divisor) {
|
||||
if table != nil && table[0].bbb != nil {
|
||||
for i := 0; i < len(table); i++ {
|
||||
table[i].bbb = nil
|
||||
table[i].nbits = 0
|
||||
table[i].ndigits = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringPowers(t *testing.T) {
|
||||
var b, p Word
|
||||
for b = 2; b <= 16; b++ {
|
||||
for p = 0; p <= 512; p++ {
|
||||
x := nat(nil).expWW(b, p)
|
||||
xs := x.string(lowercaseDigits[:b])
|
||||
xs2 := toString(x, lowercaseDigits[:b])
|
||||
if xs != xs2 {
|
||||
t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
|
||||
}
|
||||
}
|
||||
if b >= 3 && testing.Short() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeadingZeros(t *testing.T) {
|
||||
var x Word = _B >> 1
|
||||
for i := 0; i <= _W; i++ {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,489 @@
|
|||
// 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 nat-to-string conversion functions.
|
||||
|
||||
package big
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MaxBase is the largest number base accepted for string conversions.
|
||||
const MaxBase = 'z' - 'a' + 10 + 1
|
||||
|
||||
// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M.
|
||||
// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word.
|
||||
// In other words, at most n digits in base b fit into a Word.
|
||||
// TODO(gri) replace this with a table, generated at build time.
|
||||
func maxPow(b Word) (p Word, n int) {
|
||||
p, n = b, 1 // assuming b <= _M
|
||||
for max := _M / b; p <= max; {
|
||||
// p == b**n && p <= max
|
||||
p *= b
|
||||
n++
|
||||
}
|
||||
// p == b**n && p <= _M
|
||||
return
|
||||
}
|
||||
|
||||
// pow returns x**n for n > 0, and 1 otherwise.
|
||||
func pow(x Word, n int) (p Word) {
|
||||
// n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1
|
||||
// thus x**n == product of x**(2**i) for all i where bi == 1
|
||||
// (Russian Peasant Method for exponentiation)
|
||||
p = 1
|
||||
for n > 0 {
|
||||
if n&1 != 0 {
|
||||
p *= x
|
||||
}
|
||||
x *= x
|
||||
n >>= 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// scan scans the number corresponding to the longest possible prefix
|
||||
// from r representing an unsigned number in a given conversion base.
|
||||
// It returns the corresponding natural number res, the actual base b,
|
||||
// a digit count, and an error err, if any.
|
||||
//
|
||||
// number = [ prefix ] mantissa .
|
||||
// prefix = "0" [ "x" | "X" | "b" | "B" ] .
|
||||
// mantissa = digits | digits "." [ digits ] | "." digits .
|
||||
// digits = digit { digit } .
|
||||
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
|
||||
//
|
||||
// The base argument must be 0 or a value between 0 through MaxBase.
|
||||
//
|
||||
// For base 0, the number prefix determines the actual base: A prefix of
|
||||
// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix
|
||||
// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise
|
||||
// the selected base is 10 and no prefix is permitted.
|
||||
//
|
||||
// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply
|
||||
// stands for a zero digit), and a period followed by a fractional part
|
||||
// is permitted. The result value is computed as if there were no period
|
||||
// present; and the count value is used to determine the fractional part.
|
||||
//
|
||||
// A result digit count > 0 corresponds to the number of (non-prefix) digits
|
||||
// parsed. A digit count <= 0 indicates the presence of a period (if fracOk
|
||||
// is set, only), and -count is the number of fractional digits found.
|
||||
// In this case, the value of the scanned number is res * 10**count.
|
||||
//
|
||||
func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) {
|
||||
// reject illegal bases
|
||||
if base != 0 && base < 2 || base > MaxBase {
|
||||
err = errors.New("illegal number base")
|
||||
return
|
||||
}
|
||||
|
||||
// one char look-ahead
|
||||
ch, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// determine actual base
|
||||
b = base
|
||||
if base == 0 {
|
||||
// actual base is 10 unless there's a base prefix
|
||||
b = 10
|
||||
if ch == '0' {
|
||||
count = 1
|
||||
switch ch, err = r.ReadByte(); err {
|
||||
case nil:
|
||||
// possibly one of 0x, 0X, 0b, 0B
|
||||
if !fracOk {
|
||||
b = 8
|
||||
}
|
||||
switch ch {
|
||||
case 'x', 'X':
|
||||
b = 16
|
||||
case 'b', 'B':
|
||||
b = 2
|
||||
}
|
||||
switch b {
|
||||
case 16, 2:
|
||||
count = 0 // prefix is not counted
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
// io.EOF is also an error in this case
|
||||
return
|
||||
}
|
||||
case 8:
|
||||
count = 0 // prefix is not counted
|
||||
}
|
||||
case io.EOF:
|
||||
// input is "0"
|
||||
res = z[:0]
|
||||
err = nil
|
||||
return
|
||||
default:
|
||||
// read error
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert string
|
||||
// Algorithm: Collect digits in groups of at most n digits in di
|
||||
// and then use mulAddWW for every such group to add them to the
|
||||
// result.
|
||||
z = z[:0]
|
||||
b1 := Word(b)
|
||||
bn, n := maxPow(b1) // at most n digits in base b1 fit into Word
|
||||
di := Word(0) // 0 <= di < b1**i < bn
|
||||
i := 0 // 0 <= i < n
|
||||
dp := -1 // position of decimal point
|
||||
for {
|
||||
if fracOk && ch == '.' {
|
||||
fracOk = false
|
||||
dp = count
|
||||
// advance
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// convert rune into digit value d1
|
||||
var d1 Word
|
||||
switch {
|
||||
case '0' <= ch && ch <= '9':
|
||||
d1 = Word(ch - '0')
|
||||
case 'a' <= ch && ch <= 'z':
|
||||
d1 = Word(ch - 'a' + 10)
|
||||
case 'A' <= ch && ch <= 'Z':
|
||||
d1 = Word(ch - 'A' + 10)
|
||||
default:
|
||||
d1 = MaxBase + 1
|
||||
}
|
||||
if d1 >= b1 {
|
||||
r.UnreadByte() // ch does not belong to number anymore
|
||||
break
|
||||
}
|
||||
count++
|
||||
|
||||
// collect d1 in di
|
||||
di = di*b1 + d1
|
||||
i++
|
||||
|
||||
// if di is "full", add it to the result
|
||||
if i == n {
|
||||
z = z.mulAddWW(z, bn, di)
|
||||
di = 0
|
||||
i = 0
|
||||
}
|
||||
|
||||
// advance
|
||||
if ch, err = r.ReadByte(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
// no digits found
|
||||
switch {
|
||||
case base == 0 && b == 8:
|
||||
// there was only the octal prefix 0 (possibly followed by digits > 7);
|
||||
// count as one digit and return base 10, not 8
|
||||
count = 1
|
||||
b = 10
|
||||
case base != 0 || b != 8:
|
||||
// there was neither a mantissa digit nor the octal prefix 0
|
||||
err = errors.New("syntax error scanning number")
|
||||
}
|
||||
return
|
||||
}
|
||||
// count > 0
|
||||
|
||||
// add remaining digits to result
|
||||
if i > 0 {
|
||||
z = z.mulAddWW(z, pow(b1, i), di)
|
||||
}
|
||||
res = z.norm()
|
||||
|
||||
// adjust for fraction, if any
|
||||
if dp >= 0 {
|
||||
// 0 <= dp <= count > 0
|
||||
count = dp - count
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Character sets for string conversion.
|
||||
const (
|
||||
lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
)
|
||||
|
||||
// decimalString returns a decimal representation of x.
|
||||
// It calls x.string with the charset "0123456789".
|
||||
func (x nat) decimalString() string {
|
||||
return x.string(lowercaseDigits[:10])
|
||||
}
|
||||
|
||||
// hexString returns a hexadecimal representation of x.
|
||||
// It calls x.string with the charset "0123456789abcdef".
|
||||
func (x nat) hexString() string {
|
||||
return x.string(lowercaseDigits[:16])
|
||||
}
|
||||
|
||||
// string converts x to a string using digits from a charset; a digit with
|
||||
// value d is represented by charset[d]. The conversion base is determined
|
||||
// by len(charset), which must be >= 2 and <= 256.
|
||||
func (x nat) string(charset string) string {
|
||||
b := Word(len(charset))
|
||||
|
||||
// special cases
|
||||
switch {
|
||||
case b < 2 || b > 256:
|
||||
panic("invalid character set length")
|
||||
case len(x) == 0:
|
||||
return string(charset[0])
|
||||
}
|
||||
|
||||
// allocate buffer for conversion
|
||||
i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most
|
||||
s := make([]byte, i)
|
||||
|
||||
// convert power of two and non power of two bases separately
|
||||
if b == b&-b {
|
||||
// shift is base-b digit size in bits
|
||||
shift := trailingZeroBits(b) // shift > 0 because b >= 2
|
||||
mask := Word(1)<<shift - 1
|
||||
w := x[0]
|
||||
nbits := uint(_W) // number of unprocessed bits in w
|
||||
|
||||
// convert less-significant words
|
||||
for k := 1; k < len(x); k++ {
|
||||
// convert full digits
|
||||
for nbits >= shift {
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
w >>= shift
|
||||
nbits -= shift
|
||||
}
|
||||
|
||||
// convert any partial leading digit and advance to next word
|
||||
if nbits == 0 {
|
||||
// no partial digit remaining, just advance
|
||||
w = x[k]
|
||||
nbits = _W
|
||||
} else {
|
||||
// partial digit in current (k-1) and next (k) word
|
||||
w |= x[k] << nbits
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
|
||||
// advance
|
||||
w = x[k] >> (shift - nbits)
|
||||
nbits = _W - (shift - nbits)
|
||||
}
|
||||
}
|
||||
|
||||
// convert digits of most-significant word (omit leading zeros)
|
||||
for nbits >= 0 && w != 0 {
|
||||
i--
|
||||
s[i] = charset[w&mask]
|
||||
w >>= shift
|
||||
nbits -= shift
|
||||
}
|
||||
|
||||
} else {
|
||||
bb, ndigits := maxPow(Word(b))
|
||||
|
||||
// construct table of successive squares of bb*leafSize to use in subdivisions
|
||||
// result (table != nil) <=> (len(x) > leafSize > 0)
|
||||
table := divisors(len(x), b, ndigits, bb)
|
||||
|
||||
// preserve x, create local copy for use by convertWords
|
||||
q := nat(nil).set(x)
|
||||
|
||||
// convert q to string s in base b
|
||||
q.convertWords(s, charset, b, ndigits, bb, table)
|
||||
|
||||
// strip leading zeros
|
||||
// (x != 0; thus s must contain at least one non-zero digit
|
||||
// and the loop will terminate)
|
||||
i = 0
|
||||
for zero := charset[0]; s[i] == zero; {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return string(s[i:])
|
||||
}
|
||||
|
||||
// Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
|
||||
// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
|
||||
// repeated nat/Word division.
|
||||
//
|
||||
// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
|
||||
// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
|
||||
// Recursive conversion divides q by its approximate square root, yielding two parts, each half
|
||||
// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s
|
||||
// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and
|
||||
// is made better by splitting the subblocks recursively. Best is to split blocks until one more
|
||||
// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the
|
||||
// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the
|
||||
// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and
|
||||
// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
|
||||
// specific hardware.
|
||||
//
|
||||
func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) {
|
||||
// split larger blocks recursively
|
||||
if table != nil {
|
||||
// len(q) > leafSize > 0
|
||||
var r nat
|
||||
index := len(table) - 1
|
||||
for len(q) > leafSize {
|
||||
// find divisor close to sqrt(q) if possible, but in any case < q
|
||||
maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length
|
||||
minLength := maxLength >> 1 // ~= log2 sqrt(q)
|
||||
for index > 0 && table[index-1].nbits > minLength {
|
||||
index-- // desired
|
||||
}
|
||||
if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 {
|
||||
index--
|
||||
if index < 0 {
|
||||
panic("internal inconsistency")
|
||||
}
|
||||
}
|
||||
|
||||
// split q into the two digit number (q'*bbb + r) to form independent subblocks
|
||||
q, r = q.div(r, q, table[index].bbb)
|
||||
|
||||
// convert subblocks and collect results in s[:h] and s[h:]
|
||||
h := len(s) - table[index].ndigits
|
||||
r.convertWords(s[h:], charset, b, ndigits, bb, table[0:index])
|
||||
s = s[:h] // == q.convertWords(s, charset, b, ndigits, bb, table[0:index+1])
|
||||
}
|
||||
}
|
||||
|
||||
// having split any large blocks now process the remaining (small) block iteratively
|
||||
i := len(s)
|
||||
var r Word
|
||||
if b == 10 {
|
||||
// hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants)
|
||||
for len(q) > 0 {
|
||||
// extract least significant, base bb "digit"
|
||||
q, r = q.divW(q, bb)
|
||||
for j := 0; j < ndigits && i > 0; j++ {
|
||||
i--
|
||||
// avoid % computation since r%10 == r - int(r/10)*10;
|
||||
// this appears to be faster for BenchmarkString10000Base10
|
||||
// and smaller strings (but a bit slower for larger ones)
|
||||
t := r / 10
|
||||
s[i] = charset[r-t<<3-t-t] // TODO(gri) replace w/ t*10 once compiler produces better code
|
||||
r = t
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for len(q) > 0 {
|
||||
// extract least significant, base bb "digit"
|
||||
q, r = q.divW(q, bb)
|
||||
for j := 0; j < ndigits && i > 0; j++ {
|
||||
i--
|
||||
s[i] = charset[r%b]
|
||||
r /= b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepend high-order zeroes
|
||||
zero := charset[0]
|
||||
for i > 0 { // while need more leading zeroes
|
||||
i--
|
||||
s[i] = zero
|
||||
}
|
||||
}
|
||||
|
||||
// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion)
|
||||
// Benchmark and configure leafSize using: go test -bench="Leaf"
|
||||
// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
|
||||
// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
|
||||
var leafSize int = 8 // number of Word-size binary values treat as a monolithic block
|
||||
|
||||
type divisor struct {
|
||||
bbb nat // divisor
|
||||
nbits int // bit length of divisor (discounting leading zeroes) ~= log2(bbb)
|
||||
ndigits int // digit length of divisor in terms of output base digits
|
||||
}
|
||||
|
||||
var cacheBase10 struct {
|
||||
sync.Mutex
|
||||
table [64]divisor // cached divisors for base 10
|
||||
}
|
||||
|
||||
// expWW computes x**y
|
||||
func (z nat) expWW(x, y Word) nat {
|
||||
return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil)
|
||||
}
|
||||
|
||||
// construct table of powers of bb*leafSize to use in subdivisions
|
||||
func divisors(m int, b Word, ndigits int, bb Word) []divisor {
|
||||
// only compute table when recursive conversion is enabled and x is large
|
||||
if leafSize == 0 || m <= leafSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
// determine k where (bb**leafSize)**(2**k) >= sqrt(x)
|
||||
k := 1
|
||||
for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 {
|
||||
k++
|
||||
}
|
||||
|
||||
// reuse and extend existing table of divisors or create new table as appropriate
|
||||
var table []divisor // for b == 10, table overlaps with cacheBase10.table
|
||||
if b == 10 {
|
||||
cacheBase10.Lock()
|
||||
table = cacheBase10.table[0:k] // reuse old table for this conversion
|
||||
} else {
|
||||
table = make([]divisor, k) // create new table for this conversion
|
||||
}
|
||||
|
||||
// extend table
|
||||
if table[k-1].ndigits == 0 {
|
||||
// add new entries as needed
|
||||
var larger nat
|
||||
for i := 0; i < k; i++ {
|
||||
if table[i].ndigits == 0 {
|
||||
if i == 0 {
|
||||
table[0].bbb = nat(nil).expWW(bb, Word(leafSize))
|
||||
table[0].ndigits = ndigits * leafSize
|
||||
} else {
|
||||
table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
|
||||
table[i].ndigits = 2 * table[i-1].ndigits
|
||||
}
|
||||
|
||||
// optimization: exploit aggregated extra bits in macro blocks
|
||||
larger = nat(nil).set(table[i].bbb)
|
||||
for mulAddVWW(larger, larger, b, 0) == 0 {
|
||||
table[i].bbb = table[i].bbb.set(larger)
|
||||
table[i].ndigits++
|
||||
}
|
||||
|
||||
table[i].nbits = table[i].bbb.bitLen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b == 10 {
|
||||
cacheBase10.Unlock()
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
|
@ -0,0 +1,429 @@
|
|||
// 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 (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func toString(x nat, charset string) string {
|
||||
base := len(charset)
|
||||
|
||||
// special cases
|
||||
switch {
|
||||
case base < 2:
|
||||
panic("illegal base")
|
||||
case len(x) == 0:
|
||||
return string(charset[0])
|
||||
}
|
||||
|
||||
// allocate buffer for conversion
|
||||
i := x.bitLen()/log2(Word(base)) + 1 // +1: round up
|
||||
s := make([]byte, i)
|
||||
|
||||
// don't destroy x
|
||||
q := nat(nil).set(x)
|
||||
|
||||
// convert
|
||||
for len(q) > 0 {
|
||||
i--
|
||||
var r Word
|
||||
q, r = q.divW(q, Word(base))
|
||||
s[i] = charset[r]
|
||||
}
|
||||
|
||||
return string(s[i:])
|
||||
}
|
||||
|
||||
var strTests = []struct {
|
||||
x nat // nat value to be converted
|
||||
c string // conversion charset
|
||||
s string // expected result
|
||||
}{
|
||||
{nil, "01", "0"},
|
||||
{nat{1}, "01", "1"},
|
||||
{nat{0xc5}, "01", "11000101"},
|
||||
{nat{03271}, lowercaseDigits[:8], "3271"},
|
||||
{nat{10}, lowercaseDigits[:10], "10"},
|
||||
{nat{1234567890}, uppercaseDigits[:10], "1234567890"},
|
||||
{nat{0xdeadbeef}, lowercaseDigits[:16], "deadbeef"},
|
||||
{nat{0xdeadbeef}, uppercaseDigits[:16], "DEADBEEF"},
|
||||
{nat{0x229be7}, lowercaseDigits[:17], "1a2b3c"},
|
||||
{nat{0x309663e6}, uppercaseDigits[:32], "O9COV6"},
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
// test invalid character set explicitly
|
||||
var panicStr string
|
||||
func() {
|
||||
defer func() {
|
||||
panicStr = recover().(string)
|
||||
}()
|
||||
natOne.string("0")
|
||||
}()
|
||||
if panicStr != "invalid character set length" {
|
||||
t.Errorf("expected panic for invalid character set")
|
||||
}
|
||||
|
||||
for _, a := range strTests {
|
||||
s := a.x.string(a.c)
|
||||
if s != a.s {
|
||||
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
|
||||
}
|
||||
|
||||
x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false)
|
||||
if x.cmp(a.x) != 0 {
|
||||
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
|
||||
}
|
||||
if b != len(a.c) {
|
||||
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("scan%+v\n\tgot error = %s", a, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var natScanTests = []struct {
|
||||
s string // string to be scanned
|
||||
base int // input base
|
||||
frac bool // fraction ok
|
||||
x nat // expected nat
|
||||
b int // expected base
|
||||
count int // expected digit count
|
||||
ok bool // expected success
|
||||
next rune // next character (or 0, if at EOF)
|
||||
}{
|
||||
// error: illegal base
|
||||
{base: -1},
|
||||
{base: 37},
|
||||
|
||||
// error: no mantissa
|
||||
{},
|
||||
{s: "?"},
|
||||
{base: 10},
|
||||
{base: 36},
|
||||
{s: "?", base: 10},
|
||||
{s: "0x"},
|
||||
{s: "345", base: 2},
|
||||
|
||||
// error: incorrect use of decimal point
|
||||
{s: ".0"},
|
||||
{s: ".0", base: 10},
|
||||
{s: ".", base: 1},
|
||||
{s: "0x.0"},
|
||||
|
||||
// no errors
|
||||
{"0", 0, false, nil, 10, 1, true, 0},
|
||||
{"0", 10, false, nil, 10, 1, true, 0},
|
||||
{"0", 36, false, nil, 36, 1, true, 0},
|
||||
{"1", 0, false, nat{1}, 10, 1, true, 0},
|
||||
{"1", 10, false, nat{1}, 10, 1, true, 0},
|
||||
{"0 ", 0, false, nil, 10, 1, true, ' '},
|
||||
{"08", 0, false, nil, 10, 1, true, '8'},
|
||||
{"08", 10, false, nat{8}, 10, 2, true, 0},
|
||||
{"018", 0, false, nat{1}, 8, 1, true, '8'},
|
||||
{"0b1", 0, false, nat{1}, 2, 1, true, 0},
|
||||
{"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0},
|
||||
{"03271", 0, false, nat{03271}, 8, 4, true, 0},
|
||||
{"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
|
||||
{"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
|
||||
{"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
|
||||
{"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
|
||||
{"0x", 16, false, nil, 16, 1, true, 'x'},
|
||||
{"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
|
||||
{"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
|
||||
|
||||
// no errors, decimal point
|
||||
{"0.", 0, false, nil, 10, 1, true, '.'},
|
||||
{"0.", 10, true, nil, 10, 0, true, 0},
|
||||
{"0.1.2", 10, true, nat{1}, 10, -1, true, '.'},
|
||||
{".000", 10, true, nil, 10, -3, true, 0},
|
||||
{"12.3", 10, true, nat{123}, 10, -1, true, 0},
|
||||
{"012.345", 10, true, nat{12345}, 10, -3, true, 0},
|
||||
}
|
||||
|
||||
func TestScanBase(t *testing.T) {
|
||||
for _, a := range natScanTests {
|
||||
r := strings.NewReader(a.s)
|
||||
x, b, count, err := nat(nil).scan(r, a.base, a.frac)
|
||||
if err == nil && !a.ok {
|
||||
t.Errorf("scan%+v\n\texpected error", a)
|
||||
}
|
||||
if err != nil {
|
||||
if a.ok {
|
||||
t.Errorf("scan%+v\n\tgot error = %s", a, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if x.cmp(a.x) != 0 {
|
||||
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
|
||||
}
|
||||
if b != a.b {
|
||||
t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base)
|
||||
}
|
||||
if count != a.count {
|
||||
t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count)
|
||||
}
|
||||
next, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
next = 0
|
||||
err = nil
|
||||
}
|
||||
if err == nil && next != a.next {
|
||||
t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pi = "3" +
|
||||
"14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" +
|
||||
"32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" +
|
||||
"28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" +
|
||||
"96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" +
|
||||
"31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" +
|
||||
"60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" +
|
||||
"22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" +
|
||||
"29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" +
|
||||
"81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" +
|
||||
"21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" +
|
||||
"55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" +
|
||||
"63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" +
|
||||
"75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" +
|
||||
"45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" +
|
||||
"34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" +
|
||||
"16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" +
|
||||
"04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" +
|
||||
"26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" +
|
||||
"99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" +
|
||||
"53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" +
|
||||
"68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" +
|
||||
"13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" +
|
||||
"88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" +
|
||||
"79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" +
|
||||
"68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" +
|
||||
"21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" +
|
||||
"06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" +
|
||||
"14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" +
|
||||
"21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" +
|
||||
"05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" +
|
||||
"23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" +
|
||||
"90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" +
|
||||
"31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" +
|
||||
"20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" +
|
||||
"97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" +
|
||||
"44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" +
|
||||
"44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" +
|
||||
"85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" +
|
||||
"58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" +
|
||||
"27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" +
|
||||
"09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" +
|
||||
"79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" +
|
||||
"06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" +
|
||||
"91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" +
|
||||
"94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" +
|
||||
"78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" +
|
||||
"24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" +
|
||||
"59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" +
|
||||
"34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" +
|
||||
"88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" +
|
||||
"94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337"
|
||||
|
||||
// Test case for BenchmarkScanPi.
|
||||
func TestScanPi(t *testing.T) {
|
||||
var x nat
|
||||
z, _, _, err := x.scan(strings.NewReader(pi), 10, false)
|
||||
if err != nil {
|
||||
t.Errorf("scanning pi: %s", err)
|
||||
}
|
||||
if s := z.decimalString(); s != pi {
|
||||
t.Errorf("scanning pi: got %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanPiParallel(t *testing.T) {
|
||||
const n = 2
|
||||
c := make(chan int)
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
TestScanPi(t)
|
||||
c <- 0
|
||||
}()
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
<-c
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScanPi(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var x nat
|
||||
x.scan(strings.NewReader(pi), 10, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringPiParallel(b *testing.B) {
|
||||
var x nat
|
||||
x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
|
||||
if x.decimalString() != pi {
|
||||
panic("benchmark incorrect: conversion failed")
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
x.decimalString()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) }
|
||||
func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) }
|
||||
func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) }
|
||||
func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) }
|
||||
func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) }
|
||||
func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) }
|
||||
func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) }
|
||||
func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) }
|
||||
func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) }
|
||||
func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) }
|
||||
func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) }
|
||||
func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) }
|
||||
func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) }
|
||||
|
||||
func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) }
|
||||
func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) }
|
||||
func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) }
|
||||
func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) }
|
||||
func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) }
|
||||
|
||||
func ScanHelper(b *testing.B, base int, x, y Word) {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(x, y)
|
||||
|
||||
var s string
|
||||
s = z.string(lowercaseDigits[:base])
|
||||
if t := toString(z, lowercaseDigits[:base]); t != s {
|
||||
b.Fatalf("scanning: got %s; want %s", s, t)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.scan(strings.NewReader(s), base, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) }
|
||||
func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) }
|
||||
func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) }
|
||||
func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) }
|
||||
func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) }
|
||||
func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) }
|
||||
func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) }
|
||||
func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) }
|
||||
func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) }
|
||||
func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) }
|
||||
func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) }
|
||||
func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) }
|
||||
func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) }
|
||||
|
||||
func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) }
|
||||
func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) }
|
||||
func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) }
|
||||
func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) }
|
||||
func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) }
|
||||
|
||||
func StringHelper(b *testing.B, base int, x, y Word) {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(x, y)
|
||||
z.string(lowercaseDigits[:base]) // warm divisor cache
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = z.string(lowercaseDigits[:base])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting
|
||||
func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) }
|
||||
func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) }
|
||||
func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) }
|
||||
func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) }
|
||||
func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) }
|
||||
func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) }
|
||||
func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) }
|
||||
func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) }
|
||||
func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) }
|
||||
func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) }
|
||||
func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) }
|
||||
func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) }
|
||||
func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) }
|
||||
func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) }
|
||||
func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) }
|
||||
func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
|
||||
func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
|
||||
func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
|
||||
|
||||
func LeafSizeHelper(b *testing.B, base Word, size int) {
|
||||
b.StopTimer()
|
||||
originalLeafSize := leafSize
|
||||
resetTable(cacheBase10.table[:])
|
||||
leafSize = size
|
||||
b.StartTimer()
|
||||
|
||||
for d := 1; d <= 10000; d *= 10 {
|
||||
b.StopTimer()
|
||||
var z nat
|
||||
z = z.expWW(base, Word(d)) // build target number
|
||||
_ = z.string(lowercaseDigits[:base]) // warm divisor cache
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = z.string(lowercaseDigits[:base])
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
resetTable(cacheBase10.table[:])
|
||||
leafSize = originalLeafSize
|
||||
b.StartTimer()
|
||||
}
|
||||
|
||||
func resetTable(table []divisor) {
|
||||
if table != nil && table[0].bbb != nil {
|
||||
for i := 0; i < len(table); i++ {
|
||||
table[i].bbb = nil
|
||||
table[i].nbits = 0
|
||||
table[i].ndigits = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringPowers(t *testing.T) {
|
||||
var b, p Word
|
||||
for b = 2; b <= 16; b++ {
|
||||
for p = 0; p <= 512; p++ {
|
||||
x := nat(nil).expWW(b, p)
|
||||
xs := x.string(lowercaseDigits[:b])
|
||||
xs2 := toString(x, lowercaseDigits[:b])
|
||||
if xs != xs2 {
|
||||
t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
|
||||
}
|
||||
}
|
||||
if b >= 3 && testing.Short() {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue