1 // Copyright 2016 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.
5 //go:generate go run gen.go gen_common.go
7 // Package plural provides utilities for handling linguistic plurals in text.
9 // The definitions in this package are based on the plural rule handling defined
11 // http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules for
16 "golang.org/x/text/internal/number"
17 "golang.org/x/text/language"
20 // Rules defines the plural rules for all languages for a certain plural type.
23 // This package is UNDER CONSTRUCTION and its API may change.
28 inclusionMasks []uint64
32 // Cardinal defines the plural rules for numbers indicating quantities.
33 Cardinal *Rules = cardinal
35 // Ordinal defines the plural rules for numbers indicating position
36 // (first, second, etc.).
37 Ordinal *Rules = ordinal
43 ordinalInclusionMasks[:],
50 cardinalInclusionMasks[:],
54 // getIntApprox converts the digits in slice digits[start:end] to an integer
55 // according to the following rules:
56 // - Let i be asInt(digits[start:end]), where out-of-range digits are assumed
58 // - Result n is big if i / 10^nMod > 1.
59 // - Otherwise the result is i % 10^nMod.
61 // For example, if digits is {1, 2, 3} and start:end is 0:5, then the result
62 // for various values of nMod is:
63 // - when nMod == 2, n == big
64 // - when nMod == 3, n == big
65 // - when nMod == 4, n == big
66 // - when nMod == 5, n == 12300
67 // - when nMod == 6, n == 12300
68 // - when nMod == 7, n == 12300
69 func getIntApprox(digits []byte, start, end, nMod, big int) (n int) {
70 // Leading 0 digits just result in 0.
75 // Range only over the part for which we have digits.
77 if mid >= len(digits) {
80 // Check digits more significant that nMod.
81 if q := end - nMod; q > 0 {
92 n = 10*n + int(digits[p])
94 // Multiply for trailing zeros.
101 // MatchDigits computes the plural form for the given language and the given
102 // decimal floating point digits. The digits are stored in big-endian order and
103 // are of value byte(0) - byte(9). The floating point position is indicated by
104 // exp and the number of visible decimals is scale. All leading and trailing
105 // zeros may be omitted from digits.
107 // The following table contains examples of possible arguments to represent
108 // the given numbers.
109 // decimal digits exp scale
110 // 123 []byte{1, 2, 3} 3 0
111 // 123.4 []byte{1, 2, 3, 4} 3 1
112 // 123.40 []byte{1, 2, 3, 4} 3 2
113 // 100000 []byte{1} 6 0
114 // 100000.00 []byte{1} 6 3
115 func (p *Rules) MatchDigits(t language.Tag, digits []byte, exp, scale int) Form {
116 index, _ := language.CompactIndex(t)
118 // Differentiate up to including mod 1000000 for the integer part.
119 n := getIntApprox(digits, 0, exp, 6, 1000000)
121 // Differentiate up to including mod 100 for the fractional part.
122 f := getIntApprox(digits, exp, exp+scale, 2, 100)
124 return matchPlural(p, index, n, f, scale)
127 func (p *Rules) matchDisplayDigits(t language.Tag, d *number.Digits) (Form, int) {
128 n := getIntApprox(d.Digits, 0, int(d.Exp), 6, 1000000)
129 return p.MatchDigits(t, d.Digits, int(d.Exp), d.NumFracDigits()), n
132 func validForms(p *Rules, t language.Tag) (forms []Form) {
133 index, _ := language.CompactIndex(t)
134 offset := p.langToIndex[index]
135 rules := p.rules[p.index[offset]:p.index[offset+1]]
137 forms = append(forms, Other)
139 for _, r := range rules {
140 if cat := Form(r.cat & formMask); cat != andNext && last != cat {
141 forms = append(forms, cat)
148 func (p *Rules) matchComponents(t language.Tag, n, f, scale int) Form {
149 index, _ := language.CompactIndex(t)
150 return matchPlural(p, index, n, f, scale)
153 // MatchPlural returns the plural form for the given language and plural
154 // operands (as defined in
155 // http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules):
157 // n absolute value of the source number (integer and decimals)
159 // i integer digits of n.
160 // v number of visible fraction digits in n, with trailing zeros.
161 // w number of visible fraction digits in n, without trailing zeros.
162 // f visible fractional digits in n, with trailing zeros (f = t * 10^(v-w))
163 // t visible fractional digits in n, without trailing zeros.
165 // If any of the operand values is too large to fit in an int, it is okay to
166 // pass the value modulo 10,000,000.
167 func (p *Rules) MatchPlural(lang language.Tag, i, v, w, f, t int) Form {
168 index, _ := language.CompactIndex(lang)
169 return matchPlural(p, index, i, f, v)
172 func matchPlural(p *Rules, index int, n, f, v int) Form {
173 nMask := p.inclusionMasks[n%maxMod]
174 // Compute the fMask inline in the rules below, as it is relatively rare.
175 // fMask := p.inclusionMasks[f%maxMod]
176 vMask := p.inclusionMasks[v%maxMod]
179 offset := p.langToIndex[index]
180 rules := p.rules[p.index[offset]:p.index[offset+1]]
181 for i := 0; i < len(rules); i++ {
183 setBit := uint64(1 << rule.setID)
185 switch op := opID(rule.cat >> opShift); op {
187 skip = n >= numN || nMask&setBit == 0
189 case opI | opNotEqual: // i != x
190 skip = n < numN && nMask&setBit != 0
192 case opI | opMod: // i % m = x
193 skip = nMask&setBit == 0
195 case opI | opMod | opNotEqual: // i % m != x
196 skip = nMask&setBit != 0
199 skip = f != 0 || n >= numN || nMask&setBit == 0
201 case opN | opNotEqual: // n != x
202 skip = f == 0 && n < numN && nMask&setBit != 0
204 case opN | opMod: // n % m = x
205 skip = f != 0 || nMask&setBit == 0
207 case opN | opMod | opNotEqual: // n % m != x
208 skip = f == 0 && nMask&setBit != 0
211 skip = f >= numN || p.inclusionMasks[f%maxMod]&setBit == 0
213 case opF | opNotEqual: // f != x
214 skip = f < numN && p.inclusionMasks[f%maxMod]&setBit != 0
216 case opF | opMod: // f % m = x
217 skip = p.inclusionMasks[f%maxMod]&setBit == 0
219 case opF | opMod | opNotEqual: // f % m != x
220 skip = p.inclusionMasks[f%maxMod]&setBit != 0
223 skip = v < numN && vMask&setBit == 0
225 case opV | opNotEqual: // v != x
226 skip = v < numN && vMask&setBit != 0
231 case opW | opNotEqual: // w != 0
234 // Hard-wired rules that cannot be handled by our algorithm.
237 skip = f != 0 || n == 0 || n%1000000 != 0
239 case opAzerbaijan00s:
240 // 100,200,300,400,500,600,700,800,900
241 skip = n == 0 || n >= 1000 || n%100 != 0
244 skip = (f != 0 || n >= numN || nMask&setBit == 0) && n != 800
247 // advance over AND entries.
248 for ; i < len(rules) && rules[i].cat&formMask == andNext; i++ {
252 // return if we have a final entry.
253 if cat := rule.cat & formMask; cat != andNext {