internal/number: add number formatting

Now supports formatting of decimal,
scientific/ engineering and percent.

Change-Id: If94b0684dc1b1bd66106a07828d3b7f695f12e2d
Reviewed-on: https://go-review.googlesource.com/45494
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/internal/number/format.go b/internal/number/format.go
new file mode 100755
index 0000000..84903fa
--- /dev/null
+++ b/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/internal/number/format_test.go b/internal/number/format_test.go
new file mode 100755
index 0000000..355a33a
--- /dev/null
+++ b/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/internal/number/pattern.go b/internal/number/pattern.go
index ad8f729..5610c60 100644
--- a/internal/number/pattern.go
+++ b/internal/number/pattern.go
@@ -65,7 +65,27 @@
 	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 (
diff --git a/internal/number/tables.go b/internal/number/tables.go
index 5c799b4..10baa0a 100644
--- a/internal/number/tables.go
+++ b/internal/number/tables.go
@@ -1037,7 +1037,7 @@
 		RoundIncrement: 0x0,
 		PadRune:        0,
 		FormatWidth:    0x7,
-		GroupingSize: [2]uint8{0x0,
+		GroupingSize: [2]uint8{0x3,
 			0x0},
 		Flags:                0x0,
 		MinIntegerDigits:     0x1,
@@ -1105,7 +1105,7 @@
 		RoundIncrement: 0x0,
 		PadRune:        0,
 		FormatWidth:    0x6,
-		GroupingSize: [2]uint8{0x0,
+		GroupingSize: [2]uint8{0x3,
 			0x0},
 		Flags:                0x0,
 		MinIntegerDigits:     0x1,