github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/text/feature/plural/plural.go (about)

     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  }