internal/number: factor out logic to compute visible digits

This is needed for the plural computation.
Leading zeros are not needed for this computation.

Change-Id: Ib82e9270b5df3e7314d79215bfb7c6c847bd5909
Reviewed-on: https://go-review.googlesource.com/47591
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/internal/number/decimal.go b/internal/number/decimal.go
index ef5df60..199c7e4 100644
--- a/internal/number/decimal.go
+++ b/internal/number/decimal.go
@@ -44,16 +44,30 @@
 //
 // 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
+//    decimal
+//      12345      Digits: [1, 2, 3, 4, 5], Exp: 5  End: 5
+//      12.345     Digits: [1, 2, 3, 4, 5], Exp: 2  End: 5
+//      12000      Digits: [1, 2],          Exp: 5  End: 5
+//      12000.00   Digits: [1, 2],          Exp: 5  End: 7
+//      0.00123    Digits: [1, 2, 3],       Exp: -2 End: 3
+//      0          Digits: [],              Exp: 0  End: 1
+//    scientific:
+//      0          Digits: [],              Exp: 0, End: 1, Comma: 0
+//      1.23e4     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 1
+//    engineering
+//      12.3e3     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 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.
+	// End indicates the end position of the number.
+	End int32 // For decimals Exp <= End. For scientific len(Digits) <= End.
+	// Comma is used for the comma position for scientific (always 0 or 1) and
+	// engineering notation (always 0, 1, 2, or 3).
+	Comma uint8
+
+	Neg bool
+	Inf bool // Takes precedence over Digits and Exp.
+	NaN bool // Takes precedence over Inf.
 
 	buf [maxIntDigits]byte
 }
diff --git a/internal/number/format.go b/internal/number/format.go
index 70ddf7d..533a85a 100755
--- a/internal/number/format.go
+++ b/internal/number/format.go
@@ -131,23 +131,87 @@
 	return result
 }
 
-// appendDecimal appends a formatted number to dst. It returns two possible
-// insertion points for padding.
-func appendDecimal(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
-	if dst, ok := f.renderSpecial(dst, d); ok {
-		return dst, 0, len(dst)
+// TODO: just return visible digits.
+func decimalVisibleDigits(f *Formatter, d *Decimal) Decimal {
+	if d.NaN || d.Inf {
+		return *d
 	}
 	n := d.normalize()
 	if maxSig := int(f.MaxSignificantDigits); maxSig > 0 {
+		// TODO: really round to zero?
 		n.round(ToZero, maxSig)
 	}
 	digits := n.Digits
 	exp := n.Exp
 	exp += int32(f.Pattern.DigitShift)
 
+	// Cap integer digits. Remove *most-significant* digits.
+	if f.MaxIntegerDigits > 0 {
+		if p := int(exp) - int(f.MaxIntegerDigits); p > 0 {
+			if p > len(digits) {
+				p = len(digits)
+			}
+			if digits = digits[p:]; len(digits) == 0 {
+				exp = 0
+			} else {
+				exp -= int32(p)
+			}
+			// Strip leading zeros.
+			for len(digits) > 0 && digits[0] == 0 {
+				digits = digits[1:]
+				exp--
+			}
+		}
+	}
+
+	// Rounding usually is done by convert, but we don't rely on it.
+	numFrac := len(digits) - int(exp)
+	if f.MaxSignificantDigits == 0 && int(f.MaxFractionDigits) < numFrac {
+		p := int(exp) + int(f.MaxFractionDigits)
+		if p <= 0 {
+			p = 0
+		} else if p >= len(digits) {
+			p = len(digits)
+		}
+		digits = digits[:p] // TODO: round
+	}
+
+	// set End (trailing zeros)
+	n.End = int32(len(digits))
+	if len(digits) == 0 {
+		if f.MinFractionDigits > 0 {
+			n.End = int32(f.MinFractionDigits)
+		}
+		if p := int32(f.MinSignificantDigits) - 1; p > n.End {
+			n.End = p
+		}
+	} else {
+		if end := exp + int32(f.MinFractionDigits); end > n.End {
+			n.End = end
+		}
+		if n.End < int32(f.MinSignificantDigits) {
+			n.End = int32(f.MinSignificantDigits)
+		}
+	}
+	n.Digits = digits
+	n.Exp = exp
+	return n
+}
+
+// appendDecimal appends a formatted number to dst. It returns two possible
+// insertion points for padding.
+func appendDecimal(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
+	if dst, ok := f.renderSpecial(dst, d); ok {
+		return dst, 0, len(dst)
+	}
+	n := decimalVisibleDigits(f, d)
+	digits := n.Digits
+	exp := n.Exp
+
 	// Split in integer and fraction part.
 	var intDigits, fracDigits []byte
-	var numInt, numFrac int
+	numInt := 0
+	numFrac := int(n.End - n.Exp)
 	if exp > 0 {
 		numInt = int(exp)
 		if int(exp) >= len(digits) { // ddddd | ddddd00
@@ -155,39 +219,9 @@
 		} 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
@@ -220,43 +254,41 @@
 		}
 	}
 
-	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 {
+	if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
 		dst = append(dst, f.Symbol(SymDecimal)...)
 	}
 	// Add leading zeros
-	for i := numFrac - len(fracDigits); i > 0; i-- {
+	i = 0
+	for n := -int(n.Exp); i < n; i++ {
 		dst = f.AppendDigit(dst, 0)
 	}
-	i = 0
-	for ; i < len(fracDigits); i++ {
-		dst = f.AppendDigit(dst, fracDigits[i])
+	for _, d := range fracDigits {
+		i++
+		dst = f.AppendDigit(dst, d)
 	}
-	for ; trailZero > 0; trailZero-- {
+	for ; i < numFrac; i++ {
 		dst = f.AppendDigit(dst, 0)
 	}
 	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
 }
 
-// appendScientific appends a formatted number to dst. It returns two possible
-// insertion points for padding.
-func appendScientific(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
-	if dst, ok := f.renderSpecial(dst, d); ok {
-		return dst, 0, 0
+func scientificVisibleDigits(f *Formatter, d *Decimal) Decimal {
+	if d.NaN || d.Inf {
+		return *d
 	}
-	// Significant digits are transformed by parser for scientific notation and
-	// do not need to be handled here.
+	n := d.normalize()
+
+	// Significant digits are transformed by the 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 {
+		// TODO: really round to zero?
 		n.round(ToZero, maxSig)
 	}
 	digits := n.Digits
@@ -282,6 +314,30 @@
 	} else {
 		exp -= int32(numInt)
 	}
+
+	n.Comma = uint8(numInt)
+	n.End = int32(len(digits))
+	if n.End < int32(minSig) {
+		n.End = int32(minSig)
+	}
+	n.Digits = digits
+	n.Exp = exp
+	return n
+}
+
+// appendScientific appends a formatted number to dst. It returns two possible
+// insertion points for padding.
+func appendScientific(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
+	if dst, ok := f.renderSpecial(dst, d); ok {
+		return dst, 0, 0
+	}
+	// n := d.normalize()
+	n := scientificVisibleDigits(f, d)
+	digits := n.Digits
+	exp := n.Exp
+	numInt := int(n.Comma)
+	numFrac := int(n.End) - int(n.Comma)
+
 	var intDigits, fracDigits []byte
 	if numInt <= len(digits) {
 		intDigits = digits[:numInt]
@@ -308,15 +364,14 @@
 		}
 	}
 
-	trailZero := minSig - numInt - len(fracDigits)
-	if len(fracDigits) > 0 || trailZero > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
+	if numFrac > 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-- {
+	for ; i < numFrac; i++ {
 		dst = f.AppendDigit(dst, 0)
 	}