1package humanize
2
3/*
4Slightly adapted from the source to fit go-humanize.
5
6Author: https://github.com/gorhill
7Source: https://gist.github.com/gorhill/5285193
8
9*/
10
11import (
12	"math"
13	"strconv"
14)
15
16var (
17	renderFloatPrecisionMultipliers = [...]float64{
18		1,
19		10,
20		100,
21		1000,
22		10000,
23		100000,
24		1000000,
25		10000000,
26		100000000,
27		1000000000,
28	}
29
30	renderFloatPrecisionRounders = [...]float64{
31		0.5,
32		0.05,
33		0.005,
34		0.0005,
35		0.00005,
36		0.000005,
37		0.0000005,
38		0.00000005,
39		0.000000005,
40		0.0000000005,
41	}
42)
43
44// FormatFloat produces a formatted number as string based on the following user-specified criteria:
45// * thousands separator
46// * decimal separator
47// * decimal precision
48//
49// Usage: s := RenderFloat(format, n)
50// The format parameter tells how to render the number n.
51//
52// See examples: http://play.golang.org/p/LXc1Ddm1lJ
53//
54// Examples of format strings, given n = 12345.6789:
55// "#,###.##" => "12,345.67"
56// "#,###." => "12,345"
57// "#,###" => "12345,678"
58// "#\u202F###,##" => "12 345,68"
59// "#.###,###### => 12.345,678900
60// "" (aka default format) => 12,345.67
61//
62// The highest precision allowed is 9 digits after the decimal symbol.
63// There is also a version for integer number, FormatInteger(),
64// which is convenient for calls within template.
65func FormatFloat(format string, n float64) string {
66	// Special cases:
67	//   NaN = "NaN"
68	//   +Inf = "+Infinity"
69	//   -Inf = "-Infinity"
70	if math.IsNaN(n) {
71		return "NaN"
72	}
73	if n > math.MaxFloat64 {
74		return "Infinity"
75	}
76	if n < -math.MaxFloat64 {
77		return "-Infinity"
78	}
79
80	// default format
81	precision := 2
82	decimalStr := "."
83	thousandStr := ","
84	positiveStr := ""
85	negativeStr := "-"
86
87	if len(format) > 0 {
88		format := []rune(format)
89
90		// If there is an explicit format directive,
91		// then default values are these:
92		precision = 9
93		thousandStr = ""
94
95		// collect indices of meaningful formatting directives
96		formatIndx := []int{}
97		for i, char := range format {
98			if char != '#' && char != '0' {
99				formatIndx = append(formatIndx, i)
100			}
101		}
102
103		if len(formatIndx) > 0 {
104			// Directive at index 0:
105			//   Must be a '+'
106			//   Raise an error if not the case
107			// index: 0123456789
108			//        +0.000,000
109			//        +000,000.0
110			//        +0000.00
111			//        +0000
112			if formatIndx[0] == 0 {
113				if format[formatIndx[0]] != '+' {
114					panic("RenderFloat(): invalid positive sign directive")
115				}
116				positiveStr = "+"
117				formatIndx = formatIndx[1:]
118			}
119
120			// Two directives:
121			//   First is thousands separator
122			//   Raise an error if not followed by 3-digit
123			// 0123456789
124			// 0.000,000
125			// 000,000.00
126			if len(formatIndx) == 2 {
127				if (formatIndx[1] - formatIndx[0]) != 4 {
128					panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
129				}
130				thousandStr = string(format[formatIndx[0]])
131				formatIndx = formatIndx[1:]
132			}
133
134			// One directive:
135			//   Directive is decimal separator
136			//   The number of digit-specifier following the separator indicates wanted precision
137			// 0123456789
138			// 0.00
139			// 000,0000
140			if len(formatIndx) == 1 {
141				decimalStr = string(format[formatIndx[0]])
142				precision = len(format) - formatIndx[0] - 1
143			}
144		}
145	}
146
147	// generate sign part
148	var signStr string
149	if n >= 0.000000001 {
150		signStr = positiveStr
151	} else if n <= -0.000000001 {
152		signStr = negativeStr
153		n = -n
154	} else {
155		signStr = ""
156		n = 0.0
157	}
158
159	// split number into integer and fractional parts
160	intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
161
162	// generate integer part string
163	intStr := strconv.FormatInt(int64(intf), 10)
164
165	// add thousand separator if required
166	if len(thousandStr) > 0 {
167		for i := len(intStr); i > 3; {
168			i -= 3
169			intStr = intStr[:i] + thousandStr + intStr[i:]
170		}
171	}
172
173	// no fractional part, we can leave now
174	if precision == 0 {
175		return signStr + intStr
176	}
177
178	// generate fractional part
179	fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
180	// may need padding
181	if len(fracStr) < precision {
182		fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
183	}
184
185	return signStr + intStr + decimalStr + fracStr
186}
187
188// FormatInteger produces a formatted number as string.
189// See FormatFloat.
190func FormatInteger(format string, n int) string {
191	return FormatFloat(format, float64(n))
192}
193