]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: split nat conversion routines and tests into separate files
authorRobert Griesemer <gri@golang.org>
Fri, 30 Jan 2015 22:12:21 +0000 (14:12 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 30 Jan 2015 23:01:56 +0000 (23:01 +0000)
No functional changes.

Change-Id: Ibbb705b167603d30467f3ebb83a3bb39845306a5
Reviewed-on: https://go-review.googlesource.com/3671
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/nat.go
src/math/big/nat_test.go
src/math/big/natconv.go [new file with mode: 0644]
src/math/big/natconv_test.go [new file with mode: 0644]

index 2258d7564e6d13af99e84f73525df27e80e21e5f..db730d1356b2f70106b6a64336f65006cc0c7e9a 100644 (file)
@@ -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{
index 1e98118b63547c01922edfc45edbd3e269bdc13e..b25a89f731b0a2dd2a4658e1e649457c7e5006fe 100644 (file)
@@ -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++ {
diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go
new file mode 100644 (file)
index 0000000..e094d22
--- /dev/null
@@ -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
+}
diff --git a/src/math/big/natconv_test.go b/src/math/big/natconv_test.go
new file mode 100644 (file)
index 0000000..d4c3fb4
--- /dev/null
@@ -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
+               }
+       }
+}