number: added number package

provides more styles and more control over
formatting than fmt.

Change-Id: Ib9d2c27db14804af911556e3caa8b35fddd32fce
Reviewed-on: https://go-review.googlesource.com/59812
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/number/doc.go b/number/doc.go
new file mode 100644
index 0000000..97088d1
--- /dev/null
+++ b/number/doc.go
@@ -0,0 +1,29 @@
+// 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 formats numbers according to the customs of different locales.
+//
+// The number formats of this package allow for greater formatting flexibility
+// than passing values to message.Printf calls as is. It currently supports the
+// builtin Go types and anything that implements the Convert interface
+// (currently internal).
+//
+//    p := message.NewPrinter(language.English)
+//
+//    p.Printf("%v bottles of beer on the wall.", number.Decimal(1234))
+//    // Prints: 1,234 bottles of beer on the wall.
+//
+//    p.Printf("%v of gophers lose too much fur", number.Percent(0.12))
+//    // Prints: 12% of gophers lose too much fur.
+//
+//    p := message.NewPrinter(language.Dutch)
+//
+//    p.Printf("There are %v bikes per household.", number.Decimal(1.2))
+//    // Prints: Er zijn 1,2 fietsen per huishouden.
+//
+// Provided that the printed translation is available.
+//
+// The width and scale specified in the formatting directives override the
+// configuration of the formatter.
+package number
diff --git a/number/examples_test.go b/number/examples_test.go
new file mode 100644
index 0000000..fb9bcc9
--- /dev/null
+++ b/number/examples_test.go
@@ -0,0 +1,28 @@
+// 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_test
+
+import (
+	"golang.org/x/text/language"
+	"golang.org/x/text/message"
+	"golang.org/x/text/number"
+)
+
+func ExampleMaxIntegerDigits() {
+	const year = 1999
+	p := message.NewPrinter(language.English)
+	p.Println("Year:", number.Decimal(year, number.MaxIntegerDigits(2)))
+
+	// Output:
+	// Year: 99
+}
+
+func ExampleIncrementString() {
+	p := message.NewPrinter(language.English)
+
+	p.Println(number.Decimal(1.33, number.IncrementString("0.50")))
+
+	// Output: 1.50
+}
diff --git a/number/format.go b/number/format.go
new file mode 100755
index 0000000..62af651
--- /dev/null
+++ b/number/format.go
@@ -0,0 +1,106 @@
+// 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"
+	"strings"
+
+	"golang.org/x/text/feature/plural"
+	"golang.org/x/text/internal/format"
+	"golang.org/x/text/internal/number"
+	"golang.org/x/text/language"
+)
+
+// A FormatFunc formates a number.
+type FormatFunc func(x interface{}, opts ...Option) Formatter
+
+// NewFormat creates a FormatFunc based on another FormatFunc and new options.
+// Use NewFormat to cash the creation of formatters.
+func NewFormat(format FormatFunc, opts ...Option) FormatFunc {
+	o := *format(nil).options
+	n := len(o.options)
+	o.options = append(o.options[:n:n], opts...)
+	return func(x interface{}, opts ...Option) Formatter {
+		return newFormatter(&o, opts, x)
+	}
+}
+
+type options struct {
+	verbs      string
+	initFunc   initFunc
+	options    []Option
+	pluralFunc func(t language.Tag, scale int) (f plural.Form, n int)
+}
+
+type optionFlag uint16
+
+const (
+	hasScale optionFlag = 1 << iota
+	hasPrecision
+	noSeparator
+	exact
+)
+
+type initFunc func(f *number.Formatter, t language.Tag)
+
+func newFormatter(o *options, opts []Option, value interface{}) Formatter {
+	if len(opts) > 0 {
+		n := *o
+		n.options = opts
+		o = &n
+	}
+	return Formatter{o, value}
+}
+
+func newOptions(verbs string, f initFunc) *options {
+	return &options{verbs: verbs, initFunc: f}
+}
+
+type Formatter struct {
+	*options
+	value interface{}
+}
+
+// Format implements format.Formatter. It is for internal use only for now.
+func (f Formatter) Format(state format.State, verb rune) {
+	// TODO: consider implementing fmt.Formatter instead and using the following
+	// piece of code. This allows numbers to be rendered mostly as expected
+	// when using fmt. But it may get weird with the spellout options and we
+	// may need more of format.State over time.
+	// lang := language.Und
+	// if s, ok := state.(format.State); ok {
+	// 	lang = s.Language()
+	// }
+
+	lang := state.Language()
+	if !strings.Contains(f.verbs, string(verb)) {
+		fmt.Fprintf(state, "%%!%s(%T=%v)", string(verb), f.value, f.value)
+		return
+	}
+	var p number.Formatter
+	f.initFunc(&p, lang)
+	for _, o := range f.options.options {
+		o(lang, &p)
+	}
+	if w, ok := state.Width(); ok {
+		p.FormatWidth = uint16(w)
+	}
+	if prec, ok := state.Precision(); ok {
+		switch verb {
+		case 'd':
+			p.SetScale(0)
+		case 'f':
+			p.SetScale(prec)
+		case 'e':
+			p.SetPrecision(prec + 1)
+		case 'g':
+			p.SetPrecision(prec)
+		}
+	}
+	var d number.Decimal
+	d.Convert(p.RoundingContext, f.value)
+	state.Write(p.Format(nil, &d))
+}
diff --git a/number/format_test.go b/number/format_test.go
new file mode 100644
index 0000000..30be72d
--- /dev/null
+++ b/number/format_test.go
@@ -0,0 +1,45 @@
+// 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 (
+	"testing"
+
+	"golang.org/x/text/language"
+	"golang.org/x/text/message"
+)
+
+func TestWrongVerb(t *testing.T) {
+	testCases := []struct {
+		f    Formatter
+		fmt  string
+		want string
+	}{{
+		f:    Decimal(12),
+		fmt:  "%e",
+		want: "%!e(int=12)",
+	}, {
+		f:    Scientific(12),
+		fmt:  "%f",
+		want: "%!f(int=12)",
+	}, {
+		f:    Engineering(12),
+		fmt:  "%f",
+		want: "%!f(int=12)",
+	}, {
+		f:    Percent(12),
+		fmt:  "%e",
+		want: "%!e(int=12)",
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			tag := language.Und
+			got := message.NewPrinter(tag).Sprintf(tc.fmt, tc.f)
+			if got != tc.want {
+				t.Errorf("got %q; want %q", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/number/number.go b/number/number.go
new file mode 100755
index 0000000..0a6e62f
--- /dev/null
+++ b/number/number.go
@@ -0,0 +1,77 @@
+// 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
+
+// TODO:
+//    p.Printf("The gauge was at %v.", number.Spell(number.Percent(23)))
+//    // Prints: The gauge was at twenty-three percent.
+//
+//    p.Printf("From here to %v!", number.Spell(math.Inf()))
+//    // Prints: From here to infinity!
+//
+
+import (
+	"golang.org/x/text/internal/number"
+)
+
+const (
+	decimalVerbs    = "vfgd"
+	scientificVerbs = "veg"
+)
+
+// Decimal represents a number as a floating point decimal.
+func Decimal(x interface{}, opts ...Option) Formatter {
+	return newFormatter(decimalOptions, opts, x)
+}
+
+var decimalOptions = newOptions(decimalVerbs, (*number.Formatter).InitDecimal)
+
+// Scientific prints a values in scientific format.
+func Scientific(x interface{}, opts ...Option) Formatter {
+	return newFormatter(scientificOptions, opts, x)
+}
+
+var scientificOptions = newOptions(scientificVerbs, (*number.Formatter).InitScientific)
+
+// Engineering formats a number using engineering notation, which is like
+// scientific notation, but with the exponent normalized to multiples of 3.
+func Engineering(x interface{}, opts ...Option) Formatter {
+	return newFormatter(engineeringOptions, opts, x)
+}
+
+var engineeringOptions = newOptions(scientificVerbs, (*number.Formatter).InitEngineering)
+
+// Percent formats a number as a percentage. A value of 1.0 means 100%.
+func Percent(x interface{}, opts ...Option) Formatter {
+	return newFormatter(percentOptions, opts, x)
+}
+
+var percentOptions = newOptions(decimalVerbs, (*number.Formatter).InitPercent)
+
+// PerMille formats a number as a per mille indication. A value of 1.0 means
+// 1000‰.
+func PerMille(x interface{}, opts ...Option) Formatter {
+	return newFormatter(perMilleOptions, opts, x)
+}
+
+var perMilleOptions = newOptions(decimalVerbs, (*number.Formatter).InitPerMille)
+
+// TODO:
+// - Shortest: akin to verb 'g' of 'G'
+//
+// TODO: RBNF forms:
+// - Compact: 1M 3.5T
+// - CompactBinary: 1Mi 3.5Ti
+// - Long: 1 million
+// - Ordinal:
+// - Roman: MCMIIXX
+// - RomanSmall: mcmiixx
+// - Text: numbers as it typically appears in running text, allowing
+//   language-specific choices for when to use numbers and when to use words.
+// - Spell?: spelled-out number. Maybe just allow as an option?
+
+// NOTE: both spelled-out numbers and ordinals, to render correctly, need
+// detailed linguistic information from the translated string into which they
+// are substituted. We will need to implement that first.
diff --git a/number/number_test.go b/number/number_test.go
new file mode 100644
index 0000000..f380b7e
--- /dev/null
+++ b/number/number_test.go
@@ -0,0 +1,190 @@
+// 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 (
+	"strings"
+	"testing"
+
+	"golang.org/x/text/language"
+	"golang.org/x/text/message"
+)
+
+func TestFormatter(t *testing.T) {
+	overrides := map[string]string{
+		"en": "*e#######0",
+		"nl": "*n#######0",
+	}
+	testCases := []struct {
+		desc string
+		tag  string
+		f    Formatter
+		want string
+	}{{
+		desc: "decimal",
+		f:    Decimal(3),
+		want: "3",
+	}, {
+		desc: "decimal fraction",
+		f:    Decimal(0.123),
+		want: "0.123",
+	}, {
+		desc: "separators",
+		f:    Decimal(1234.567),
+		want: "1,234.567",
+	}, {
+		desc: "no separators",
+		f:    Decimal(1234.567, NoSeparator()),
+		want: "1234.567",
+	}, {
+		desc: "max integer",
+		f:    Decimal(1973, MaxIntegerDigits(2)),
+		want: "73",
+	}, {
+		desc: "max integer overflow",
+		f:    Decimal(1973, MaxIntegerDigits(1000)),
+		want: "1,973",
+	}, {
+		desc: "min integer",
+		f:    Decimal(12, MinIntegerDigits(5)),
+		want: "00,012",
+	}, {
+		desc: "max fraction zero",
+		f:    Decimal(0.12345, MaxFractionDigits(0)),
+		want: "0",
+	}, {
+		desc: "max fraction 2",
+		f:    Decimal(0.12, MaxFractionDigits(2)),
+		want: "0.12",
+	}, {
+		desc: "min fraction 2",
+		f:    Decimal(0.12, MaxFractionDigits(2)),
+		want: "0.12",
+	}, {
+		desc: "max fraction overflow",
+		f:    Decimal(0.123, MaxFractionDigits(1e6)),
+		want: "0.123",
+	}, {
+		desc: "min integer overflow",
+		f:    Decimal(0, MinIntegerDigits(1e6)),
+		want: strings.Repeat("000,", 255/3-1) + "000",
+	}, {
+		desc: "min fraction overflow",
+		f:    Decimal(0, MinFractionDigits(1e6)),
+		want: "0." + strings.Repeat("0", 255), // TODO: fraction separators
+	}, {
+		desc: "format width",
+		f:    Decimal(123, FormatWidth(10)),
+		want: "       123",
+	}, {
+		desc: "format width pad option before",
+		f:    Decimal(123, PadRune('*'), FormatWidth(10)),
+		want: "*******123",
+	}, {
+		desc: "format width pad option after",
+		f:    Decimal(123, FormatWidth(10), PadRune('*')),
+		want: "*******123",
+	}, {
+		desc: "format width illegal",
+		f:    Decimal(123, FormatWidth(-1)),
+		want: "123",
+	}, {
+		desc: "increment",
+		f:    Decimal(10.33, IncrementString("0.5")),
+		want: "10.5",
+	}, {
+		desc: "increment",
+		f:    Decimal(10, IncrementString("ppp")),
+		want: "10",
+	}, {
+		desc: "increment and scale",
+		f:    Decimal(10.33, IncrementString("0.5"), Scale(2)),
+		want: "10.50",
+	}, {
+		desc: "pattern overrides en",
+		tag:  "en",
+		f:    Decimal(101, PatternOverrides(overrides)),
+		want: "eeeee101",
+	}, {
+		desc: "pattern overrides nl",
+		tag:  "nl",
+		f:    Decimal(101, PatternOverrides(overrides)),
+		want: "nnnnn101",
+	}, {
+		desc: "pattern overrides de",
+		tag:  "de",
+		f:    Decimal(101, PatternOverrides(overrides)),
+		want: "101",
+	}, {
+		desc: "language selection",
+		tag:  "bn",
+		f:    Decimal(123456.78, Scale(2)),
+		want: "১,২৩,৪৫৬.৭৮",
+	}, {
+		desc: "scale",
+		f:    Decimal(1234.567, Scale(2)),
+		want: "1,234.57",
+	}, {
+		desc: "scientific",
+		f:    Scientific(3.00),
+		want: "3\u202f×\u202f10⁰",
+	}, {
+		desc: "scientific",
+		f:    Scientific(1234),
+		want: "1.234\u202f×\u202f10³",
+	}, {
+		desc: "scientific",
+		f:    Scientific(1234, Scale(2)),
+		want: "1.23\u202f×\u202f10³",
+	}, {
+		desc: "engineering",
+		f:    Engineering(12345),
+		want: "12.345\u202f×\u202f10³",
+	}, {
+		desc: "engineering scale",
+		f:    Engineering(12345, Scale(2)),
+		want: "12.34\u202f×\u202f10³",
+	}, {
+		desc: "engineering precision(4)",
+		f:    Engineering(12345, Precision(4)),
+		want: "12.34\u202f×\u202f10³",
+	}, {
+		desc: "engineering precision(2)",
+		f:    Engineering(1234.5, Precision(2)),
+		want: "1.2\u202f×\u202f10³",
+	}, {
+		desc: "percent",
+		f:    Percent(0.12),
+		want: "12%",
+	}, {
+		desc: "permille",
+		f:    PerMille(0.123),
+		want: "123‰",
+	}, {
+		desc: "percent rounding",
+		f:    PerMille(0.12345),
+		want: "123‰",
+	}, {
+		desc: "percent fraction",
+		f:    PerMille(0.12345, Scale(2)),
+		want: "123.45‰",
+	}, {
+		desc: "percent fraction",
+		f:    PerMille(0.12345, Scale(1)),
+		want: "123.4‰",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			tag := language.Und
+			if tc.tag != "" {
+				tag = language.MustParse(tc.tag)
+			}
+			got := message.NewPrinter(tag).Sprint(tc.f)
+			if got != tc.want {
+				t.Errorf("got %q; want %q", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/number/option.go b/number/option.go
new file mode 100644
index 0000000..2e73525
--- /dev/null
+++ b/number/option.go
@@ -0,0 +1,177 @@
+// 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"
+
+	"golang.org/x/text/internal/number"
+	"golang.org/x/text/language"
+)
+
+// An Option configures a Formatter.
+type Option option
+
+type option func(tag language.Tag, f *number.Formatter)
+
+// TODO: SpellOut requires support of the ICU RBNF format.
+// func SpellOut() Option
+
+// NoSeparator causes a number to be displayed without grouping separators.
+func NoSeparator() Option {
+	return func(t language.Tag, f *number.Formatter) {
+		f.GroupingSize = [2]uint8{}
+	}
+}
+
+// MaxIntegerDigits limits the number of integer digits, eliminating the
+// most significant digits.
+func MaxIntegerDigits(max int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		if max >= 1<<8 {
+			max = (1 << 8) - 1
+		}
+		f.MaxIntegerDigits = uint8(max)
+	}
+}
+
+// MinIntegerDigits specifies the minimum number of integer digits, adding
+// leading zeros when needed.
+func MinIntegerDigits(min int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		if min >= 1<<8 {
+			min = (1 << 8) - 1
+		}
+		f.MinIntegerDigits = uint8(min)
+	}
+}
+
+// MaxFractionDigits specifies the maximum number of digits after the comma.
+func MaxFractionDigits(max int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		if max >= 1<<15 {
+			max = (1 << 15) - 1
+		}
+		f.MaxFractionDigits = int16(max)
+	}
+}
+
+// MinFractionDigits specifies the minimum number of digits after the comma.
+func MinFractionDigits(min int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		if min >= 1<<8 {
+			min = (1 << 8) - 1
+		}
+		f.MinFractionDigits = uint8(min)
+	}
+}
+
+// Precision sets the maximum number of significant digits. A negative value
+// means exact.
+func Precision(prec int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		f.SetPrecision(prec)
+	}
+}
+
+// Scale simultaneously sets MinFractionDigits and MaxFractionDigits to the
+// given value.
+func Scale(decimals int) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		f.SetScale(decimals)
+	}
+}
+
+// IncrementString sets the incremental value to which numbers should be
+// rounded. For instance: Increment("0.05") will cause 1.44 to round to 1.45.
+// IncrementString also sets scale to the scale of the increment.
+func IncrementString(decimal string) Option {
+	increment := 0
+	scale := 0
+	d := decimal
+	p := 0
+	for ; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ {
+		increment *= 10
+		increment += int(d[p]) - '0'
+	}
+	if p < len(d) && d[p] == '.' {
+		for p++; p < len(d) && '0' <= d[p] && d[p] <= '9'; p++ {
+			increment *= 10
+			increment += int(d[p]) - '0'
+			scale++
+		}
+	}
+	if p < len(d) {
+		increment = 0
+		scale = 0
+	}
+	return func(t language.Tag, f *number.Formatter) {
+		f.Increment = uint32(increment)
+		f.IncrementScale = uint8(scale)
+		f.SetScale(scale)
+	}
+}
+
+func noop(language.Tag, *number.Formatter) {}
+
+// PatternOverrides allows users to specify alternative patterns for specific
+// languages. The Pattern will be overridden for all languages in a subgroup as
+// well. The function will panic for invalid input. It is best to create this
+// option at startup time.
+// PatternOverrides must be the first Option passed to a formatter.
+func PatternOverrides(patterns map[string]string) Option {
+	// TODO: make it so that it does not have to be the first option.
+	// TODO: use -x-nochild to indicate it does not override child tags.
+	m := map[language.Tag]*number.Pattern{}
+	for k, v := range patterns {
+		tag := language.MustParse(k)
+		p, err := number.ParsePattern(v)
+		if err != nil {
+			panic(fmt.Errorf("number: PatternOverrides: %v", err))
+		}
+		m[tag] = p
+	}
+	return func(t language.Tag, f *number.Formatter) {
+		// TODO: Use language grouping relation instead of parent relation.
+		// TODO: Should parent implement the grouping relation?
+		for lang := t; ; lang = t.Parent() {
+			if p, ok := m[lang]; ok {
+				f.Pattern = *p
+				break
+			}
+			if lang == language.Und {
+				break
+			}
+		}
+	}
+}
+
+// FormatWidth sets the total format width.
+func FormatWidth(n int) Option {
+	if n <= 0 {
+		return noop
+	}
+	return func(t language.Tag, f *number.Formatter) {
+		f.FormatWidth = uint16(n)
+		if f.PadRune == 0 {
+			f.PadRune = ' '
+		}
+	}
+}
+
+// PadRune sets the rune to be used for filling up to the format width.
+func PadRune(r rune) Option {
+	return func(t language.Tag, f *number.Formatter) {
+		f.PadRune = r
+	}
+}
+
+// TODO:
+// - FormatPosition (using type aliasing?)
+// - Multiplier: find a better way to represent and figure out what to do
+//   with clashes with percent/permille.
+// - NumberingSystem(nu string): not accessable in number.Info now. Also, should
+//      this be keyed by language or generic?
+// - SymbolOverrides(symbols map[string]map[number.SymbolType]string) Option