taldir

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

plural.go (7509B)


      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.
      4 
      5 //go:generate go run gen.go gen_common.go
      6 
      7 // Package plural provides utilities for handling linguistic plurals in text.
      8 //
      9 // The definitions in this package are based on the plural rule handling defined
     10 // in CLDR. See
     11 // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules for
     12 // details.
     13 package plural
     14 
     15 import (
     16 	"golang.org/x/text/internal/language/compact"
     17 	"golang.org/x/text/internal/number"
     18 	"golang.org/x/text/language"
     19 )
     20 
     21 // Rules defines the plural rules for all languages for a certain plural type.
     22 //
     23 // This package is UNDER CONSTRUCTION and its API may change.
     24 type Rules struct {
     25 	rules          []pluralCheck
     26 	index          []byte
     27 	langToIndex    []byte
     28 	inclusionMasks []uint64
     29 }
     30 
     31 var (
     32 	// Cardinal defines the plural rules for numbers indicating quantities.
     33 	Cardinal *Rules = cardinal
     34 
     35 	// Ordinal defines the plural rules for numbers indicating position
     36 	// (first, second, etc.).
     37 	Ordinal *Rules = ordinal
     38 
     39 	ordinal = &Rules{
     40 		ordinalRules,
     41 		ordinalIndex,
     42 		ordinalLangToIndex,
     43 		ordinalInclusionMasks[:],
     44 	}
     45 
     46 	cardinal = &Rules{
     47 		cardinalRules,
     48 		cardinalIndex,
     49 		cardinalLangToIndex,
     50 		cardinalInclusionMasks[:],
     51 	}
     52 )
     53 
     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
     57 //     to be zero.
     58 //   - Result n is big if i / 10^nMod > 1.
     59 //   - Otherwise the result is i % 10^nMod.
     60 //
     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.
     71 	p := start
     72 	if p < 0 {
     73 		p = 0
     74 	}
     75 	// Range only over the part for which we have digits.
     76 	mid := end
     77 	if mid >= len(digits) {
     78 		mid = len(digits)
     79 	}
     80 	// Check digits more significant that nMod.
     81 	if q := end - nMod; q > 0 {
     82 		if q > mid {
     83 			q = mid
     84 		}
     85 		for ; p < q; p++ {
     86 			if digits[p] != 0 {
     87 				return big
     88 			}
     89 		}
     90 	}
     91 	for ; p < mid; p++ {
     92 		n = 10*n + int(digits[p])
     93 	}
     94 	// Multiply for trailing zeros.
     95 	for ; p < end; p++ {
     96 		n *= 10
     97 	}
     98 	return n
     99 }
    100 
    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.
    106 //
    107 // The following table contains examples of possible arguments to represent
    108 // the given numbers.
    109 //
    110 //	decimal    digits              exp    scale
    111 //	123        []byte{1, 2, 3}     3      0
    112 //	123.4      []byte{1, 2, 3, 4}  3      1
    113 //	123.40     []byte{1, 2, 3, 4}  3      2
    114 //	100000     []byte{1}           6      0
    115 //	100000.00  []byte{1}           6      3
    116 func (p *Rules) MatchDigits(t language.Tag, digits []byte, exp, scale int) Form {
    117 	index := tagToID(t)
    118 
    119 	// Differentiate up to including mod 1000000 for the integer part.
    120 	n := getIntApprox(digits, 0, exp, 6, 1000000)
    121 
    122 	// Differentiate up to including mod 100 for the fractional part.
    123 	f := getIntApprox(digits, exp, exp+scale, 2, 100)
    124 
    125 	return matchPlural(p, index, n, f, scale)
    126 }
    127 
    128 func (p *Rules) matchDisplayDigits(t language.Tag, d *number.Digits) (Form, int) {
    129 	n := getIntApprox(d.Digits, 0, int(d.Exp), 6, 1000000)
    130 	return p.MatchDigits(t, d.Digits, int(d.Exp), d.NumFracDigits()), n
    131 }
    132 
    133 func validForms(p *Rules, t language.Tag) (forms []Form) {
    134 	offset := p.langToIndex[tagToID(t)]
    135 	rules := p.rules[p.index[offset]:p.index[offset+1]]
    136 
    137 	forms = append(forms, Other)
    138 	last := Other
    139 	for _, r := range rules {
    140 		if cat := Form(r.cat & formMask); cat != andNext && last != cat {
    141 			forms = append(forms, cat)
    142 			last = cat
    143 		}
    144 	}
    145 	return forms
    146 }
    147 
    148 func (p *Rules) matchComponents(t language.Tag, n, f, scale int) Form {
    149 	return matchPlural(p, tagToID(t), n, f, scale)
    150 }
    151 
    152 // MatchPlural returns the plural form for the given language and plural
    153 // operands (as defined in
    154 // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules):
    155 //
    156 //	where
    157 //		n  absolute value of the source number (integer and decimals)
    158 //	input
    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.
    164 //
    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 	return matchPlural(p, tagToID(lang), i, f, v)
    169 }
    170 
    171 func matchPlural(p *Rules, index compact.ID, n, f, v int) Form {
    172 	nMask := p.inclusionMasks[n%maxMod]
    173 	// Compute the fMask inline in the rules below, as it is relatively rare.
    174 	// fMask := p.inclusionMasks[f%maxMod]
    175 	vMask := p.inclusionMasks[v%maxMod]
    176 
    177 	// Do the matching
    178 	offset := p.langToIndex[index]
    179 	rules := p.rules[p.index[offset]:p.index[offset+1]]
    180 	for i := 0; i < len(rules); i++ {
    181 		rule := rules[i]
    182 		setBit := uint64(1 << rule.setID)
    183 		var skip bool
    184 		switch op := opID(rule.cat >> opShift); op {
    185 		case opI: // i = x
    186 			skip = n >= numN || nMask&setBit == 0
    187 
    188 		case opI | opNotEqual: // i != x
    189 			skip = n < numN && nMask&setBit != 0
    190 
    191 		case opI | opMod: // i % m = x
    192 			skip = nMask&setBit == 0
    193 
    194 		case opI | opMod | opNotEqual: // i % m != x
    195 			skip = nMask&setBit != 0
    196 
    197 		case opN: // n = x
    198 			skip = f != 0 || n >= numN || nMask&setBit == 0
    199 
    200 		case opN | opNotEqual: // n != x
    201 			skip = f == 0 && n < numN && nMask&setBit != 0
    202 
    203 		case opN | opMod: // n % m = x
    204 			skip = f != 0 || nMask&setBit == 0
    205 
    206 		case opN | opMod | opNotEqual: // n % m != x
    207 			skip = f == 0 && nMask&setBit != 0
    208 
    209 		case opF: // f = x
    210 			skip = f >= numN || p.inclusionMasks[f%maxMod]&setBit == 0
    211 
    212 		case opF | opNotEqual: // f != x
    213 			skip = f < numN && p.inclusionMasks[f%maxMod]&setBit != 0
    214 
    215 		case opF | opMod: // f % m = x
    216 			skip = p.inclusionMasks[f%maxMod]&setBit == 0
    217 
    218 		case opF | opMod | opNotEqual: // f % m != x
    219 			skip = p.inclusionMasks[f%maxMod]&setBit != 0
    220 
    221 		case opV: // v = x
    222 			skip = v < numN && vMask&setBit == 0
    223 
    224 		case opV | opNotEqual: // v != x
    225 			skip = v < numN && vMask&setBit != 0
    226 
    227 		case opW: // w == 0
    228 			skip = f != 0
    229 
    230 		case opW | opNotEqual: // w != 0
    231 			skip = f == 0
    232 
    233 		// Hard-wired rules that cannot be handled by our algorithm.
    234 
    235 		case opBretonM:
    236 			skip = f != 0 || n == 0 || n%1000000 != 0
    237 
    238 		case opAzerbaijan00s:
    239 			// 100,200,300,400,500,600,700,800,900
    240 			skip = n == 0 || n >= 1000 || n%100 != 0
    241 
    242 		case opItalian800:
    243 			skip = (f != 0 || n >= numN || nMask&setBit == 0) && n != 800
    244 		}
    245 		if skip {
    246 			// advance over AND entries.
    247 			for ; i < len(rules) && rules[i].cat&formMask == andNext; i++ {
    248 			}
    249 			continue
    250 		}
    251 		// return if we have a final entry.
    252 		if cat := rule.cat & formMask; cat != andNext {
    253 			return Form(cat)
    254 		}
    255 	}
    256 	return Other
    257 }
    258 
    259 func tagToID(t language.Tag) compact.ID {
    260 	id, _ := compact.RegionalID(compact.Tag(t))
    261 	return id
    262 }