// 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.

//go:generate go run gen.go gen_common.go

// Package plural provides utilities for handling linguistic plurals in text. // // The definitions in this package are based on the plural rule handling defined // in CLDR. See // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules for // details.
package plural import ( ) // Rules defines the plural rules for all languages for a certain plural type. // // This package is UNDER CONSTRUCTION and its API may change. type Rules struct { rules []pluralCheck index []byte langToIndex []byte inclusionMasks []uint64 } var ( // Cardinal defines the plural rules for numbers indicating quantities. Cardinal *Rules = cardinal // Ordinal defines the plural rules for numbers indicating position // (first, second, etc.). Ordinal *Rules = ordinal ordinal = &Rules{ ordinalRules, ordinalIndex, ordinalLangToIndex, ordinalInclusionMasks[:], } cardinal = &Rules{ cardinalRules, cardinalIndex, cardinalLangToIndex, cardinalInclusionMasks[:], } ) // getIntApprox converts the digits in slice digits[start:end] to an integer // according to the following rules: // - Let i be asInt(digits[start:end]), where out-of-range digits are assumed // to be zero. // - Result n is big if i / 10^nMod > 1. // - Otherwise the result is i % 10^nMod. // // For example, if digits is {1, 2, 3} and start:end is 0:5, then the result // for various values of nMod is: // - when nMod == 2, n == big // - when nMod == 3, n == big // - when nMod == 4, n == big // - when nMod == 5, n == 12300 // - when nMod == 6, n == 12300 // - when nMod == 7, n == 12300 func getIntApprox( []byte, , , , int) ( int) { // Leading 0 digits just result in 0. := if < 0 { = 0 } // Range only over the part for which we have digits. := if >= len() { = len() } // Check digits more significant that nMod. if := - ; > 0 { if > { = } for ; < ; ++ { if [] != 0 { return } } } for ; < ; ++ { = 10* + int([]) } // Multiply for trailing zeros. for ; < ; ++ { *= 10 } return } // MatchDigits computes the plural form for the given language and the given // decimal floating point digits. The digits are stored in big-endian order and // are of value byte(0) - byte(9). The floating point position is indicated by // exp and the number of visible decimals is scale. All leading and trailing // zeros may be omitted from digits. // // The following table contains examples of possible arguments to represent // the given numbers. // // decimal digits exp scale // 123 []byte{1, 2, 3} 3 0 // 123.4 []byte{1, 2, 3, 4} 3 1 // 123.40 []byte{1, 2, 3, 4} 3 2 // 100000 []byte{1} 6 0 // 100000.00 []byte{1} 6 3 func ( *Rules) ( language.Tag, []byte, , int) Form { := tagToID() // Differentiate up to including mod 1000000 for the integer part. := getIntApprox(, 0, , 6, 1000000) // Differentiate up to including mod 100 for the fractional part. := getIntApprox(, , +, 2, 100) return matchPlural(, , , , ) } func ( *Rules) ( language.Tag, *number.Digits) (Form, int) { := getIntApprox(.Digits, 0, int(.Exp), 6, 1000000) return .MatchDigits(, .Digits, int(.Exp), .NumFracDigits()), } func validForms( *Rules, language.Tag) ( []Form) { := .langToIndex[tagToID()] := .rules[.index[]:.index[+1]] = append(, Other) := Other for , := range { if := Form(.cat & formMask); != andNext && != { = append(, ) = } } return } func ( *Rules) ( language.Tag, , , int) Form { return matchPlural(, tagToID(), , , ) } // MatchPlural returns the plural form for the given language and plural // operands (as defined in // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules): // // where // n absolute value of the source number (integer and decimals) // input // i integer digits of n. // v number of visible fraction digits in n, with trailing zeros. // w number of visible fraction digits in n, without trailing zeros. // f visible fractional digits in n, with trailing zeros (f = t * 10^(v-w)) // t visible fractional digits in n, without trailing zeros. // // If any of the operand values is too large to fit in an int, it is okay to // pass the value modulo 10,000,000. func ( *Rules) ( language.Tag, , , , , int) Form { return matchPlural(, tagToID(), , , ) } func matchPlural( *Rules, compact.ID, , , int) Form { := .inclusionMasks[%maxMod] // Compute the fMask inline in the rules below, as it is relatively rare. // fMask := p.inclusionMasks[f%maxMod] := .inclusionMasks[%maxMod] // Do the matching := .langToIndex[] := .rules[.index[]:.index[+1]] for := 0; < len(); ++ { := [] := uint64(1 << .setID) var bool switch := opID(.cat >> opShift); { case opI: // i = x = >= numN || & == 0 case opI | opNotEqual: // i != x = < numN && & != 0 case opI | opMod: // i % m = x = & == 0 case opI | opMod | opNotEqual: // i % m != x = & != 0 case opN: // n = x = != 0 || >= numN || & == 0 case opN | opNotEqual: // n != x = == 0 && < numN && & != 0 case opN | opMod: // n % m = x = != 0 || & == 0 case opN | opMod | opNotEqual: // n % m != x = == 0 && & != 0 case opF: // f = x = >= numN || .inclusionMasks[%maxMod]& == 0 case opF | opNotEqual: // f != x = < numN && .inclusionMasks[%maxMod]& != 0 case opF | opMod: // f % m = x = .inclusionMasks[%maxMod]& == 0 case opF | opMod | opNotEqual: // f % m != x = .inclusionMasks[%maxMod]& != 0 case opV: // v = x = < numN && & == 0 case opV | opNotEqual: // v != x = < numN && & != 0 case opW: // w == 0 = != 0 case opW | opNotEqual: // w != 0 = == 0 // Hard-wired rules that cannot be handled by our algorithm. case opBretonM: = != 0 || == 0 || %1000000 != 0 case opAzerbaijan00s: // 100,200,300,400,500,600,700,800,900 = == 0 || >= 1000 || %100 != 0 case opItalian800: = ( != 0 || >= numN || & == 0) && != 800 } if { // advance over AND entries. for ; < len() && [].cat&formMask == andNext; ++ { } continue } // return if we have a final entry. if := .cat & formMask; != andNext { return Form() } } return Other } func tagToID( language.Tag) compact.ID { , := compact.RegionalID(compact.Tag()) return }