taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

format.go (13681B)


      1 // Copyright 2017 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package number
      6 
      7 import (
      8 	"strconv"
      9 	"unicode/utf8"
     10 
     11 	"golang.org/x/text/language"
     12 )
     13 
     14 // TODO:
     15 // - grouping of fractions
     16 // - allow user-defined superscript notation (such as <sup>4</sup>)
     17 // - same for non-breaking spaces, like &nbsp;
     18 
     19 // A VisibleDigits computes digits, comma placement and trailing zeros as they
     20 // will be shown to the user.
     21 type VisibleDigits interface {
     22 	Digits(buf []byte, t language.Tag, scale int) Digits
     23 	// TODO: Do we also need to add the verb or pass a format.State?
     24 }
     25 
     26 // Formatting proceeds along the following lines:
     27 // 0) Compose rounding information from format and context.
     28 // 1) Convert a number into a Decimal.
     29 // 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
     30 //    (non-increment) rounding. The Decimal that results from this is suitable
     31 //    for determining the plural form.
     32 // 3) Render the Decimal in the localized form.
     33 
     34 // Formatter contains all the information needed to render a number.
     35 type Formatter struct {
     36 	Pattern
     37 	Info
     38 }
     39 
     40 func (f *Formatter) init(t language.Tag, index []uint8) {
     41 	f.Info = InfoFromTag(t)
     42 	f.Pattern = formats[index[tagToID(t)]]
     43 }
     44 
     45 // InitPattern initializes a Formatter for the given Pattern.
     46 func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
     47 	f.Info = InfoFromTag(t)
     48 	f.Pattern = *pat
     49 }
     50 
     51 // InitDecimal initializes a Formatter using the default Pattern for the given
     52 // language.
     53 func (f *Formatter) InitDecimal(t language.Tag) {
     54 	f.init(t, tagToDecimal)
     55 }
     56 
     57 // InitScientific initializes a Formatter using the default Pattern for the
     58 // given language.
     59 func (f *Formatter) InitScientific(t language.Tag) {
     60 	f.init(t, tagToScientific)
     61 	f.Pattern.MinFractionDigits = 0
     62 	f.Pattern.MaxFractionDigits = -1
     63 }
     64 
     65 // InitEngineering initializes a Formatter using the default Pattern for the
     66 // given language.
     67 func (f *Formatter) InitEngineering(t language.Tag) {
     68 	f.init(t, tagToScientific)
     69 	f.Pattern.MinFractionDigits = 0
     70 	f.Pattern.MaxFractionDigits = -1
     71 	f.Pattern.MaxIntegerDigits = 3
     72 	f.Pattern.MinIntegerDigits = 1
     73 }
     74 
     75 // InitPercent initializes a Formatter using the default Pattern for the given
     76 // language.
     77 func (f *Formatter) InitPercent(t language.Tag) {
     78 	f.init(t, tagToPercent)
     79 }
     80 
     81 // InitPerMille initializes a Formatter using the default Pattern for the given
     82 // language.
     83 func (f *Formatter) InitPerMille(t language.Tag) {
     84 	f.init(t, tagToPercent)
     85 	f.Pattern.DigitShift = 3
     86 }
     87 
     88 func (f *Formatter) Append(dst []byte, x interface{}) []byte {
     89 	var d Decimal
     90 	r := f.RoundingContext
     91 	d.Convert(r, x)
     92 	return f.Render(dst, FormatDigits(&d, r))
     93 }
     94 
     95 func FormatDigits(d *Decimal, r RoundingContext) Digits {
     96 	if r.isScientific() {
     97 		return scientificVisibleDigits(r, d)
     98 	}
     99 	return decimalVisibleDigits(r, d)
    100 }
    101 
    102 func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
    103 	return f.Render(dst, FormatDigits(d, f.RoundingContext))
    104 }
    105 
    106 func (f *Formatter) Render(dst []byte, d Digits) []byte {
    107 	var result []byte
    108 	var postPrefix, preSuffix int
    109 	if d.IsScientific {
    110 		result, postPrefix, preSuffix = appendScientific(dst, f, &d)
    111 	} else {
    112 		result, postPrefix, preSuffix = appendDecimal(dst, f, &d)
    113 	}
    114 	if f.PadRune == 0 {
    115 		return result
    116 	}
    117 	width := int(f.FormatWidth)
    118 	if count := utf8.RuneCount(result); count < width {
    119 		insertPos := 0
    120 		switch f.Flags & PadMask {
    121 		case PadAfterPrefix:
    122 			insertPos = postPrefix
    123 		case PadBeforeSuffix:
    124 			insertPos = preSuffix
    125 		case PadAfterSuffix:
    126 			insertPos = len(result)
    127 		}
    128 		num := width - count
    129 		pad := [utf8.UTFMax]byte{' '}
    130 		sz := 1
    131 		if r := f.PadRune; r != 0 {
    132 			sz = utf8.EncodeRune(pad[:], r)
    133 		}
    134 		extra := sz * num
    135 		if n := len(result) + extra; n < cap(result) {
    136 			result = result[:n]
    137 			copy(result[insertPos+extra:], result[insertPos:])
    138 		} else {
    139 			buf := make([]byte, n)
    140 			copy(buf, result[:insertPos])
    141 			copy(buf[insertPos+extra:], result[insertPos:])
    142 			result = buf
    143 		}
    144 		for ; num > 0; num-- {
    145 			insertPos += copy(result[insertPos:], pad[:sz])
    146 		}
    147 	}
    148 	return result
    149 }
    150 
    151 // decimalVisibleDigits converts d according to the RoundingContext. Note that
    152 // the exponent may change as a result of this operation.
    153 func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
    154 	if d.NaN || d.Inf {
    155 		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
    156 	}
    157 	n := Digits{digits: d.normalize().digits}
    158 
    159 	exp := n.Exp
    160 	exp += int32(r.DigitShift)
    161 
    162 	// Cap integer digits. Remove *most-significant* digits.
    163 	if r.MaxIntegerDigits > 0 {
    164 		if p := int(exp) - int(r.MaxIntegerDigits); p > 0 {
    165 			if p > len(n.Digits) {
    166 				p = len(n.Digits)
    167 			}
    168 			if n.Digits = n.Digits[p:]; len(n.Digits) == 0 {
    169 				exp = 0
    170 			} else {
    171 				exp -= int32(p)
    172 			}
    173 			// Strip leading zeros.
    174 			for len(n.Digits) > 0 && n.Digits[0] == 0 {
    175 				n.Digits = n.Digits[1:]
    176 				exp--
    177 			}
    178 		}
    179 	}
    180 
    181 	// Rounding if not already done by Convert.
    182 	p := len(n.Digits)
    183 	if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
    184 		p = maxSig
    185 	}
    186 	if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 {
    187 		if cap := int(exp) + maxFrac; cap < p {
    188 			p = int(exp) + maxFrac
    189 		}
    190 		if p < 0 {
    191 			p = 0
    192 		}
    193 	}
    194 	n.round(r.Mode, p)
    195 
    196 	// set End (trailing zeros)
    197 	n.End = int32(len(n.Digits))
    198 	if n.End == 0 {
    199 		exp = 0
    200 		if r.MinFractionDigits > 0 {
    201 			n.End = int32(r.MinFractionDigits)
    202 		}
    203 		if p := int32(r.MinSignificantDigits) - 1; p > n.End {
    204 			n.End = p
    205 		}
    206 	} else {
    207 		if end := exp + int32(r.MinFractionDigits); end > n.End {
    208 			n.End = end
    209 		}
    210 		if n.End < int32(r.MinSignificantDigits) {
    211 			n.End = int32(r.MinSignificantDigits)
    212 		}
    213 	}
    214 	n.Exp = exp
    215 	return n
    216 }
    217 
    218 // appendDecimal appends a formatted number to dst. It returns two possible
    219 // insertion points for padding.
    220 func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
    221 	if dst, ok := f.renderSpecial(dst, n); ok {
    222 		return dst, 0, len(dst)
    223 	}
    224 	digits := n.Digits
    225 	exp := n.Exp
    226 
    227 	// Split in integer and fraction part.
    228 	var intDigits, fracDigits []byte
    229 	numInt := 0
    230 	numFrac := int(n.End - n.Exp)
    231 	if exp > 0 {
    232 		numInt = int(exp)
    233 		if int(exp) >= len(digits) { // ddddd | ddddd00
    234 			intDigits = digits
    235 		} else { // ddd.dd
    236 			intDigits = digits[:exp]
    237 			fracDigits = digits[exp:]
    238 		}
    239 	} else {
    240 		fracDigits = digits
    241 	}
    242 
    243 	neg := n.Neg
    244 	affix, suffix := f.getAffixes(neg)
    245 	dst = appendAffix(dst, f, affix, neg)
    246 	savedLen := len(dst)
    247 
    248 	minInt := int(f.MinIntegerDigits)
    249 	if minInt == 0 && f.MinSignificantDigits > 0 {
    250 		minInt = 1
    251 	}
    252 	// add leading zeros
    253 	for i := minInt; i > numInt; i-- {
    254 		dst = f.AppendDigit(dst, 0)
    255 		if f.needsSep(i) {
    256 			dst = append(dst, f.Symbol(SymGroup)...)
    257 		}
    258 	}
    259 	i := 0
    260 	for ; i < len(intDigits); i++ {
    261 		dst = f.AppendDigit(dst, intDigits[i])
    262 		if f.needsSep(numInt - i) {
    263 			dst = append(dst, f.Symbol(SymGroup)...)
    264 		}
    265 	}
    266 	for ; i < numInt; i++ {
    267 		dst = f.AppendDigit(dst, 0)
    268 		if f.needsSep(numInt - i) {
    269 			dst = append(dst, f.Symbol(SymGroup)...)
    270 		}
    271 	}
    272 
    273 	if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
    274 		dst = append(dst, f.Symbol(SymDecimal)...)
    275 	}
    276 	// Add trailing zeros
    277 	i = 0
    278 	for n := -int(n.Exp); i < n; i++ {
    279 		dst = f.AppendDigit(dst, 0)
    280 	}
    281 	for _, d := range fracDigits {
    282 		i++
    283 		dst = f.AppendDigit(dst, d)
    284 	}
    285 	for ; i < numFrac; i++ {
    286 		dst = f.AppendDigit(dst, 0)
    287 	}
    288 	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
    289 }
    290 
    291 func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
    292 	if d.NaN || d.Inf {
    293 		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
    294 	}
    295 	n := Digits{digits: d.normalize().digits, IsScientific: true}
    296 
    297 	// Normalize to have at least one digit. This simplifies engineering
    298 	// notation.
    299 	if len(n.Digits) == 0 {
    300 		n.Digits = append(n.Digits, 0)
    301 		n.Exp = 1
    302 	}
    303 
    304 	// Significant digits are transformed by the parser for scientific notation
    305 	// and do not need to be handled here.
    306 	maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits)
    307 	if numInt == 0 {
    308 		numInt = 1
    309 	}
    310 
    311 	// If a maximum number of integers is specified, the minimum must be 1
    312 	// and the exponent is grouped by this number (e.g. for engineering)
    313 	if maxInt > numInt {
    314 		// Correct the exponent to reflect a single integer digit.
    315 		numInt = 1
    316 		// engineering
    317 		// 0.01234 ([12345]e-1) -> 1.2345e-2  12.345e-3
    318 		// 12345   ([12345]e+5) -> 1.2345e4  12.345e3
    319 		d := int(n.Exp-1) % maxInt
    320 		if d < 0 {
    321 			d += maxInt
    322 		}
    323 		numInt += d
    324 	}
    325 
    326 	p := len(n.Digits)
    327 	if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
    328 		p = maxSig
    329 	}
    330 	if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p {
    331 		p = numInt + maxFrac
    332 	}
    333 	n.round(r.Mode, p)
    334 
    335 	n.Comma = uint8(numInt)
    336 	n.End = int32(len(n.Digits))
    337 	if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig {
    338 		n.End = minSig
    339 	}
    340 	return n
    341 }
    342 
    343 // appendScientific appends a formatted number to dst. It returns two possible
    344 // insertion points for padding.
    345 func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
    346 	if dst, ok := f.renderSpecial(dst, n); ok {
    347 		return dst, 0, 0
    348 	}
    349 	digits := n.Digits
    350 	numInt := int(n.Comma)
    351 	numFrac := int(n.End) - int(n.Comma)
    352 
    353 	var intDigits, fracDigits []byte
    354 	if numInt <= len(digits) {
    355 		intDigits = digits[:numInt]
    356 		fracDigits = digits[numInt:]
    357 	} else {
    358 		intDigits = digits
    359 	}
    360 	neg := n.Neg
    361 	affix, suffix := f.getAffixes(neg)
    362 	dst = appendAffix(dst, f, affix, neg)
    363 	savedLen := len(dst)
    364 
    365 	i := 0
    366 	for ; i < len(intDigits); i++ {
    367 		dst = f.AppendDigit(dst, intDigits[i])
    368 		if f.needsSep(numInt - i) {
    369 			dst = append(dst, f.Symbol(SymGroup)...)
    370 		}
    371 	}
    372 	for ; i < numInt; i++ {
    373 		dst = f.AppendDigit(dst, 0)
    374 		if f.needsSep(numInt - i) {
    375 			dst = append(dst, f.Symbol(SymGroup)...)
    376 		}
    377 	}
    378 
    379 	if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
    380 		dst = append(dst, f.Symbol(SymDecimal)...)
    381 	}
    382 	i = 0
    383 	for ; i < len(fracDigits); i++ {
    384 		dst = f.AppendDigit(dst, fracDigits[i])
    385 	}
    386 	for ; i < numFrac; i++ {
    387 		dst = f.AppendDigit(dst, 0)
    388 	}
    389 
    390 	// exp
    391 	buf := [12]byte{}
    392 	// TODO: use exponential if superscripting is not available (no Latin
    393 	// numbers or no tags) and use exponential in all other cases.
    394 	exp := n.Exp - int32(n.Comma)
    395 	exponential := f.Symbol(SymExponential)
    396 	if exponential == "E" {
    397 		dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
    398 		dst = f.AppendDigit(dst, 1)
    399 		dst = f.AppendDigit(dst, 0)
    400 		switch {
    401 		case exp < 0:
    402 			dst = append(dst, superMinus...)
    403 			exp = -exp
    404 		case f.Flags&AlwaysExpSign != 0:
    405 			dst = append(dst, superPlus...)
    406 		}
    407 		b = strconv.AppendUint(buf[:0], uint64(exp), 10)
    408 		for i := len(b); i < int(f.MinExponentDigits); i++ {
    409 			dst = append(dst, superDigits[0]...)
    410 		}
    411 		for _, c := range b {
    412 			dst = append(dst, superDigits[c-'0']...)
    413 		}
    414 	} else {
    415 		dst = append(dst, exponential...)
    416 		switch {
    417 		case exp < 0:
    418 			dst = append(dst, f.Symbol(SymMinusSign)...)
    419 			exp = -exp
    420 		case f.Flags&AlwaysExpSign != 0:
    421 			dst = append(dst, f.Symbol(SymPlusSign)...)
    422 		}
    423 		b = strconv.AppendUint(buf[:0], uint64(exp), 10)
    424 		for i := len(b); i < int(f.MinExponentDigits); i++ {
    425 			dst = f.AppendDigit(dst, 0)
    426 		}
    427 		for _, c := range b {
    428 			dst = f.AppendDigit(dst, c-'0')
    429 		}
    430 	}
    431 	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
    432 }
    433 
    434 const (
    435 	superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
    436 	superPlus  = "\u207A" // SUPERSCRIPT PLUS SIGN
    437 )
    438 
    439 var (
    440 	// Note: the digits are not sequential!!!
    441 	superDigits = []string{
    442 		"\u2070", // SUPERSCRIPT DIGIT ZERO
    443 		"\u00B9", // SUPERSCRIPT DIGIT ONE
    444 		"\u00B2", // SUPERSCRIPT DIGIT TWO
    445 		"\u00B3", // SUPERSCRIPT DIGIT THREE
    446 		"\u2074", // SUPERSCRIPT DIGIT FOUR
    447 		"\u2075", // SUPERSCRIPT DIGIT FIVE
    448 		"\u2076", // SUPERSCRIPT DIGIT SIX
    449 		"\u2077", // SUPERSCRIPT DIGIT SEVEN
    450 		"\u2078", // SUPERSCRIPT DIGIT EIGHT
    451 		"\u2079", // SUPERSCRIPT DIGIT NINE
    452 	}
    453 )
    454 
    455 func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
    456 	str := f.Affix
    457 	if str != "" {
    458 		if f.NegOffset > 0 {
    459 			if neg {
    460 				str = str[f.NegOffset:]
    461 			} else {
    462 				str = str[:f.NegOffset]
    463 			}
    464 		}
    465 		sufStart := 1 + str[0]
    466 		affix = str[1:sufStart]
    467 		suffix = str[sufStart+1:]
    468 	}
    469 	// TODO: introduce a NeedNeg sign to indicate if the left pattern already
    470 	// has a sign marked?
    471 	if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
    472 		affix = "-" + affix
    473 	}
    474 	return affix, suffix
    475 }
    476 
    477 func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) {
    478 	if d.NaN {
    479 		return fmtNaN(dst, f), true
    480 	}
    481 	if d.Inf {
    482 		return fmtInfinite(dst, f, d), true
    483 	}
    484 	return dst, false
    485 }
    486 
    487 func fmtNaN(dst []byte, f *Formatter) []byte {
    488 	return append(dst, f.Symbol(SymNan)...)
    489 }
    490 
    491 func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte {
    492 	affix, suffix := f.getAffixes(d.Neg)
    493 	dst = appendAffix(dst, f, affix, d.Neg)
    494 	dst = append(dst, f.Symbol(SymInfinity)...)
    495 	dst = appendAffix(dst, f, suffix, d.Neg)
    496 	return dst
    497 }
    498 
    499 func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
    500 	quoting := false
    501 	escaping := false
    502 	for _, r := range affix {
    503 		switch {
    504 		case escaping:
    505 			// escaping occurs both inside and outside of quotes
    506 			dst = append(dst, string(r)...)
    507 			escaping = false
    508 		case r == '\\':
    509 			escaping = true
    510 		case r == '\'':
    511 			quoting = !quoting
    512 		case quoting:
    513 			dst = append(dst, string(r)...)
    514 		case r == '%':
    515 			if f.DigitShift == 3 {
    516 				dst = append(dst, f.Symbol(SymPerMille)...)
    517 			} else {
    518 				dst = append(dst, f.Symbol(SymPercentSign)...)
    519 			}
    520 		case r == '-' || r == '+':
    521 			if neg {
    522 				dst = append(dst, f.Symbol(SymMinusSign)...)
    523 			} else if f.Flags&ElideSign == 0 {
    524 				dst = append(dst, f.Symbol(SymPlusSign)...)
    525 			} else {
    526 				dst = append(dst, ' ')
    527 			}
    528 		default:
    529 			dst = append(dst, string(r)...)
    530 		}
    531 	}
    532 	return dst
    533 }