]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/big: re-vendor
authorJosh Bleecher Snyder <josharian@gmail.com>
Thu, 7 Jul 2016 22:29:48 +0000 (15:29 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Tue, 16 Aug 2016 14:51:21 +0000 (14:51 +0000)
Pick up a bunch of changes and fixes.

Change-Id: If4101f7185d433a4c89096bc786ee5de8eeabac0
Reviewed-on: https://go-review.googlesource.com/27123
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
17 files changed:
src/cmd/compile/internal/big/arith_test.go
src/cmd/compile/internal/big/decimal_test.go
src/cmd/compile/internal/big/doc.go [new file with mode: 0644]
src/cmd/compile/internal/big/float.go
src/cmd/compile/internal/big/floatconv_test.go
src/cmd/compile/internal/big/floatexample_test.go
src/cmd/compile/internal/big/floatmarsh.go
src/cmd/compile/internal/big/floatmarsh_test.go
src/cmd/compile/internal/big/ftoa.go
src/cmd/compile/internal/big/gcd_test.go
src/cmd/compile/internal/big/int.go
src/cmd/compile/internal/big/nat.go
src/cmd/compile/internal/big/nat_test.go
src/cmd/compile/internal/big/natconv.go
src/cmd/compile/internal/big/natconv_test.go
src/cmd/compile/internal/big/ratconv.go
src/cmd/compile/internal/big/ratconv_test.go

index 7d2f69a7511572ce5a2a0db2053ce7688401fee1..75862b4951fffe64b5900f16a2e2dd31d4891446 100644 (file)
@@ -5,6 +5,7 @@
 package big
 
 import (
+       "fmt"
        "math/rand"
        "testing"
 )
@@ -118,28 +119,22 @@ func rndV(n int) []Word {
        return v
 }
 
-func benchmarkFunVV(b *testing.B, f funVV, n int) {
-       x := rndV(n)
-       y := rndV(n)
-       z := make([]Word, n)
-       b.SetBytes(int64(n * _W))
-       b.ResetTimer()
-       for i := 0; i < b.N; i++ {
-               f(z, x, y)
+var benchSizes = []int{1, 2, 3, 4, 5, 1e1, 1e2, 1e3, 1e4, 1e5}
+
+func BenchmarkAddVV(b *testing.B) {
+       for _, n := range benchSizes {
+               x := rndV(n)
+               y := rndV(n)
+               z := make([]Word, n)
+               b.Run(fmt.Sprint(n), func(b *testing.B) {
+                       b.SetBytes(int64(n * _W))
+                       for i := 0; i < b.N; i++ {
+                               addVV(z, x, y)
+                       }
+               })
        }
 }
 
-func BenchmarkAddVV_1(b *testing.B)   { benchmarkFunVV(b, addVV, 1) }
-func BenchmarkAddVV_2(b *testing.B)   { benchmarkFunVV(b, addVV, 2) }
-func BenchmarkAddVV_3(b *testing.B)   { benchmarkFunVV(b, addVV, 3) }
-func BenchmarkAddVV_4(b *testing.B)   { benchmarkFunVV(b, addVV, 4) }
-func BenchmarkAddVV_5(b *testing.B)   { benchmarkFunVV(b, addVV, 5) }
-func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) }
-func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) }
-func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) }
-func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) }
-func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) }
-
 type funVW func(z, x []Word, y Word) (c Word)
 type argVW struct {
        z, x nat
@@ -236,28 +231,20 @@ func TestFunVW(t *testing.T) {
        }
 }
 
-func benchmarkFunVW(b *testing.B, f funVW, n int) {
-       x := rndV(n)
-       y := rndW()
-       z := make([]Word, n)
-       b.SetBytes(int64(n * _S))
-       b.ResetTimer()
-       for i := 0; i < b.N; i++ {
-               f(z, x, y)
+func BenchmarkAddVW(b *testing.B) {
+       for _, n := range benchSizes {
+               x := rndV(n)
+               y := rndW()
+               z := make([]Word, n)
+               b.Run(fmt.Sprint(n), func(b *testing.B) {
+                       b.SetBytes(int64(n * _S))
+                       for i := 0; i < b.N; i++ {
+                               addVW(z, x, y)
+                       }
+               })
        }
 }
 
-func BenchmarkAddVW_1(b *testing.B)   { benchmarkFunVW(b, addVW, 1) }
-func BenchmarkAddVW_2(b *testing.B)   { benchmarkFunVW(b, addVW, 2) }
-func BenchmarkAddVW_3(b *testing.B)   { benchmarkFunVW(b, addVW, 3) }
-func BenchmarkAddVW_4(b *testing.B)   { benchmarkFunVW(b, addVW, 4) }
-func BenchmarkAddVW_5(b *testing.B)   { benchmarkFunVW(b, addVW, 5) }
-func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) }
-func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) }
-func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) }
-func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) }
-func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) }
-
 type funVWW func(z, x []Word, y, r Word) (c Word)
 type argVWW struct {
        z, x nat
@@ -382,28 +369,20 @@ func TestMulAddWWW(t *testing.T) {
        }
 }
 
-func benchmarkAddMulVVW(b *testing.B, n int) {
-       x := rndV(n)
-       y := rndW()
-       z := make([]Word, n)
-       b.SetBytes(int64(n * _W))
-       b.ResetTimer()
-       for i := 0; i < b.N; i++ {
-               addMulVVW(z, x, y)
+func BenchmarkAddMulVVW(b *testing.B) {
+       for _, n := range benchSizes {
+               x := rndV(n)
+               y := rndW()
+               z := make([]Word, n)
+               b.Run(fmt.Sprint(n), func(b *testing.B) {
+                       b.SetBytes(int64(n * _W))
+                       for i := 0; i < b.N; i++ {
+                               addMulVVW(z, x, y)
+                       }
+               })
        }
 }
 
-func BenchmarkAddMulVVW_1(b *testing.B)   { benchmarkAddMulVVW(b, 1) }
-func BenchmarkAddMulVVW_2(b *testing.B)   { benchmarkAddMulVVW(b, 2) }
-func BenchmarkAddMulVVW_3(b *testing.B)   { benchmarkAddMulVVW(b, 3) }
-func BenchmarkAddMulVVW_4(b *testing.B)   { benchmarkAddMulVVW(b, 4) }
-func BenchmarkAddMulVVW_5(b *testing.B)   { benchmarkAddMulVVW(b, 5) }
-func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) }
-func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) }
-func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) }
-func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) }
-func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) }
-
 func testWordBitLen(t *testing.T, fname string, f func(Word) int) {
        for i := 0; i <= _W; i++ {
                x := Word(1) << uint(i-1) // i == 0 => x == 0
@@ -420,23 +399,15 @@ func TestWordBitLen(t *testing.T) {
 }
 
 // runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1.
-func benchmarkBitLenN(b *testing.B, nbits uint) {
-       testword := Word((uint64(1) << nbits) - 1)
-       for i := 0; i < b.N; i++ {
-               bitLen(testword)
+func BenchmarkBitLen(b *testing.B) {
+       // Individual bitLen tests. Numbers chosen to examine both sides
+       // of powers-of-two boundaries.
+       for _, nbits := range []uint{0, 1, 2, 3, 4, 5, 8, 9, 16, 17, 31} {
+               testword := Word((uint64(1) << nbits) - 1)
+               b.Run(fmt.Sprint(nbits), func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               bitLen(testword)
+                       }
+               })
        }
 }
-
-// Individual bitLen tests. Numbers chosen to examine both sides
-// of powers-of-two boundaries.
-func BenchmarkBitLen0(b *testing.B)  { benchmarkBitLenN(b, 0) }
-func BenchmarkBitLen1(b *testing.B)  { benchmarkBitLenN(b, 1) }
-func BenchmarkBitLen2(b *testing.B)  { benchmarkBitLenN(b, 2) }
-func BenchmarkBitLen3(b *testing.B)  { benchmarkBitLenN(b, 3) }
-func BenchmarkBitLen4(b *testing.B)  { benchmarkBitLenN(b, 4) }
-func BenchmarkBitLen5(b *testing.B)  { benchmarkBitLenN(b, 5) }
-func BenchmarkBitLen8(b *testing.B)  { benchmarkBitLenN(b, 8) }
-func BenchmarkBitLen9(b *testing.B)  { benchmarkBitLenN(b, 9) }
-func BenchmarkBitLen16(b *testing.B) { benchmarkBitLenN(b, 16) }
-func BenchmarkBitLen17(b *testing.B) { benchmarkBitLenN(b, 17) }
-func BenchmarkBitLen31(b *testing.B) { benchmarkBitLenN(b, 31) }
index 15bdb181e725742bca198443dc720aaa3f06a0e0..13452f83436c56edf5c8e7fee995b78647674a04 100644 (file)
@@ -105,12 +105,14 @@ func TestDecimalRounding(t *testing.T) {
        }
 }
 
+var sink string
+
 func BenchmarkDecimalConversion(b *testing.B) {
        for i := 0; i < b.N; i++ {
                for shift := -100; shift <= +100; shift++ {
                        var d decimal
                        d.init(natOne, shift)
-                       d.String()
+                       sink = d.String()
                }
        }
 }
diff --git a/src/cmd/compile/internal/big/doc.go b/src/cmd/compile/internal/big/doc.go
new file mode 100644 (file)
index 0000000..a3c2375
--- /dev/null
@@ -0,0 +1,99 @@
+// Copyright 2009 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 implements arbitrary-precision arithmetic (big numbers).
+The following numeric types are supported:
+
+       Int    signed integers
+       Rat    rational numbers
+       Float  floating-point numbers
+
+The zero value for an Int, Rat, or Float correspond to 0. Thus, new
+values can be declared in the usual ways and denote 0 without further
+initialization:
+
+       var x Int        // &x is an *Int of value 0
+       var r = &Rat{}   // r is a *Rat of value 0
+       y := new(Float)  // y is a *Float of value 0
+
+Alternatively, new values can be allocated and initialized with factory
+functions of the form:
+
+       func NewT(v V) *T
+
+For instance, NewInt(x) returns an *Int set to the value of the int64
+argument x, NewRat(a, b) returns a *Rat set to the fraction a/b where
+a and b are int64 values, and NewFloat(f) returns a *Float initialized
+to the float64 argument f. More flexibility is provided with explicit
+setters, for instance:
+
+       var z1 Int
+       z1.SetUint64(123)                 // z1 := 123
+       z2 := new(Rat).SetFloat64(1.2)    // z2 := 6/5
+       z3 := new(Float).SetInt(z1)       // z3 := 123.0
+
+Setters, numeric operations and predicates are represented as methods of
+the form:
+
+       func (z *T) SetV(v V) *T          // z = v
+       func (z *T) Unary(x *T) *T        // z = unary x
+       func (z *T) Binary(x, y *T) *T    // z = x binary y
+       func (x *T) Pred() P              // p = pred(x)
+
+with T one of Int, Rat, or Float. For unary and binary operations, the
+result is the receiver (usually named z in that case; see below); if it
+is one of the operands x or y it may be safely overwritten (and its memory
+reused).
+
+Arithmetic expressions are typically written as a sequence of individual
+method calls, with each call corresponding to an operation. The receiver
+denotes the result and the method arguments are the operation's operands.
+For instance, given three *Int values a, b and c, the invocation
+
+       c.Add(a, b)
+
+computes the sum a + b and stores the result in c, overwriting whatever
+value was held in c before. Unless specified otherwise, operations permit
+aliasing of parameters, so it is perfectly ok to write
+
+       sum.Add(sum, x)
+
+to accumulate values x in a sum.
+
+(By always passing in a result value via the receiver, memory use can be
+much better controlled. Instead of having to allocate new memory for each
+result, an operation can reuse the space allocated for the result value,
+and overwrite that value with the new result in the process.)
+
+Notational convention: Incoming method parameters (including the receiver)
+are named consistently in the API to clarify their use. Incoming operands
+are usually named x, y, a, b, and so on, but never z. A parameter specifying
+the result is named z (typically the receiver).
+
+For instance, the arguments for (*Int).Add are named x and y, and because
+the receiver specifies the result destination, it is called z:
+
+       func (z *Int) Add(x, y *Int) *Int
+
+Methods of this form typically return the incoming receiver as well, to
+enable simple call chaining.
+
+Methods which don't require a result value to be passed in (for instance,
+Int.Sign), simply return the result. In this case, the receiver is typically
+the first operand, named x:
+
+       func (x *Int) Sign() int
+
+Various methods support conversions between strings and corresponding
+numeric values, and vice versa: *Int, *Rat, and *Float values implement
+the Stringer interface for a (default) string representation of the value,
+but also provide SetString methods to initialize a value from a string in
+a variety of supported formats (see the respective SetString documentation).
+
+Finally, *Int, *Rat, and *Float satisfy the fmt package's Scanner interface
+for scanning and (except for *Rat) the Formatter interface for formatted
+printing.
+*/
+package big
index 4b8ad388d3966239f4670020b414349c2dacc148..7a9c2b3dfb2b40191193210f301eb5ba2d86e55f 100644 (file)
@@ -1008,9 +1008,9 @@ func (x *Float) Float64() (float64, Accuracy) {
                if r.form == inf || e > emax {
                        // overflow
                        if x.neg {
-                               return float64(math.Inf(-1)), Below
+                               return math.Inf(-1), Below
                        }
-                       return float64(math.Inf(+1)), Above
+                       return math.Inf(+1), Above
                }
                // e <= emax
 
index b6f999360825b8332f88cf90dba0cc6b01522b4b..b2a1ab05fcc69d9fadcae425d1d11251c85b3888 100644 (file)
@@ -290,6 +290,11 @@ func TestFloat64Text(t *testing.T) {
                // Issue 2625.
                {383260575764816448, 'f', 0, "383260575764816448"},
                {383260575764816448, 'g', -1, "3.8326057576481645e+17"},
+
+               // Issue 15918.
+               {1, 'f', -10, "1"},
+               {1, 'f', -11, "1"},
+               {1, 'f', -12, "1"},
        } {
                // The test cases are from the strconv package which tests float64 values.
                // When formatting values with prec = -1 (shortest representation),
index 83c6bdacae377d2f031a7e94cc20323c12b1f67b..beb9052ebd79169baf9022968b80356f55f93120 100644 (file)
@@ -11,7 +11,7 @@ import (
 )
 
 func ExampleFloat_Add() {
-       // Operating on numbers of different precision.
+       // Operate on numbers of different precision.
        var x, y, z big.Float
        x.SetInt64(1000)          // x is automatically set to 64bit precision
        y.SetFloat64(2.718281828) // y is automatically set to 53bit precision
@@ -26,8 +26,8 @@ func ExampleFloat_Add() {
        // z = 1002.718282 (0x.faadf854p+10, prec = 32, acc = Below)
 }
 
-func Example_Shift() {
-       // Implementing Float "shift" by modifying the (binary) exponents directly.
+func ExampleFloat_shift() {
+       // Implement Float "shift" by modifying the (binary) exponents directly.
        for s := -5; s <= 5; s++ {
                x := big.NewFloat(0.5)
                x.SetMantExp(x, x.MantExp(nil)+s) // shift x by s
index 44987ee03a61367c3a1ab38a0db3c07c2700da7e..3725d4b8345e77fef560de2e65183077e2cd93bd 100644 (file)
@@ -6,7 +6,94 @@
 
 package big
 
-import "fmt"
+import (
+       "encoding/binary"
+       "fmt"
+)
+
+// Gob codec version. Permits backward-compatible changes to the encoding.
+const floatGobVersion byte = 1
+
+// GobEncode implements the gob.GobEncoder interface.
+// The Float value and all its attributes (precision,
+// rounding mode, accuracy) are marshalled.
+func (x *Float) GobEncode() ([]byte, error) {
+       if x == nil {
+               return nil, nil
+       }
+
+       // determine max. space (bytes) required for encoding
+       sz := 1 + 1 + 4 // version + mode|acc|form|neg (3+2+2+1bit) + prec
+       n := 0          // number of mantissa words
+       if x.form == finite {
+               // add space for mantissa and exponent
+               n = int((x.prec + (_W - 1)) / _W) // required mantissa length in words for given precision
+               // actual mantissa slice could be shorter (trailing 0's) or longer (unused bits):
+               // - if shorter, only encode the words present
+               // - if longer, cut off unused words when encoding in bytes
+               //   (in practice, this should never happen since rounding
+               //   takes care of it, but be safe and do it always)
+               if len(x.mant) < n {
+                       n = len(x.mant)
+               }
+               // len(x.mant) >= n
+               sz += 4 + n*_S // exp + mant
+       }
+       buf := make([]byte, sz)
+
+       buf[0] = floatGobVersion
+       b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1
+       if x.neg {
+               b |= 1
+       }
+       buf[1] = b
+       binary.BigEndian.PutUint32(buf[2:], x.prec)
+
+       if x.form == finite {
+               binary.BigEndian.PutUint32(buf[6:], uint32(x.exp))
+               x.mant[len(x.mant)-n:].bytes(buf[10:]) // cut off unused trailing words
+       }
+
+       return buf, nil
+}
+
+// GobDecode implements the gob.GobDecoder interface.
+// The result is rounded per the precision and rounding mode of
+// z unless z's precision is 0, in which case z is set exactly
+// to the decoded value.
+func (z *Float) GobDecode(buf []byte) error {
+       if len(buf) == 0 {
+               // Other side sent a nil or default value.
+               *z = Float{}
+               return nil
+       }
+
+       if buf[0] != floatGobVersion {
+               return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0])
+       }
+
+       oldPrec := z.prec
+       oldMode := z.mode
+
+       b := buf[1]
+       z.mode = RoundingMode((b >> 5) & 7)
+       z.acc = Accuracy((b>>3)&3) - 1
+       z.form = form((b >> 1) & 3)
+       z.neg = b&1 != 0
+       z.prec = binary.BigEndian.Uint32(buf[2:])
+
+       if z.form == finite {
+               z.exp = int32(binary.BigEndian.Uint32(buf[6:]))
+               z.mant = z.mant.setBytes(buf[10:])
+       }
+
+       if oldPrec != 0 {
+               z.mode = oldMode
+               z.SetPrec(uint(oldPrec))
+       }
+
+       return nil
+}
 
 // MarshalText implements the encoding.TextMarshaler interface.
 // Only the Float value is marshaled (in full precision), other
index d7ef2fca68fcc744213e014cc17225deee6e4cd7..5bd906ddae40b9dcef52b8b816268bd743355c74 100644 (file)
@@ -5,7 +5,10 @@
 package big
 
 import (
+       "bytes"
+       "encoding/gob"
        "encoding/json"
+       "io"
        "testing"
 )
 
@@ -23,6 +26,85 @@ var floatVals = []string{
        "Inf",
 }
 
+func TestFloatGobEncoding(t *testing.T) {
+       var medium bytes.Buffer
+       enc := gob.NewEncoder(&medium)
+       dec := gob.NewDecoder(&medium)
+       for _, test := range floatVals {
+               for _, sign := range []string{"", "+", "-"} {
+                       for _, prec := range []uint{0, 1, 2, 10, 53, 64, 100, 1000} {
+                               for _, mode := range []RoundingMode{ToNearestEven, ToNearestAway, ToZero, AwayFromZero, ToNegativeInf, ToPositiveInf} {
+                                       medium.Reset() // empty buffer for each test case (in case of failures)
+                                       x := sign + test
+
+                                       var tx Float
+                                       _, _, err := tx.SetPrec(prec).SetMode(mode).Parse(x, 0)
+                                       if err != nil {
+                                               t.Errorf("parsing of %s (%dbits, %v) failed (invalid test case): %v", x, prec, mode, err)
+                                               continue
+                                       }
+
+                                       // If tx was set to prec == 0, tx.Parse(x, 0) assumes precision 64. Correct it.
+                                       if prec == 0 {
+                                               tx.SetPrec(0)
+                                       }
+
+                                       if err := enc.Encode(&tx); err != nil {
+                                               t.Errorf("encoding of %v (%dbits, %v) failed: %v", &tx, prec, mode, err)
+                                               continue
+                                       }
+
+                                       var rx Float
+                                       if err := dec.Decode(&rx); err != nil {
+                                               t.Errorf("decoding of %v (%dbits, %v) failed: %v", &tx, prec, mode, err)
+                                               continue
+                                       }
+
+                                       if rx.Cmp(&tx) != 0 {
+                                               t.Errorf("transmission of %s failed: got %s want %s", x, rx.String(), tx.String())
+                                               continue
+                                       }
+
+                                       if rx.Prec() != prec {
+                                               t.Errorf("transmission of %s's prec failed: got %d want %d", x, rx.Prec(), prec)
+                                       }
+
+                                       if rx.Mode() != mode {
+                                               t.Errorf("transmission of %s's mode failed: got %s want %s", x, rx.Mode(), mode)
+                                       }
+
+                                       if rx.Acc() != tx.Acc() {
+                                               t.Errorf("transmission of %s's accuracy failed: got %s want %s", x, rx.Acc(), tx.Acc())
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func TestFloatCorruptGob(t *testing.T) {
+       var buf bytes.Buffer
+       tx := NewFloat(4 / 3).SetPrec(1000).SetMode(ToPositiveInf)
+       if err := gob.NewEncoder(&buf).Encode(tx); err != nil {
+               t.Fatal(err)
+       }
+       b := buf.Bytes()
+
+       var rx Float
+       if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&rx); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := gob.NewDecoder(bytes.NewReader(b[:10])).Decode(&rx); err != io.ErrUnexpectedEOF {
+               t.Errorf("got %v want EOF", err)
+       }
+
+       b[1] = 0
+       if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&rx); err == nil {
+               t.Fatal("got nil want version error")
+       }
+}
+
 func TestFloatJSONEncoding(t *testing.T) {
        for _, test := range floatVals {
                for _, sign := range []string{"", "+", "-"} {
index 624ea5e0731926c67b8cbd5cc02a25f9f57fa9bb..57b16e1ad1ff7ffa337eabbe961458464d8c9ac1 100644 (file)
@@ -41,8 +41,11 @@ import (
 // x.Prec() mantissa bits.
 // The prec value is ignored for the 'b' or 'p' format.
 func (x *Float) Text(format byte, prec int) string {
-       const extra = 10 // TODO(gri) determine a good/better value here
-       return string(x.Append(make([]byte, 0, prec+extra), format, prec))
+       cap := 10 // TODO(gri) determine a good/better value here
+       if prec > 0 {
+               cap += prec
+       }
+       return string(x.Append(make([]byte, 0, cap), format, prec))
 }
 
 // String formats x like x.Text('g', 10).
index c0b9f583000dfb4f06da9940f1572bb5f60fa811..a929bf597f42da91997ed062afe7592f5809dc17 100644 (file)
@@ -20,13 +20,27 @@ func randInt(r *rand.Rand, size uint) *Int {
 }
 
 func runGCD(b *testing.B, aSize, bSize uint) {
+       b.Run("WithoutXY", func(b *testing.B) {
+               runGCDExt(b, aSize, bSize, false)
+       })
+       b.Run("WithXY", func(b *testing.B) {
+               runGCDExt(b, aSize, bSize, true)
+       })
+}
+
+func runGCDExt(b *testing.B, aSize, bSize uint, calcXY bool) {
        b.StopTimer()
        var r = rand.New(rand.NewSource(1234))
        aa := randInt(r, aSize)
        bb := randInt(r, bSize)
+       var x, y *Int
+       if calcXY {
+               x = new(Int)
+               y = new(Int)
+       }
        b.StartTimer()
        for i := 0; i < b.N; i++ {
-               new(Int).GCD(nil, nil, aa, bb)
+               new(Int).GCD(x, y, aa, bb)
        }
 }
 
index 67ab7042ffe699b41d8da7b0081f670d163a6cf9..f2a75d1cd50076fa18f4d55f7606344e44a429d2 100644 (file)
@@ -459,11 +459,11 @@ func (z *Int) GCD(x, y, a, b *Int) *Int {
        q := new(Int)
        temp := new(Int)
 
+       r := new(Int)
        for len(B.abs) > 0 {
-               r := new(Int)
                q, r = q.QuoRem(A, B, r)
 
-               A, B = B, r
+               A, B, r = B, r, A
 
                temp.Set(X)
                X.Mul(X, q)
index 7668b6481b3ed78c9293e9226ead25a671eae22f..2e65d2a7ef7f33ec21e6cfc4f95d9dc989b6cf62 100644 (file)
@@ -8,7 +8,10 @@
 
 package big
 
-import "math/rand"
+import (
+       "math/rand"
+       "sync"
+)
 
 // An unsigned integer x of the form
 //
@@ -539,6 +542,21 @@ func (z nat) div(z2, u, v nat) (q, r nat) {
        return
 }
 
+// getNat returns a nat of len n. The contents may not be zero.
+func getNat(n int) nat {
+       var z nat
+       if v := natPool.Get(); v != nil {
+               z = v.(nat)
+       }
+       return z.make(n)
+}
+
+func putNat(x nat) {
+       natPool.Put(x)
+}
+
+var natPool sync.Pool
+
 // q = (uIn-r)/v, with 0 <= r < y
 // Uses z as storage for q, and u as storage for r if possible.
 // See Knuth, Volume 2, section 4.3.1, Algorithm D.
@@ -557,7 +575,7 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
        }
        q = z.make(m + 1)
 
-       qhatv := make(nat, n+1)
+       qhatv := getNat(n + 1)
        if alias(u, uIn) || alias(u, v) {
                u = nil // u is an alias for uIn or v - cannot reuse
        }
@@ -565,10 +583,11 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
        u.clear() // TODO(gri) no need to clear if we allocated a new u
 
        // D1.
+       var v1 nat
        shift := nlz(v[n-1])
        if shift > 0 {
                // do not modify v, it may be used by another goroutine simultaneously
-               v1 := make(nat, n)
+               v1 = getNat(n)
                shlVU(v1, v, shift)
                v = v1
        }
@@ -609,6 +628,10 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
 
                q[j] = qhat
        }
+       if v1 != nil {
+               putNat(v1)
+       }
+       putNat(qhatv)
 
        q = q.norm()
        shrVU(u, u, shift)
index 563ccb30523adab0c1f4982cc40bc1e67ea2944b..ebb29856549b8ef47a9eb228fa71adfa0f7e7e83 100644 (file)
@@ -5,6 +5,7 @@
 package big
 
 import (
+       "fmt"
        "runtime"
        "strings"
        "testing"
@@ -509,24 +510,20 @@ func TestExpNN(t *testing.T) {
        }
 }
 
-func ExpHelper(b *testing.B, x, y Word) {
-       var z nat
-       for i := 0; i < b.N; i++ {
-               z.expWW(x, y)
+func BenchmarkExp3Power(b *testing.B) {
+       const x = 3
+       for _, y := range []Word{
+               0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000,
+       } {
+               b.Run(fmt.Sprintf("%#x", y), func(b *testing.B) {
+                       var z nat
+                       for i := 0; i < b.N; i++ {
+                               z.expWW(x, y)
+                       }
+               })
        }
 }
 
-func BenchmarkExp3Power0x10(b *testing.B)     { ExpHelper(b, 3, 0x10) }
-func BenchmarkExp3Power0x40(b *testing.B)     { ExpHelper(b, 3, 0x40) }
-func BenchmarkExp3Power0x100(b *testing.B)    { ExpHelper(b, 3, 0x100) }
-func BenchmarkExp3Power0x400(b *testing.B)    { ExpHelper(b, 3, 0x400) }
-func BenchmarkExp3Power0x1000(b *testing.B)   { ExpHelper(b, 3, 0x1000) }
-func BenchmarkExp3Power0x4000(b *testing.B)   { ExpHelper(b, 3, 0x4000) }
-func BenchmarkExp3Power0x10000(b *testing.B)  { ExpHelper(b, 3, 0x10000) }
-func BenchmarkExp3Power0x40000(b *testing.B)  { ExpHelper(b, 3, 0x40000) }
-func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
-func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }
-
 func fibo(n int) nat {
        switch n {
        case 0:
index d2ce667fb602c5c695414e1219dbf166fb4719f1..44547842c1ced2f86a225e17f59a35d5c4bd6f47 100644 (file)
@@ -302,7 +302,7 @@ func (x nat) itoa(neg bool, base int) []byte {
                }
 
        } else {
-               bb, ndigits := maxPow(Word(b))
+               bb, ndigits := maxPow(b)
 
                // construct table of successive squares of bb*leafSize to use in subdivisions
                // result (table != nil) <=> (len(x) > leafSize > 0)
@@ -391,7 +391,7 @@ func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []diviso
                                // this appears to be faster for BenchmarkString10000Base10
                                // and smaller strings (but a bit slower for larger ones)
                                t := r / 10
-                               s[i] = '0' + byte(r-t<<3-t-t) // TODO(gri) replace w/ t*10 once compiler produces better code
+                               s[i] = '0' + byte(r-t*10)
                                r = t
                        }
                }
index 028e5a858eb98c4e7054abb709bdb376a1de9a3b..79901d1880477fe6f402f9f95240e1e250d38a7e 100644 (file)
@@ -6,6 +6,7 @@ package big
 
 import (
        "bytes"
+       "fmt"
        "io"
        "strings"
        "testing"
@@ -273,102 +274,58 @@ func BenchmarkStringPiParallel(b *testing.B) {
        })
 }
 
-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)
-
-       s := z.utoa(base)
-       if t := itoa(z, base); !bytes.Equal(s, t) {
-               b.Fatalf("scanning: got %s; want %s", s, t)
+func BenchmarkScan(b *testing.B) {
+       const x = 10
+       for _, base := range []int{2, 8, 10, 16} {
+               for _, y := range []Word{10, 100, 1000, 10000, 100000} {
+                       b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) {
+                               b.StopTimer()
+                               var z nat
+                               z = z.expWW(x, y)
+
+                               s := z.utoa(base)
+                               if t := itoa(z, base); !bytes.Equal(s, t) {
+                                       b.Fatalf("scanning: got %s; want %s", s, t)
+                               }
+                               b.StartTimer()
+
+                               for i := 0; i < b.N; i++ {
+                                       z.scan(bytes.NewReader(s), base, false)
+                               }
+                       })
+               }
        }
-       b.StartTimer()
+}
 
-       for i := 0; i < b.N; i++ {
-               z.scan(bytes.NewReader(s), base, false)
+func BenchmarkString(b *testing.B) {
+       const x = 10
+       for _, base := range []int{2, 8, 10, 16} {
+               for _, y := range []Word{10, 100, 1000, 10000, 100000} {
+                       b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) {
+                               b.StopTimer()
+                               var z nat
+                               z = z.expWW(x, y)
+                               z.utoa(base) // warm divisor cache
+                               b.StartTimer()
+
+                               for i := 0; i < b.N; i++ {
+                                       _ = z.utoa(base)
+                               }
+                       })
+               }
        }
 }
 
-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.utoa(base) // warm divisor cache
-       b.StartTimer()
-
-       for i := 0; i < b.N; i++ {
-               _ = z.utoa(base)
+func BenchmarkLeafSize(b *testing.B) {
+       for n := 0; n <= 16; n++ {
+               b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) })
+       }
+       // Try some large lengths
+       for _, n := range []int{32, 64} {
+               b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) })
        }
 }
 
-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, size int) {
        b.StopTimer()
        originalLeafSize := leafSize
index 57df124e88fc97b25a21895360ca0c492153b0d0..ef2b6750d0c0c4e3e68cc35602c5df4113013c51 100644 (file)
@@ -88,6 +88,12 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
                return nil, false
        }
 
+       // special-case 0 (see also issue #16176)
+       if len(z.a.abs) == 0 {
+               return z, true
+       }
+       // len(z.a.abs) > 0
+
        // correct exponent
        if ecorr < 0 {
                exp += int64(ecorr)
@@ -178,7 +184,7 @@ func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err err
                        }
                        break // i > 0
                }
-               digits = append(digits, byte(ch))
+               digits = append(digits, ch)
        }
        // i > 0 => we have at least one digit
 
index 17bda4763713d731fcfec1282efe7d4b631e0480..35ad6ccea773a873391e8e64ea154804abe55659 100644 (file)
@@ -48,6 +48,7 @@ var setStringTests = []StringTest{
        {"53/70893980658822810696", "53/70893980658822810696", true},
        {"106/141787961317645621392", "53/70893980658822810696", true},
        {"204211327800791583.81095", "4084226556015831676219/20000", true},
+       {"0e9999999999", "0", true}, // issue #16176
        {in: "1/0"},
 }