summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/text/internal/number
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/text/internal/number')
-rw-r--r--vendor/golang.org/x/text/internal/number/decimal.go430
-rw-r--r--vendor/golang.org/x/text/internal/number/decimal_test.go294
-rwxr-xr-xvendor/golang.org/x/text/internal/number/format.go321
-rwxr-xr-xvendor/golang.org/x/text/internal/number/format_test.go363
-rw-r--r--vendor/golang.org/x/text/internal/number/number.go9
-rw-r--r--vendor/golang.org/x/text/internal/number/number_test.go24
-rw-r--r--vendor/golang.org/x/text/internal/number/pattern.go28
-rw-r--r--vendor/golang.org/x/text/internal/number/pattern_test.go61
-rw-r--r--vendor/golang.org/x/text/internal/number/roundingmode_string.go16
-rw-r--r--vendor/golang.org/x/text/internal/number/tables.go4
10 files changed, 1530 insertions, 20 deletions
diff --git a/vendor/golang.org/x/text/internal/number/decimal.go b/vendor/golang.org/x/text/internal/number/decimal.go
new file mode 100644
index 000000000..4e42ec785
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/decimal.go
@@ -0,0 +1,430 @@
+// Copyright 2017 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.
+
+//go:generate stringer -type RoundingMode
+
+package number
+
+import (
+ "math"
+ "strconv"
+)
+
+// RoundingMode determines how a number is rounded to the desired precision.
+type RoundingMode byte
+
+const (
+ ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
+ ToNearestZero // towards the nearest integer, or towards zero if equidistant.
+ ToNearestAway // towards the nearest integer, or away from zero if equidistant.
+ ToPositiveInf // towards infinity
+ ToNegativeInf // towards negative infinity
+ ToZero // towards zero
+ AwayFromZero // away from zero
+ numModes
+)
+
+// A RoundingContext indicates how a number should be converted to digits.
+type RoundingContext struct {
+ Mode RoundingMode
+ Increment int32 // if > 0, round to Increment * 10^-Scale
+
+ Precision int32 // maximum number of significant digits.
+ Scale int32 // maximum number of decimals after the dot.
+}
+
+// A Decimal represents floating point number represented in digits of the base
+// in which a number is to be displayed. Digits represents a number [0, 1.0),
+// and the absolute value represented by Decimal is Digits * 10^Exp.
+// Leading and trailing zeros may be omitted and Exp may point outside a valid
+// position in Digits.
+//
+// Examples:
+// Number Decimal
+// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5
+// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2
+// 12000 Digits: [1, 2], Exp: 5
+// 0.00123 Digits: [1, 2, 3], Exp: -2
+type Decimal struct {
+ Digits []byte // mantissa digits, big-endian
+ Exp int32 // exponent
+ Neg bool
+ Inf bool // Takes precedence over Digits and Exp.
+ NaN bool // Takes precedence over Inf.
+
+ buf [10]byte
+}
+
+// normalize retuns a new Decimal with leading and trailing zeros removed.
+func (d *Decimal) normalize() (n Decimal) {
+ n = *d
+ b := n.Digits
+ // Strip leading zeros. Resulting number of digits is significant digits.
+ for len(b) > 0 && b[0] == 0 {
+ b = b[1:]
+ n.Exp--
+ }
+ // Strip trailing zeros
+ for len(b) > 0 && b[len(b)-1] == 0 {
+ b = b[:len(b)-1]
+ }
+ if len(b) == 0 {
+ n.Exp = 0
+ }
+ n.Digits = b
+ return n
+}
+
+func (d *Decimal) clear() {
+ b := d.Digits
+ if b == nil {
+ b = d.buf[:0]
+ }
+ *d = Decimal{}
+ d.Digits = b[:0]
+}
+
+func (x *Decimal) String() string {
+ if x.NaN {
+ return "NaN"
+ }
+ var buf []byte
+ if x.Neg {
+ buf = append(buf, '-')
+ }
+ if x.Inf {
+ buf = append(buf, "Inf"...)
+ return string(buf)
+ }
+ if len(x.Digits) == 0 {
+ return "0"
+ }
+ switch {
+ case x.Exp <= 0:
+ // 0.00ddd
+ buf = append(buf, "0."...)
+ buf = appendZeros(buf, -int(x.Exp))
+ buf = appendDigits(buf, x.Digits)
+
+ case /* 0 < */ int(x.Exp) < len(x.Digits):
+ // dd.ddd
+ buf = appendDigits(buf, x.Digits[:x.Exp])
+ buf = append(buf, '.')
+ buf = appendDigits(buf, x.Digits[x.Exp:])
+
+ default: // len(x.Digits) <= x.Exp
+ // ddd00
+ buf = appendDigits(buf, x.Digits)
+ buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
+ }
+ return string(buf)
+}
+
+func appendDigits(buf []byte, digits []byte) []byte {
+ for _, c := range digits {
+ buf = append(buf, c+'0')
+ }
+ return buf
+}
+
+// appendZeros appends n 0 digits to buf and returns buf.
+func appendZeros(buf []byte, n int) []byte {
+ for ; n > 0; n-- {
+ buf = append(buf, '0')
+ }
+ return buf
+}
+
+func (d *Decimal) round(mode RoundingMode, n int) {
+ if n >= len(d.Digits) {
+ return
+ }
+ // Make rounding decision: The result mantissa is truncated ("rounded down")
+ // by default. Decide if we need to increment, or "round up", the (unsigned)
+ // mantissa.
+ inc := false
+ switch mode {
+ case ToNegativeInf:
+ inc = d.Neg
+ case ToPositiveInf:
+ inc = !d.Neg
+ case ToZero:
+ // nothing to do
+ case AwayFromZero:
+ inc = true
+ case ToNearestEven:
+ inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
+ (len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
+ case ToNearestAway:
+ inc = d.Digits[n] >= 5
+ case ToNearestZero:
+ inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
+ default:
+ panic("unreachable")
+ }
+ if inc {
+ d.roundUp(n)
+ } else {
+ d.roundDown(n)
+ }
+}
+
+// roundFloat rounds a floating point number.
+func (r RoundingMode) roundFloat(x float64) float64 {
+ // Make rounding decision: The result mantissa is truncated ("rounded down")
+ // by default. Decide if we need to increment, or "round up", the (unsigned)
+ // mantissa.
+ abs := x
+ if x < 0 {
+ abs = -x
+ }
+ i, f := math.Modf(abs)
+ if f == 0.0 {
+ return x
+ }
+ inc := false
+ switch r {
+ case ToNegativeInf:
+ inc = x < 0
+ case ToPositiveInf:
+ inc = x >= 0
+ case ToZero:
+ // nothing to do
+ case AwayFromZero:
+ inc = true
+ case ToNearestEven:
+ // TODO: check overflow
+ inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
+ case ToNearestAway:
+ inc = f >= 0.5
+ case ToNearestZero:
+ inc = f > 0.5
+ default:
+ panic("unreachable")
+ }
+ if inc {
+ i += 1
+ }
+ if abs != x {
+ i = -i
+ }
+ return i
+}
+
+func (x *Decimal) roundUp(n int) {
+ if n < 0 || n >= len(x.Digits) {
+ return // nothing to do
+ }
+ // find first digit < 9
+ for n > 0 && x.Digits[n-1] >= 9 {
+ n--
+ }
+
+ if n == 0 {
+ // all digits are 9s => round up to 1 and update exponent
+ x.Digits[0] = 1 // ok since len(x.Digits) > n
+ x.Digits = x.Digits[:1]
+ x.Exp++
+ return
+ }
+ x.Digits[n-1]++
+ x.Digits = x.Digits[:n]
+ // x already trimmed
+}
+
+func (x *Decimal) roundDown(n int) {
+ if n < 0 || n >= len(x.Digits) {
+ return // nothing to do
+ }
+ x.Digits = x.Digits[:n]
+ trim(x)
+}
+
+// trim cuts off any trailing zeros from x's mantissa;
+// they are meaningless for the value of x.
+func trim(x *Decimal) {
+ i := len(x.Digits)
+ for i > 0 && x.Digits[i-1] == 0 {
+ i--
+ }
+ x.Digits = x.Digits[:i]
+ if i == 0 {
+ x.Exp = 0
+ }
+}
+
+// A Converter converts a number into decimals according to the given rounding
+// criteria.
+type Converter interface {
+ Convert(d *Decimal, r *RoundingContext)
+}
+
+const (
+ signed = true
+ unsigned = false
+)
+
+// Convert converts the given number to the decimal representation using the
+// supplied RoundingContext.
+func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
+ switch f := number.(type) {
+ case Converter:
+ d.clear()
+ f.Convert(d, r)
+ case float32:
+ d.convertFloat64(r, float64(f), 32)
+ case float64:
+ d.convertFloat64(r, f, 64)
+ case int:
+ d.convertInt(r, signed, uint64(f))
+ case int8:
+ d.convertInt(r, signed, uint64(f))
+ case int16:
+ d.convertInt(r, signed, uint64(f))
+ case int32:
+ d.convertInt(r, signed, uint64(f))
+ case int64:
+ d.convertInt(r, signed, uint64(f))
+ case uint:
+ d.convertInt(r, unsigned, uint64(f))
+ case uint8:
+ d.convertInt(r, unsigned, uint64(f))
+ case uint16:
+ d.convertInt(r, unsigned, uint64(f))
+ case uint32:
+ d.convertInt(r, unsigned, uint64(f))
+ case uint64:
+ d.convertInt(r, unsigned, f)
+
+ // TODO:
+ // case string: if produced by strconv, allows for easy arbitrary pos.
+ // case reflect.Value:
+ // case big.Float
+ // case big.Int
+ // case big.Rat?
+ // catch underlyings using reflect or will this already be done by the
+ // message package?
+ }
+}
+
+func (d *Decimal) convertInt(r *RoundingContext, signed bool, x uint64) {
+ if r.Increment > 0 {
+ // TODO: if uint64 is too large, fall back to float64
+ if signed {
+ d.convertFloat64(r, float64(int64(x)), 64)
+ } else {
+ d.convertFloat64(r, float64(x), 64)
+ }
+ return
+ }
+ d.clear()
+ if signed && int64(x) < 0 {
+ x = uint64(-int64(x))
+ d.Neg = true
+ }
+ d.fillIntDigits(x)
+ d.Exp = int32(len(d.Digits))
+}
+
+func (d *Decimal) convertFloat64(r *RoundingContext, x float64, size int) {
+ d.clear()
+ if math.IsNaN(x) {
+ d.NaN = true
+ return
+ }
+ abs := x
+ if x < 0 {
+ d.Neg = true
+ abs = -x
+ }
+ if math.IsInf(abs, 1) {
+ d.Inf = true
+ return
+ }
+ // Simple case: decimal notation
+ if r.Scale > 0 || r.Increment > 0 && r.Scale == 0 {
+ if int(r.Scale) > len(scales) {
+ x *= math.Pow(10, float64(r.Scale))
+ } else {
+ x *= scales[r.Scale]
+ }
+ if r.Increment > 0 {
+ inc := float64(r.Increment)
+ x /= float64(inc)
+ x = r.Mode.roundFloat(x)
+ x *= inc
+ } else {
+ x = r.Mode.roundFloat(x)
+ }
+ d.fillIntDigits(uint64(math.Abs(x)))
+ d.Exp = int32(len(d.Digits)) - r.Scale
+ return
+ }
+
+ // Nasty case (for non-decimal notation).
+ // Asides from being inefficient, this result is also wrong as it will
+ // apply ToNearestEven rounding regardless of the user setting.
+ // TODO: expose functionality in strconv so we can avoid this hack.
+ // Something like this would work:
+ // AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
+
+ prec := int(r.Precision)
+ if prec > 0 {
+ prec--
+ }
+ b := strconv.AppendFloat(d.Digits, abs, 'e', prec, size)
+ i := 0
+ k := 0
+ // No need to check i < len(b) as we always have an 'e'.
+ for {
+ if c := b[i]; '0' <= c && c <= '9' {
+ b[k] = c - '0'
+ k++
+ } else if c != '.' {
+ break
+ }
+ i++
+ }
+ d.Digits = b[:k]
+ i += len("e")
+ pSign := i
+ exp := 0
+ for i++; i < len(b); i++ {
+ exp *= 10
+ exp += int(b[i] - '0')
+ }
+ if b[pSign] == '-' {
+ exp = -exp
+ }
+ d.Exp = int32(exp) + 1
+}
+
+func (d *Decimal) fillIntDigits(x uint64) {
+ const maxUintDigits = 10
+ if cap(d.Digits) < maxUintDigits {
+ d.Digits = d.buf[:]
+ } else {
+ d.Digits = d.buf[:maxUintDigits]
+ }
+ i := 0
+ for ; x > 0; x /= 10 {
+ d.Digits[i] = byte(x % 10)
+ i++
+ }
+ d.Digits = d.Digits[:i]
+ for p := 0; p < i; p++ {
+ i--
+ d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
+ }
+}
+
+var scales [70]float64
+
+func init() {
+ x := 1.0
+ for i := range scales {
+ scales[i] = x
+ x *= 10
+ }
+}
diff --git a/vendor/golang.org/x/text/internal/number/decimal_test.go b/vendor/golang.org/x/text/internal/number/decimal_test.go
new file mode 100644
index 000000000..b99fedc40
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/decimal_test.go
@@ -0,0 +1,294 @@
+// Copyright 2017 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 number
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func mkfloat(num string) float64 {
+ u, _ := strconv.ParseUint(num, 10, 32)
+ return float64(u)
+}
+
+// mkdec creates a decimal from a string. All ASCII digits are converted to
+// digits in the decimal. The dot is used to indicate the scale by which the
+// digits are shifted. Numbers may have an additional exponent or be the special
+// value NaN, Inf, or -Inf.
+func mkdec(num string) (d Decimal) {
+ if num[0] == '-' {
+ d.Neg = true
+ num = num[1:]
+ }
+ switch num {
+ case "NaN":
+ d.NaN = true
+ return
+ case "Inf":
+ d.Inf = true
+ return
+ }
+ if p := strings.IndexAny(num, "eE"); p != -1 {
+ i64, err := strconv.ParseInt(num[p+1:], 10, 32)
+ if err != nil {
+ panic(err)
+ }
+ d.Exp = int32(i64)
+ num = num[:p]
+ }
+ if p := strings.IndexByte(num, '.'); p != -1 {
+ d.Exp += int32(p)
+ num = num[:p] + num[p+1:]
+ } else {
+ d.Exp += int32(len(num))
+ }
+ d.Digits = []byte(num)
+ for i := range d.Digits {
+ d.Digits[i] -= '0'
+ }
+ return d.normalize()
+}
+
+func byteNum(s string) []byte {
+ b := make([]byte, len(s))
+ for i := 0; i < len(s); i++ {
+ if c := s[i]; '0' <= c && c <= '9' {
+ b[i] = s[i] - '0'
+ } else {
+ b[i] = s[i] - 'a' + 10
+ }
+ }
+ return b
+}
+
+func strNum(s string) string {
+ return string(byteNum(s))
+}
+
+func TestDecimalString(t *testing.T) {
+ for _, test := range []struct {
+ x Decimal
+ want string
+ }{
+ {want: "0"},
+ {Decimal{Digits: nil, Exp: 1000}, "0"}, // exponent of 1000 is ignored
+ {Decimal{Digits: byteNum("12345"), Exp: 0}, "0.12345"},
+ {Decimal{Digits: byteNum("12345"), Exp: -3}, "0.00012345"},
+ {Decimal{Digits: byteNum("12345"), Exp: +3}, "123.45"},
+ {Decimal{Digits: byteNum("12345"), Exp: +10}, "1234500000"},
+ } {
+ if got := test.x.String(); got != test.want {
+ t.Errorf("%v == %q; want %q", test.x, got, test.want)
+ }
+ }
+}
+
+func TestRounding(t *testing.T) {
+ testCases := []struct {
+ x string
+ n int
+ // modes is the result for modes. Signs are left out of the result.
+ // The results are stored in the following order:
+ // zero, negInf
+ // nearZero, nearEven, nearAway
+ // away, posInf
+ modes [numModes]string
+ }{
+ {"0", 1, [numModes]string{
+ "0", "0",
+ "0", "0", "0",
+ "0", "0"}},
+ {"1", 1, [numModes]string{
+ "1", "1",
+ "1", "1", "1",
+ "1", "1"}},
+ {"5", 1, [numModes]string{
+ "5", "5",
+ "5", "5", "5",
+ "5", "5"}},
+ {"15", 1, [numModes]string{
+ "10", "10",
+ "10", "20", "20",
+ "20", "20"}},
+ {"45", 1, [numModes]string{
+ "40", "40",
+ "40", "40", "50",
+ "50", "50"}},
+ {"95", 1, [numModes]string{
+ "90", "90",
+ "90", "100", "100",
+ "100", "100"}},
+
+ {"12344999", 4, [numModes]string{
+ "12340000", "12340000",
+ "12340000", "12340000", "12340000",
+ "12350000", "12350000"}},
+ {"12345000", 4, [numModes]string{
+ "12340000", "12340000",
+ "12340000", "12340000", "12350000",
+ "12350000", "12350000"}},
+ {"12345001", 4, [numModes]string{
+ "12340000", "12340000",
+ "12350000", "12350000", "12350000",
+ "12350000", "12350000"}},
+ {"12345100", 4, [numModes]string{
+ "12340000", "12340000",
+ "12350000", "12350000", "12350000",
+ "12350000", "12350000"}},
+ {"23454999", 4, [numModes]string{
+ "23450000", "23450000",
+ "23450000", "23450000", "23450000",
+ "23460000", "23460000"}},
+ {"23455000", 4, [numModes]string{
+ "23450000", "23450000",
+ "23450000", "23460000", "23460000",
+ "23460000", "23460000"}},
+ {"23455001", 4, [numModes]string{
+ "23450000", "23450000",
+ "23460000", "23460000", "23460000",
+ "23460000", "23460000"}},
+ {"23455100", 4, [numModes]string{
+ "23450000", "23450000",
+ "23460000", "23460000", "23460000",
+ "23460000", "23460000"}},
+
+ {"99994999", 4, [numModes]string{
+ "99990000", "99990000",
+ "99990000", "99990000", "99990000",
+ "100000000", "100000000"}},
+ {"99995000", 4, [numModes]string{
+ "99990000", "99990000",
+ "99990000", "100000000", "100000000",
+ "100000000", "100000000"}},
+ {"99999999", 4, [numModes]string{
+ "99990000", "99990000",
+ "100000000", "100000000", "100000000",
+ "100000000", "100000000"}},
+
+ {"12994999", 4, [numModes]string{
+ "12990000", "12990000",
+ "12990000", "12990000", "12990000",
+ "13000000", "13000000"}},
+ {"12995000", 4, [numModes]string{
+ "12990000", "12990000",
+ "12990000", "13000000", "13000000",
+ "13000000", "13000000"}},
+ {"12999999", 4, [numModes]string{
+ "12990000", "12990000",
+ "13000000", "13000000", "13000000",
+ "13000000", "13000000"}},
+ }
+ modes := []RoundingMode{
+ ToZero, ToNegativeInf,
+ ToNearestZero, ToNearestEven, ToNearestAway,
+ AwayFromZero, ToPositiveInf,
+ }
+ for _, tc := range testCases {
+ // Create negative counterpart tests: the sign is reversed and
+ // ToPositiveInf and ToNegativeInf swapped.
+ negModes := tc.modes
+ negModes[1], negModes[6] = negModes[6], negModes[1]
+ for i, res := range negModes {
+ if res != "0" {
+ negModes[i] = "-" + res
+ }
+ }
+
+ for i, m := range modes {
+ t.Run(fmt.Sprintf("v:%s/n:%d/%s", tc.x, tc.n, m), func(t *testing.T) {
+ d := mkdec(tc.x)
+ d.round(m, tc.n)
+ if got := d.String(); got != tc.modes[i] {
+ t.Errorf("pos decimal: got %q; want %q", d.String(), tc.modes[i])
+ }
+
+ mult := math.Pow(10, float64(len(tc.x)-tc.n))
+ f := mkfloat(tc.x)
+ f = m.roundFloat(f/mult) * mult
+ if got := fmt.Sprintf("%.0f", f); got != tc.modes[i] {
+ t.Errorf("pos float: got %q; want %q", got, tc.modes[i])
+ }
+
+ // Test the negative case. This is the same as the positive
+ // case, but with ToPositiveInf and ToNegativeInf swapped.
+ d = mkdec(tc.x)
+ d.Neg = true
+ d.round(m, tc.n)
+ if got, want := d.String(), negModes[i]; got != want {
+ t.Errorf("neg decimal: got %q; want %q", d.String(), want)
+ }
+
+ if f = mkfloat(tc.x); f != 0 {
+ f = -f // avoid creating -0.0
+ }
+ f = m.roundFloat(f/mult) * mult
+ if got := fmt.Sprintf("%.0f", f); got != negModes[i] {
+ t.Errorf("neg float: got %q; want %q", got, negModes[i])
+ }
+ })
+ }
+ }
+}
+
+func TestConvert(t *testing.T) {
+ scale2 := &RoundingContext{Scale: 2}
+ scale2away := &RoundingContext{Scale: 2, Mode: AwayFromZero}
+ inc0_05 := &RoundingContext{Increment: 5, Scale: 2}
+ inc50 := &RoundingContext{Increment: 50}
+ prec3 := &RoundingContext{Precision: 3}
+ testCases := []struct {
+ x interface{}
+ rc *RoundingContext
+ out string
+ }{
+ {int8(-34), scale2, "-34"},
+ {int16(-234), scale2, "-234"},
+ {int32(-234), scale2, "-234"},
+ {int64(-234), scale2, "-234"},
+ {int(-234), scale2, "-234"},
+ {uint8(234), scale2, "234"},
+ {uint16(234), scale2, "234"},
+ {uint32(234), scale2, "234"},
+ {uint64(234), scale2, "234"},
+ {uint(234), scale2, "234"},
+ {0.234, scale2, "0.23"},
+ {0.234, scale2away, "0.24"},
+ {0.1234, prec3, "0.123"},
+ {1234.0, prec3, "1230"},
+ {1.2345e10, prec3, "12300000000"},
+
+ {0.03, inc0_05, "0.05"},
+ {0.025, inc0_05, "0"},
+ {0.075, inc0_05, "0.10"},
+ {325, inc50, "300"},
+ {375, inc50, "400"},
+
+ {converter(3), scale2, "100"},
+
+ {math.Inf(1), inc50, "Inf"},
+ {math.Inf(-1), inc50, "-Inf"},
+ {math.NaN(), inc50, "NaN"},
+ }
+ for _, tc := range testCases {
+ var d Decimal
+ t.Run(fmt.Sprintf("%T:%v-%v", tc.x, tc.x, tc.rc), func(t *testing.T) {
+ d.Convert(tc.rc, tc.x)
+ if got := d.String(); got != tc.out {
+ t.Errorf("got %q; want %q", got, tc.out)
+ }
+ })
+ }
+}
+
+type converter int
+
+func (c converter) Convert(d *Decimal, r *RoundingContext) {
+ d.Digits = append(d.Digits, 1, 0, 0)
+ d.Exp = 3
+}
diff --git a/vendor/golang.org/x/text/internal/number/format.go b/vendor/golang.org/x/text/internal/number/format.go
new file mode 100755
index 000000000..84903fad8
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/format.go
@@ -0,0 +1,321 @@
+// Copyright 2017 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 number
+
+import (
+ "strconv"
+
+ "golang.org/x/text/language"
+)
+
+// TODO:
+// - public (but internal) API for creating formatters
+// - split out the logic that computes the visible digits from the rest of the
+// formatting code (needed for plural).
+// - grouping of fractions
+// - reuse percent pattern for permille
+// - padding
+
+// Formatter contains all the information needed to render a number.
+type Formatter struct {
+ *Pattern
+ Info
+ RoundingContext
+ f func(dst []byte, f *Formatter, d *Decimal) []byte
+}
+
+func lookupFormat(t language.Tag, tagToIndex []uint8) *Pattern {
+ for ; ; t = t.Parent() {
+ if ci, ok := language.CompactIndex(t); ok {
+ return &formats[tagToIndex[ci]]
+ }
+ }
+}
+
+func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
+ return f.f(dst, f, d)
+}
+
+func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
+ if dst, ok := f.renderSpecial(dst, d); ok {
+ return dst
+ }
+ n := d.normalize()
+ if maxSig := int(f.MaxSignificantDigits); maxSig > 0 {
+ n.round(ToZero, maxSig)
+ }
+ digits := n.Digits
+ exp := n.Exp
+
+ // Split in integer and fraction part.
+ var intDigits, fracDigits []byte
+ var numInt, numFrac int
+ if exp > 0 {
+ numInt = int(exp)
+ if int(exp) >= len(digits) { // ddddd | ddddd00
+ intDigits = digits
+ } else { // ddd.dd
+ intDigits = digits[:exp]
+ fracDigits = digits[exp:]
+ numFrac = len(fracDigits)
+ }
+ } else {
+ fracDigits = digits
+ numFrac = -int(exp) + len(digits)
+ }
+ // Cap integer digits. Remove *most-significant* digits.
+ if f.MaxIntegerDigits > 0 && numInt > int(f.MaxIntegerDigits) {
+ offset := numInt - int(f.MaxIntegerDigits)
+ if offset > len(intDigits) {
+ numInt = 0
+ intDigits = nil
+ } else {
+ numInt = int(f.MaxIntegerDigits)
+ intDigits = intDigits[offset:]
+ // for keeping track of significant digits
+ digits = digits[offset:]
+ }
+ // Strip leading zeros. Resulting number of digits is significant digits.
+ for len(intDigits) > 0 && intDigits[0] == 0 {
+ intDigits = intDigits[1:]
+ digits = digits[1:]
+ numInt--
+ }
+ }
+ if f.MaxSignificantDigits == 0 && int(f.MaxFractionDigits) < numFrac {
+ if extra := numFrac - int(f.MaxFractionDigits); extra > len(fracDigits) {
+ numFrac = 0
+ fracDigits = nil
+ } else {
+ numFrac = int(f.MaxFractionDigits)
+ fracDigits = fracDigits[:len(fracDigits)-extra]
+ }
+ }
+
+ neg := d.Neg && numInt+numFrac > 0
+ affix, suffix := f.getAffixes(neg)
+ dst = appendAffix(dst, f, affix, neg)
+ savedLen := len(dst)
+
+ minInt := int(f.MinIntegerDigits)
+ if minInt == 0 && f.MinSignificantDigits > 0 {
+ minInt = 1
+ }
+ // add leading zeros
+ for i := numInt; i < minInt; i++ {
+ dst = f.AppendDigit(dst, 0)
+ if f.needsSep(minInt - i) {
+ dst = append(dst, f.Symbol(SymGroup)...)
+ }
+ }
+ i := 0
+ for ; i < len(intDigits); i++ {
+ dst = f.AppendDigit(dst, intDigits[i])
+ if f.needsSep(numInt - i) {
+ dst = append(dst, f.Symbol(SymGroup)...)
+ }
+ }
+ for ; i < numInt; i++ {
+ dst = f.AppendDigit(dst, 0)
+ if f.needsSep(numInt - i) {
+ dst = append(dst, f.Symbol(SymGroup)...)
+ }
+ }
+
+ trailZero := int(f.MinFractionDigits) - numFrac
+ if d := int(f.MinSignificantDigits) - len(digits); d > 0 && d > trailZero {
+ trailZero = d
+ }
+ if numFrac > 0 || trailZero > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
+ dst = append(dst, f.Symbol(SymDecimal)...)
+ }
+ // Add leading zeros
+ for i := numFrac - len(fracDigits); i > 0; i-- {
+ dst = f.AppendDigit(dst, 0)
+ }
+ i = 0
+ for ; i < len(fracDigits); i++ {
+ dst = f.AppendDigit(dst, fracDigits[i])
+ }
+ for ; trailZero > 0; trailZero-- {
+ dst = f.AppendDigit(dst, 0)
+ }
+ // Ensure that at least one digit is written no matter what. This makes
+ // things more robust, even though a pattern should always require at least
+ // one fraction or integer digit.
+ if len(dst) == savedLen {
+ dst = f.AppendDigit(dst, 0)
+ }
+ return appendAffix(dst, f, suffix, neg)
+}
+
+func appendScientific(dst []byte, f *Formatter, d *Decimal) []byte {
+ if dst, ok := f.renderSpecial(dst, d); ok {
+ return dst
+ }
+ // Significant digits are transformed by parser for scientific notation and
+ // do not need to be handled here.
+ maxInt, numInt := int(f.MaxIntegerDigits), int(f.MinIntegerDigits)
+ if numInt == 0 {
+ numInt = 1
+ }
+ maxSig := int(f.MaxFractionDigits) + numInt
+ minSig := int(f.MinFractionDigits) + numInt
+ n := d.normalize()
+ if maxSig > 0 {
+ n.round(ToZero, maxSig)
+ }
+ digits := n.Digits
+ exp := n.Exp
+
+ // If a maximum number of integers is specified, the minimum must be 1
+ // and the exponent is grouped by this number (e.g. for engineering)
+ if len(digits) == 0 {
+ exp = 0
+ } else if maxInt > numInt {
+ // Correct the exponent to reflect a single integer digit.
+ exp--
+ numInt = 1
+ // engineering
+ // 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3
+ // 12345 ([12345]e+5) -> 1.2345e4 12.345e3
+ d := int(exp) % maxInt
+ if d < 0 {
+ d += maxInt
+ }
+ exp -= int32(d)
+ numInt += d
+ } else {
+ exp -= int32(numInt)
+ }
+ var intDigits, fracDigits []byte
+ if numInt <= len(digits) {
+ intDigits = digits[:numInt]
+ fracDigits = digits[numInt:]
+ } else {
+ intDigits = digits
+ }
+ neg := d.Neg && len(digits) > 0
+ affix, suffix := f.getAffixes(neg)
+ dst = appendAffix(dst, f, affix, neg)
+ savedLen := len(dst)
+
+ i := 0
+ for ; i < len(intDigits); i++ {
+ dst = f.AppendDigit(dst, intDigits[i])
+ if f.needsSep(numInt - i) {
+ dst = append(dst, f.Symbol(SymGroup)...)
+ }
+ }
+ for ; i < numInt; i++ {
+ dst = f.AppendDigit(dst, 0)
+ if f.needsSep(numInt - i) {
+ dst = append(dst, f.Symbol(SymGroup)...)
+ }
+ }
+
+ trailZero := minSig - numInt - len(fracDigits)
+ if len(fracDigits) > 0 || trailZero > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
+ dst = append(dst, f.Symbol(SymDecimal)...)
+ }
+ i = 0
+ for ; i < len(fracDigits); i++ {
+ dst = f.AppendDigit(dst, fracDigits[i])
+ }
+ for ; trailZero > 0; trailZero-- {
+ dst = f.AppendDigit(dst, 0)
+ }
+ // Ensure that at least one digit is written no matter what. This makes
+ // things more robust, even though a pattern should always require at least
+ // one fraction or integer digit.
+ if len(dst) == savedLen {
+ dst = f.AppendDigit(dst, 0)
+ }
+
+ // exp
+ dst = append(dst, f.Symbol(SymExponential)...)
+ switch {
+ case exp < 0:
+ dst = append(dst, f.Symbol(SymMinusSign)...)
+ exp = -exp
+ case f.Flags&AlwaysExpSign != 0:
+ dst = append(dst, f.Symbol(SymPlusSign)...)
+ }
+ buf := [12]byte{}
+ b := strconv.AppendUint(buf[:0], uint64(exp), 10)
+ for i := len(b); i < int(f.MinExponentDigits); i++ {
+ dst = f.AppendDigit(dst, 0)
+ }
+ for _, c := range b {
+ dst = f.AppendDigit(dst, c-'0')
+ }
+ return appendAffix(dst, f, suffix, neg)
+}
+
+func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
+ str := f.Affix
+ if str != "" {
+ if f.NegOffset > 0 {
+ if neg {
+ str = str[f.NegOffset:]
+ } else {
+ str = str[:f.NegOffset]
+ }
+ }
+ sufStart := 1 + str[0]
+ affix = str[1:sufStart]
+ suffix = str[sufStart+1:]
+ } else if neg {
+ affix = "-"
+ }
+ return affix, suffix
+}
+
+func (f *Formatter) renderSpecial(dst []byte, d *Decimal) (b []byte, ok bool) {
+ if d.NaN {
+ return fmtNaN(dst, f), true
+ }
+ if d.Inf {
+ return fmtInfinite(dst, f, d), true
+ }
+ return dst, false
+}
+
+func fmtNaN(dst []byte, f *Formatter) []byte {
+ return append(dst, f.Symbol(SymNan)...)
+}
+
+func fmtInfinite(dst []byte, f *Formatter, d *Decimal) []byte {
+ if d.Neg {
+ dst = append(dst, f.Symbol(SymMinusSign)...)
+ }
+ return append(dst, f.Symbol(SymInfinity)...)
+}
+
+func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
+ quoting := false
+ escaping := false
+ for _, r := range affix {
+ switch {
+ case escaping:
+ // escaping occurs both inside and outside of quotes
+ dst = append(dst, string(r)...)
+ escaping = false
+ case r == '\\':
+ escaping = true
+ case r == '\'':
+ quoting = !quoting
+ case !quoting && (r == '-' || r == '+'):
+ if neg {
+ dst = append(dst, f.Symbol(SymMinusSign)...)
+ } else {
+ dst = append(dst, f.Symbol(SymPlusSign)...)
+ }
+ default:
+ dst = append(dst, string(r)...)
+ }
+ }
+ return dst
+}
diff --git a/vendor/golang.org/x/text/internal/number/format_test.go b/vendor/golang.org/x/text/internal/number/format_test.go
new file mode 100755
index 000000000..355a33a70
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/format_test.go
@@ -0,0 +1,363 @@
+// Copyright 2017 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 number
+
+import (
+ "fmt"
+ "log"
+ "strings"
+ "testing"
+
+ "golang.org/x/text/language"
+)
+
+func TestAppendDecimal(t *testing.T) {
+ type pairs map[string]string // alternates with decimal input and result
+
+ testCases := []struct {
+ pattern string
+ // We want to be able to test some forms of patterns that cannot be
+ // represented as a string.
+ pat *Pattern
+
+ test pairs
+ }{{
+ pattern: "0",
+ test: pairs{
+ "0": "0",
+ "1": "1",
+ "-1": "-1",
+ ".00": "0",
+ "10.": "10",
+ "12": "12",
+ "1.2": "1",
+ "NaN": "NaN",
+ "-Inf": "-∞",
+ },
+ }, {
+ pattern: "+0",
+ test: pairs{
+ "0": "+0",
+ "1": "+1",
+ "-1": "-1",
+ ".00": "+0",
+ "10.": "+10",
+ "12": "+12",
+ "1.2": "+1",
+ "NaN": "NaN",
+ "-Inf": "-∞",
+ },
+ }, {
+ pattern: "0 +",
+ test: pairs{
+ "0": "0 +",
+ "1": "1 +",
+ "-1": "1 -",
+ ".00": "0 +",
+ },
+ }, {
+ pattern: "0;0-",
+ test: pairs{
+ "-1": "1-",
+ },
+ }, {
+ pattern: "0000",
+ test: pairs{
+ "0": "0000",
+ "1": "0001",
+ "12": "0012",
+ "12345": "12345",
+ },
+ }, {
+ pattern: ".0",
+ test: pairs{
+ "0": ".0",
+ "1": "1.0",
+ "1.2": "1.2",
+ "1.2345": "1.2",
+ },
+ }, {
+ pattern: "#.0",
+ test: pairs{
+ "0": ".0",
+ },
+ }, {
+ pattern: "#.0#",
+ test: pairs{
+ "0": ".0",
+ "1": "1.0",
+ },
+ }, {
+ pattern: "0.0#",
+ test: pairs{
+ "0": "0.0",
+ },
+ }, {
+ pattern: "#0.###",
+ test: pairs{
+ "0": "0",
+ "1": "1",
+ "1.2": "1.2",
+ "1.2345": "1.234", // rounding should have been done earlier
+ "1234.5": "1234.5",
+ "1234.567": "1234.567",
+ },
+ }, {
+ pattern: "#0.######",
+ test: pairs{
+ "0": "0",
+ "1234.5678": "1234.5678",
+ "0.123456789": "0.123456",
+ "NaN": "NaN",
+ "Inf": "∞",
+ },
+
+ // Test separators.
+ }, {
+ pattern: "#,#.00",
+ test: pairs{
+ "100": "1,0,0.00",
+ },
+ }, {
+ pattern: "#,0.##",
+ test: pairs{
+ "10": "1,0",
+ },
+ }, {
+ pattern: "#,0",
+ test: pairs{
+ "10": "1,0",
+ },
+ }, {
+ pattern: "#,##,#.00",
+ test: pairs{
+ "1000": "1,00,0.00",
+ },
+ }, {
+ pattern: "#,##0.###",
+ test: pairs{
+ "0": "0",
+ "1234.5678": "1,234.567",
+ "0.123456789": "0.123",
+ },
+ }, {
+ pattern: "#,##,##0.###",
+ test: pairs{
+ "0": "0",
+ "123456789012": "1,23,45,67,89,012",
+ "0.123456789": "0.123",
+ },
+
+ // Support for ill-formed patterns.
+ }, {
+ pattern: "#",
+ test: pairs{
+ ".00": "0",
+ "0": "0",
+ "1": "1",
+ "10.": "10",
+ },
+ }, {
+ pattern: ".#",
+ test: pairs{
+ "0": "0",
+ "1": "1",
+ "1.2": "1.2",
+ "1.2345": "1.2",
+ },
+ }, {
+ pattern: "#,#.##",
+ test: pairs{
+ "10": "1,0",
+ },
+ }, {
+ pattern: "#,#",
+ test: pairs{
+ "10": "1,0",
+ },
+
+ // Special patterns
+ }, {
+ pattern: "#,max_int=2",
+ pat: &Pattern{
+ MaxIntegerDigits: 2,
+ },
+ test: pairs{
+ "2017": "17",
+ },
+ }, {
+ pattern: "0,max_int=2",
+ pat: &Pattern{
+ MaxIntegerDigits: 2,
+ MinIntegerDigits: 1,
+ },
+ test: pairs{
+ "2000": "0",
+ "2001": "1",
+ "2017": "17",
+ },
+ }, {
+ pattern: "00,max_int=2",
+ pat: &Pattern{
+ MaxIntegerDigits: 2,
+ MinIntegerDigits: 2,
+ },
+ test: pairs{
+ "2000": "00",
+ "2001": "01",
+ "2017": "17",
+ },
+ }, {
+ pattern: "@@@@,max_int=2",
+ pat: &Pattern{
+ MaxIntegerDigits: 2,
+ MinSignificantDigits: 4,
+ },
+ test: pairs{
+ "2017": "17.00",
+ "2000": "0.000",
+ "2001": "1.000",
+ },
+
+ // Significant digits
+ }, {
+ pattern: "@@##",
+ test: pairs{
+ "1": "1.0",
+ "0.1": "0.10",
+ "123": "123",
+ "1234": "1234",
+ "12345": "12340",
+ },
+ }, {
+ pattern: "@@@@",
+ test: pairs{
+ "1": "1.000",
+ ".1": "0.1000",
+ ".001": "0.001000",
+ "123": "123.0",
+ "1234": "1234",
+ "12345": "12340", // rounding down
+ "NaN": "NaN",
+ "-Inf": "-∞",
+ },
+
+ // TODO: rounding
+ // {"@@@@": "23456": "23460"}, // rounding up
+ // TODO: padding
+
+ // Scientific and Engineering notation
+ }, {
+ pattern: "#E0",
+ test: pairs{
+ "0": "0E0",
+ "1": "1E0",
+ "123.456": "1E2",
+ },
+ }, {
+ pattern: "#E+0",
+ test: pairs{
+ "0": "0E+0",
+ "1000": "1E+3",
+ "1E100": "1E+100",
+ "1E-100": "1E-100",
+ "NaN": "NaN",
+ "-Inf": "-∞",
+ },
+ }, {
+ pattern: "##0E00",
+ test: pairs{
+ "100": "100E00",
+ "12345": "10E03",
+ "123.456": "100E00",
+ },
+ }, {
+ pattern: "##0.###E00",
+ test: pairs{
+ "100": "100E00",
+ "12345": "12.34E03",
+ "123.456": "123.4E00",
+ },
+ }, {
+ pattern: "##0.000E00",
+ test: pairs{
+ "100": "100.0E00",
+ "12345": "12.34E03",
+ "123.456": "123.4E00",
+ },
+ }}
+
+ // TODO:
+ // "@@E0",
+ // "@###E00",
+ // "0.0%",
+ // "0.0‰",
+ // "#,##0.00¤",
+ // "#,##0.00 ¤;(#,##0.00 ¤)",
+ // // padding
+ // "*x#",
+ // "#*x",
+ // "*xpre#suf",
+ // "pre*x#suf",
+ // "pre#*xsuf",
+ // "pre#suf*x",
+ for _, tc := range testCases {
+ pat := tc.pat
+ if pat == nil {
+ var err error
+ if pat, err = ParsePattern(tc.pattern); err != nil {
+ log.Fatal(err)
+ }
+ }
+ f := &Formatter{
+ pat,
+ InfoFromTag(language.English),
+ RoundingContext{},
+ appendDecimal,
+ }
+ if strings.IndexByte(tc.pattern, 'E') != -1 {
+ f.f = appendScientific
+ }
+ for dec, want := range tc.test {
+ buf := make([]byte, 100)
+ t.Run(tc.pattern+"/"+dec, func(t *testing.T) {
+ dec := mkdec(dec)
+ buf = f.Format(buf[:0], &dec)
+ if got := string(buf); got != want {
+ t.Errorf("\n got %q\nwant %q", got, want)
+ }
+ })
+ }
+ }
+}
+
+func TestLocales(t *testing.T) {
+ testCases := []struct {
+ tag language.Tag
+ num string
+ want string
+ }{
+ {language.Make("en"), "123456.78", "123,456.78"},
+ {language.Make("de"), "123456.78", "123.456,78"},
+ {language.Make("de-CH"), "123456.78", "123'456.78"},
+ {language.Make("fr"), "123456.78", "123 456,78"},
+ {language.Make("bn"), "123456.78", "১,২৩,৪৫৬.৭৮"},
+ }
+ for _, tc := range testCases {
+ t.Run(fmt.Sprint(tc.tag, "/", tc.num), func(t *testing.T) {
+ f := &Formatter{
+ lookupFormat(tc.tag, tagToDecimal),
+ InfoFromTag(tc.tag),
+ RoundingContext{},
+ appendDecimal,
+ }
+ d := mkdec(tc.num)
+ b := f.Format(nil, &d)
+ if got := string(b); got != tc.want {
+ t.Errorf("got %q; want %q", got, tc.want)
+ }
+ })
+ }
+}
diff --git a/vendor/golang.org/x/text/internal/number/number.go b/vendor/golang.org/x/text/internal/number/number.go
index 34f4477d0..ea341c796 100644
--- a/vendor/golang.org/x/text/internal/number/number.go
+++ b/vendor/golang.org/x/text/internal/number/number.go
@@ -121,6 +121,15 @@ func (n Info) WriteDigit(dst []byte, asciiDigit rune) int {
return int(n.system.digitSize)
}
+// AppendDigit appends the UTF-8 sequence for n corresponding to the given digit
+// to dst and reports the number of bytes written. dst must be large enough to
+// hold the rune (can be up to utf8.UTFMax bytes).
+func (n Info) AppendDigit(dst []byte, digit byte) []byte {
+ dst = append(dst, n.system.zero[:n.system.digitSize]...)
+ dst[len(dst)-1] += digit
+ return dst
+}
+
// Digit returns the digit for the numbering system for the corresponding ASCII
// value. For example, ni.Digit('3') could return '三'. Note that the argument
// is the rune constant '3', which equals 51, not the integer constant 3.
diff --git a/vendor/golang.org/x/text/internal/number/number_test.go b/vendor/golang.org/x/text/internal/number/number_test.go
index fc04887c4..3eb533914 100644
--- a/vendor/golang.org/x/text/internal/number/number_test.go
+++ b/vendor/golang.org/x/text/internal/number/number_test.go
@@ -5,6 +5,7 @@
package number
import (
+ "fmt"
"testing"
"golang.org/x/text/internal/testtext"
@@ -51,13 +52,22 @@ func TestInfo(t *testing.T) {
{"en-u-nu-roman", SymPlusSign, "+", '9'},
}
for _, tc := range testCases {
- info := InfoFromTag(language.MustParse(tc.lang))
- if got := info.Symbol(tc.sym); got != tc.wantSym {
- t.Errorf("%s:%v:sym: got %q; want %q", tc.lang, tc.sym, got, tc.wantSym)
- }
- if got := info.Digit('9'); got != tc.wantNine {
- t.Errorf("%s:%v:nine: got %q; want %q", tc.lang, tc.sym, got, tc.wantNine)
- }
+ t.Run(fmt.Sprintf("%s:%v", tc.lang, tc.sym), func(t *testing.T) {
+ info := InfoFromTag(language.MustParse(tc.lang))
+ if got := info.Symbol(tc.sym); got != tc.wantSym {
+ t.Errorf("sym: got %q; want %q", got, tc.wantSym)
+ }
+ if got := info.Digit('9'); got != tc.wantNine {
+ t.Errorf("Digit(9): got %+q; want %+q", got, tc.wantNine)
+ }
+ var buf [4]byte
+ if got := string(buf[:info.WriteDigit(buf[:], '9')]); got != string(tc.wantNine) {
+ t.Errorf("WriteDigit(9): got %+q; want %+q", got, tc.wantNine)
+ }
+ if got := string(info.AppendDigit([]byte{}, 9)); got != string(tc.wantNine) {
+ t.Errorf("AppendDigit(9): got %+q; want %+q", got, tc.wantNine)
+ }
+ })
}
}
diff --git a/vendor/golang.org/x/text/internal/number/pattern.go b/vendor/golang.org/x/text/internal/number/pattern.go
index 018cf02d5..5610c6026 100644
--- a/vendor/golang.org/x/text/internal/number/pattern.go
+++ b/vendor/golang.org/x/text/internal/number/pattern.go
@@ -65,7 +65,27 @@ type Pattern struct {
MinExponentDigits uint8
}
-// A PatternFlag is a bit mask for the flag field of a Format.
+func (f *Pattern) needsSep(pos int) bool {
+ p := pos - 1
+ size := int(f.GroupingSize[0])
+ if size == 0 || p == 0 {
+ return false
+ }
+ if p == size {
+ return true
+ }
+ if p -= size; p < 0 {
+ return false
+ }
+ // TODO: make second groupingsize the same as first if 0 so that we can
+ // avoid this check.
+ if x := int(f.GroupingSize[1]); x != 0 {
+ size = x
+ }
+ return p%size == 0
+}
+
+// A PatternFlag is a bit mask for the flag field of a Pattern.
type PatternFlag uint8
const (
@@ -104,7 +124,8 @@ func (p *parser) setError(err error) {
}
func (p *parser) updateGrouping() {
- if p.hasGroup && p.groupingCount < 255 {
+ if p.hasGroup &&
+ 0 < p.groupingCount && p.groupingCount < 255 {
p.GroupingSize[1] = p.GroupingSize[0]
p.GroupingSize[0] = uint8(p.groupingCount)
}
@@ -163,6 +184,7 @@ func (p *parser) parseSubPattern(s string) string {
s = p.parsePad(s, PadAfterPrefix)
s = p.parse(p.number, s)
+ p.updateGrouping()
s = p.parsePad(s, PadBeforeSuffix)
s = p.parseAffix(s)
@@ -294,6 +316,8 @@ func (p *parser) integer(r rune) state {
next = p.exponent
case '.':
next = p.fraction
+ case ',':
+ next = p.integer
}
p.updateGrouping()
return next
diff --git a/vendor/golang.org/x/text/internal/number/pattern_test.go b/vendor/golang.org/x/text/internal/number/pattern_test.go
index 810b5a855..6adeaf7cc 100644
--- a/vendor/golang.org/x/text/internal/number/pattern_test.go
+++ b/vendor/golang.org/x/text/internal/number/pattern_test.go
@@ -26,6 +26,20 @@ var testCases = []struct {
MinIntegerDigits: 1,
},
}, {
+ "+0",
+ &Pattern{
+ Affix: "\x01+\x00",
+ FormatWidth: 2,
+ MinIntegerDigits: 1,
+ },
+}, {
+ "0+",
+ &Pattern{
+ Affix: "\x00\x01+",
+ FormatWidth: 2,
+ MinIntegerDigits: 1,
+ },
+}, {
"0000",
&Pattern{
FormatWidth: 4,
@@ -52,6 +66,22 @@ var testCases = []struct {
MaxFractionDigits: 6,
},
}, {
+ "#,0",
+ &Pattern{
+ FormatWidth: 3,
+ GroupingSize: [2]uint8{1, 0},
+ MinIntegerDigits: 1,
+ },
+}, {
+ "#,0.00",
+ &Pattern{
+ FormatWidth: 6,
+ GroupingSize: [2]uint8{1, 0},
+ MinIntegerDigits: 1,
+ MinFractionDigits: 2,
+ MaxFractionDigits: 2,
+ },
+}, {
"#,##0.###",
&Pattern{
FormatWidth: 9,
@@ -176,7 +206,7 @@ var testCases = []struct {
MaxFractionDigits: 3,
},
}, {
- // Rounding increments
+ // Rounding increment
"1.05",
&Pattern{
RoundIncrement: 105,
@@ -186,6 +216,17 @@ var testCases = []struct {
MaxFractionDigits: 2,
},
}, {
+ // Rounding increment with grouping
+ "1,05",
+ &Pattern{
+ RoundIncrement: 105,
+ FormatWidth: 4,
+ GroupingSize: [2]uint8{2, 0},
+ MinIntegerDigits: 3,
+ MinFractionDigits: 0,
+ MaxFractionDigits: 0,
+ },
+}, {
"0.0%",
&Pattern{
Affix: "\x00\x01%",
@@ -282,19 +323,21 @@ var testCases = []struct {
func TestParsePattern(t *testing.T) {
for i, tc := range testCases {
- f, err := ParsePattern(tc.pat)
- if !reflect.DeepEqual(f, tc.want) {
- t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want)
- }
- if got, want := err != nil, tc.want == nil; got != want {
- t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want)
- }
+ t.Run(tc.pat, func(t *testing.T) {
+ f, err := ParsePattern(tc.pat)
+ if !reflect.DeepEqual(f, tc.want) {
+ t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want)
+ }
+ if got, want := err != nil, tc.want == nil; got != want {
+ t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want)
+ }
+ })
}
}
func TestPatternSize(t *testing.T) {
if sz := unsafe.Sizeof(Pattern{}); sz > 48 {
- t.Errorf("got %d; want 48", sz)
+ t.Errorf("got %d; want <= 48", sz)
}
}
diff --git a/vendor/golang.org/x/text/internal/number/roundingmode_string.go b/vendor/golang.org/x/text/internal/number/roundingmode_string.go
new file mode 100644
index 000000000..f264ea549
--- /dev/null
+++ b/vendor/golang.org/x/text/internal/number/roundingmode_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type RoundingMode"; DO NOT EDIT.
+
+package number
+
+import "fmt"
+
+const _RoundingMode_name = "ToNearestEvenToNearestZeroToNearestAwayToPositiveInfToNegativeInfToZeroAwayFromZeronumModes"
+
+var _RoundingMode_index = [...]uint8{0, 13, 26, 39, 52, 65, 71, 83, 91}
+
+func (i RoundingMode) String() string {
+ if i >= RoundingMode(len(_RoundingMode_index)-1) {
+ return fmt.Sprintf("RoundingMode(%d)", i)
+ }
+ return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]
+}
diff --git a/vendor/golang.org/x/text/internal/number/tables.go b/vendor/golang.org/x/text/internal/number/tables.go
index 5c799b433..10baa0a1d 100644
--- a/vendor/golang.org/x/text/internal/number/tables.go
+++ b/vendor/golang.org/x/text/internal/number/tables.go
@@ -1037,7 +1037,7 @@ var formats = []Pattern{Pattern{Affix: "",
RoundIncrement: 0x0,
PadRune: 0,
FormatWidth: 0x7,
- GroupingSize: [2]uint8{0x0,
+ GroupingSize: [2]uint8{0x3,
0x0},
Flags: 0x0,
MinIntegerDigits: 0x1,
@@ -1105,7 +1105,7 @@ var formats = []Pattern{Pattern{Affix: "",
RoundIncrement: 0x0,
PadRune: 0,
FormatWidth: 0x6,
- GroupingSize: [2]uint8{0x0,
+ GroupingSize: [2]uint8{0x3,
0x0},
Flags: 0x0,
MinIntegerDigits: 0x1,