summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/text/internal/number/plural.go
blob: 5714e1128ea04d10c7c5b752c0b2cd89f2fdf273 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2016 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 "golang.org/x/text/internal/format/plural"

type pluralRules struct {
	rules          []pluralCheck
	index          []byte
	langToIndex    []byte
	inclusionMasks []uint64
}

var (
	ordinalData = pluralRules{
		ordinalRules,
		ordinalIndex,
		ordinalLangToIndex,
		ordinalInclusionMasks[:],
	}
	cardinalData = pluralRules{
		cardinalRules,
		cardinalIndex,
		cardinalLangToIndex,
		cardinalInclusionMasks[:],
	}
)

// See gen_plural.go for an explanation of the algorithm.

func matchPlural(p *pluralRules, index int, n, f, v int) plural.Form {
	nMask := p.inclusionMasks[n%maxMod]
	// Compute the fMask inline in the rules below, as it is relatively rare.
	// fMask := p.inclusionMasks[f%maxMod]
	vMask := p.inclusionMasks[v%maxMod]

	// Do the matching
	offset := p.langToIndex[index]
	rules := p.rules[p.index[offset]:p.index[offset+1]]
	for i := 0; i < len(rules); i++ {
		rule := rules[i]
		setBit := uint64(1 << rule.setID)
		var skip bool
		switch op := opID(rule.cat >> opShift); op {
		case opI: // i = x
			skip = n >= numN || nMask&setBit == 0

		case opI | opNotEqual: // i != x
			skip = n < numN && nMask&setBit != 0

		case opI | opMod: // i % m = x
			skip = nMask&setBit == 0

		case opI | opMod | opNotEqual: // i % m != x
			skip = nMask&setBit != 0

		case opN: // n = x
			skip = f != 0 || n >= numN || nMask&setBit == 0

		case opN | opNotEqual: // n != x
			skip = f == 0 && n < numN && nMask&setBit != 0

		case opN | opMod: // n % m = x
			skip = f != 0 || nMask&setBit == 0

		case opN | opMod | opNotEqual: // n % m != x
			skip = f == 0 && nMask&setBit != 0

		case opF: // f = x
			skip = f >= numN || p.inclusionMasks[f%maxMod]&setBit == 0

		case opF | opNotEqual: // f != x
			skip = f < numN && p.inclusionMasks[f%maxMod]&setBit != 0

		case opF | opMod: // f % m = x
			skip = p.inclusionMasks[f%maxMod]&setBit == 0

		case opF | opMod | opNotEqual: // f % m != x
			skip = p.inclusionMasks[f%maxMod]&setBit != 0

		case opV: // v = x
			skip = v < numN && vMask&setBit == 0

		case opV | opNotEqual: // v != x
			skip = v < numN && vMask&setBit != 0

		case opW: // w == 0
			skip = f != 0

		case opW | opNotEqual: // w != 0
			skip = f == 0

		// Hard-wired rules that cannot be handled by our algorithm.

		case opBretonM:
			skip = f != 0 || n == 0 || n%1000000 != 0

		case opAzerbaijan00s:
			// 100,200,300,400,500,600,700,800,900
			skip = n == 0 || n >= 1000 || n%100 != 0

		case opItalian800:
			skip = (f != 0 || n >= numN || nMask&setBit == 0) && n != 800
		}
		if skip {
			// advance over AND entries.
			for ; i < len(rules) && rules[i].cat&formMask == andNext; i++ {
			}
			continue
		}
		// return if we have a final entry.
		if cat := rule.cat & formMask; cat != andNext {
			return plural.Form(cat)
		}
	}
	return plural.Other
}