From: Robert Griesemer Date: Fri, 30 Jan 2015 22:12:21 +0000 (-0800) Subject: math/big: split nat conversion routines and tests into separate files X-Git-Tag: go1.5beta1~2193 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d135966ef60cc4d1b94de1a184cafdfcb6ee59b1;p=gostls13.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 --- diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 2258d7564e..db730d1356 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -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 { - 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{ diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go index 1e98118b63..b25a89f731 100644 --- a/src/math/big/nat_test.go +++ b/src/math/big/nat_test.go @@ -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 index 0000000000..e094d22193 --- /dev/null +++ b/src/math/big/natconv.go @@ -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 { + 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 index 0000000000..d4c3fb4b34 --- /dev/null +++ b/src/math/big/natconv_test.go @@ -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 + } + } +}