tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

plurrule.cpp (65700B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 2007-2016, International Business Machines Corporation and
      6 * others. All Rights Reserved.
      7 *******************************************************************************
      8 *
      9 * File plurrule.cpp
     10 */
     11 
     12 #include <math.h>
     13 #include <stdio.h>
     14 
     15 #include <utility>
     16 
     17 #include "unicode/utypes.h"
     18 #include "unicode/localpointer.h"
     19 #include "unicode/plurrule.h"
     20 #include "unicode/upluralrules.h"
     21 #include "unicode/ures.h"
     22 #include "unicode/numfmt.h"
     23 #include "unicode/decimfmt.h"
     24 #include "unicode/numberrangeformatter.h"
     25 #include "charstr.h"
     26 #include "cmemory.h"
     27 #include "cstring.h"
     28 #include "hash.h"
     29 #include "locutil.h"
     30 #include "mutex.h"
     31 #include "number_decnum.h"
     32 #include "patternprops.h"
     33 #include "plurrule_impl.h"
     34 #include "putilimp.h"
     35 #include "ucln_in.h"
     36 #include "ustrfmt.h"
     37 #include "uassert.h"
     38 #include "uvectr32.h"
     39 #include "sharedpluralrules.h"
     40 #include "unifiedcache.h"
     41 #include "number_decimalquantity.h"
     42 #include "util.h"
     43 #include "pluralranges.h"
     44 #include "numrange_impl.h"
     45 #include "ulocimp.h"
     46 
     47 #if !UCONFIG_NO_FORMATTING
     48 
     49 U_NAMESPACE_BEGIN
     50 
     51 using namespace icu::pluralimpl;
     52 using icu::number::impl::DecNum;
     53 using icu::number::impl::DecimalQuantity;
     54 using icu::number::impl::RoundingMode;
     55 
     56 static const char16_t PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0};
     57 static const char16_t PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0};
     58 static const char16_t PK_IN[]={LOW_I,LOW_N,0};
     59 static const char16_t PK_NOT[]={LOW_N,LOW_O,LOW_T,0};
     60 static const char16_t PK_IS[]={LOW_I,LOW_S,0};
     61 static const char16_t PK_MOD[]={LOW_M,LOW_O,LOW_D,0};
     62 static const char16_t PK_AND[]={LOW_A,LOW_N,LOW_D,0};
     63 static const char16_t PK_OR[]={LOW_O,LOW_R,0};
     64 static const char16_t PK_VAR_N[]={LOW_N,0};
     65 static const char16_t PK_VAR_I[]={LOW_I,0};
     66 static const char16_t PK_VAR_F[]={LOW_F,0};
     67 static const char16_t PK_VAR_T[]={LOW_T,0};
     68 static const char16_t PK_VAR_E[]={LOW_E,0};
     69 static const char16_t PK_VAR_C[]={LOW_C,0};
     70 static const char16_t PK_VAR_V[]={LOW_V,0};
     71 static const char16_t PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
     72 static const char16_t PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0};
     73 static const char16_t PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0};
     74 
     75 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules)
     76 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
     77 
     78 PluralRules::PluralRules(UErrorCode& /*status*/)
     79 :   UObject(),
     80    mRules(nullptr),
     81    mStandardPluralRanges(nullptr),
     82    mInternalStatus(U_ZERO_ERROR)
     83 {
     84 }
     85 
     86 PluralRules::PluralRules(const PluralRules& other)
     87 : UObject(other),
     88    mRules(nullptr),
     89    mStandardPluralRanges(nullptr),
     90    mInternalStatus(U_ZERO_ERROR)
     91 {
     92    *this=other;
     93 }
     94 
     95 PluralRules::~PluralRules() {
     96    delete mRules;
     97    delete mStandardPluralRanges;
     98 }
     99 
    100 SharedPluralRules::~SharedPluralRules() {
    101    delete ptr;
    102 }
    103 
    104 PluralRules*
    105 PluralRules::clone() const {
    106    // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if
    107    // the newly created object was not fully constructed properly (an error occurred).
    108    UErrorCode localStatus = U_ZERO_ERROR;
    109    return clone(localStatus);
    110 }
    111 
    112 PluralRules*
    113 PluralRules::clone(UErrorCode& status) const {
    114    LocalPointer<PluralRules> newObj(new PluralRules(*this), status);
    115    if (U_SUCCESS(status) && U_FAILURE(newObj->mInternalStatus)) {
    116        status = newObj->mInternalStatus;
    117        newObj.adoptInstead(nullptr);
    118    }
    119    return newObj.orphan();
    120 }
    121 
    122 PluralRules&
    123 PluralRules::operator=(const PluralRules& other) {
    124    if (this != &other) {
    125        delete mRules;
    126        mRules = nullptr;
    127        delete mStandardPluralRanges;
    128        mStandardPluralRanges = nullptr;
    129        mInternalStatus = other.mInternalStatus;
    130        if (U_FAILURE(mInternalStatus)) {
    131            // bail out early if the object we were copying from was already 'invalid'.
    132            return *this;
    133        }
    134        if (other.mRules != nullptr) {
    135            mRules = new RuleChain(*other.mRules);
    136            if (mRules == nullptr) {
    137                mInternalStatus = U_MEMORY_ALLOCATION_ERROR;
    138            }
    139            else if (U_FAILURE(mRules->fInternalStatus)) {
    140                // If the RuleChain wasn't fully copied, then set our status to failure as well.
    141                mInternalStatus = mRules->fInternalStatus;
    142            }
    143        }
    144        if (other.mStandardPluralRanges != nullptr) {
    145            mStandardPluralRanges = other.mStandardPluralRanges->copy(mInternalStatus)
    146                .toPointer(mInternalStatus)
    147                .orphan();
    148        }
    149    }
    150    return *this;
    151 }
    152 
    153 StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) {
    154    if (U_FAILURE(status)) {
    155        return nullptr;
    156    }
    157    LocalPointer<StringEnumeration> result(new PluralAvailableLocalesEnumeration(status), status);
    158    if (U_FAILURE(status)) {
    159        return nullptr;
    160    }
    161    return result.orphan();
    162 }
    163 
    164 
    165 PluralRules* U_EXPORT2
    166 PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
    167    if (U_FAILURE(status)) {
    168        return nullptr;
    169    }
    170    PluralRuleParser parser;
    171    LocalPointer<PluralRules> newRules(new PluralRules(status), status);
    172    if (U_FAILURE(status)) {
    173        return nullptr;
    174    }
    175    parser.parse(description, newRules.getAlias(), status);
    176    if (U_FAILURE(status)) {
    177        newRules.adoptInstead(nullptr);
    178    }
    179    return newRules.orphan();
    180 }
    181 
    182 
    183 PluralRules* U_EXPORT2
    184 PluralRules::createDefaultRules(UErrorCode& status) {
    185    return createRules(UnicodeString(true, PLURAL_DEFAULT_RULE, -1), status);
    186 }
    187 
    188 /******************************************************************************/
    189 /* Create PluralRules cache */
    190 
    191 template<> U_I18N_API
    192 const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject(
    193        const void * /*unused*/, UErrorCode &status) const {
    194    const char *localeId = fLoc.getName();
    195    LocalPointer<PluralRules> pr(PluralRules::internalForLocale(localeId, UPLURAL_TYPE_CARDINAL, status), status);
    196    if (U_FAILURE(status)) {
    197        return nullptr;
    198    }
    199    LocalPointer<SharedPluralRules> result(new SharedPluralRules(pr.getAlias()), status);
    200    if (U_FAILURE(status)) {
    201        return nullptr;
    202    }
    203    pr.orphan(); // result was successfully created so it nows pr.
    204    result->addRef();
    205    return result.orphan();
    206 }
    207 
    208 /* end plural rules cache */
    209 /******************************************************************************/
    210 
    211 const SharedPluralRules* U_EXPORT2
    212 PluralRules::createSharedInstance(
    213        const Locale& locale, UPluralType type, UErrorCode& status) {
    214    if (U_FAILURE(status)) {
    215        return nullptr;
    216    }
    217    if (type != UPLURAL_TYPE_CARDINAL) {
    218        status = U_UNSUPPORTED_ERROR;
    219        return nullptr;
    220    }
    221    const SharedPluralRules *result = nullptr;
    222    UnifiedCache::getByLocale(locale, result, status);
    223    return result;
    224 }
    225 
    226 PluralRules* U_EXPORT2
    227 PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
    228    return forLocale(locale, UPLURAL_TYPE_CARDINAL, status);
    229 }
    230 
    231 PluralRules* U_EXPORT2
    232 PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
    233    if (type != UPLURAL_TYPE_CARDINAL) {
    234        return internalForLocale(locale, type, status);
    235    }
    236    const SharedPluralRules *shared = createSharedInstance(
    237            locale, type, status);
    238    if (U_FAILURE(status)) {
    239        return nullptr;
    240    }
    241    PluralRules *result = (*shared)->clone(status);
    242    shared->removeRef();
    243    return result;
    244 }
    245 
    246 PluralRules* U_EXPORT2
    247 PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
    248    if (U_FAILURE(status)) {
    249        return nullptr;
    250    }
    251    if (type >= UPLURAL_TYPE_COUNT) {
    252        status = U_ILLEGAL_ARGUMENT_ERROR;
    253        return nullptr;
    254    }
    255    LocalPointer<PluralRules> newObj(new PluralRules(status), status);
    256    if (U_FAILURE(status)) {
    257        return nullptr;
    258    }
    259    UnicodeString locRule = newObj->getRuleFromResource(locale, type, status);
    260    // TODO: which other errors, if any, should be returned?
    261    if (locRule.length() == 0) {
    262        // If an out-of-memory error occurred, then stop and report the failure.
    263        if (status == U_MEMORY_ALLOCATION_ERROR) {
    264            return nullptr;
    265        }
    266        // Locales with no specific rules (all numbers have the "other" category
    267        //   will return a U_MISSING_RESOURCE_ERROR at this point. This is not
    268        //   an error.
    269        locRule =  UnicodeString(PLURAL_DEFAULT_RULE);
    270        status = U_ZERO_ERROR;
    271    }
    272    PluralRuleParser parser;
    273    parser.parse(locRule, newObj.getAlias(), status);
    274        //  TODO: should rule parse errors be returned, or
    275        //        should we silently use default rules?
    276        //        Original impl used default rules.
    277        //        Ask the question to ICU Core.
    278 
    279    newObj->mStandardPluralRanges = StandardPluralRanges::forLocale(locale, status)
    280        .toPointer(status)
    281        .orphan();
    282 
    283    return newObj.orphan();
    284 }
    285 
    286 UnicodeString
    287 PluralRules::select(int32_t number) const {
    288    return select(FixedDecimal(number));
    289 }
    290 
    291 UnicodeString
    292 PluralRules::select(double number) const {
    293    return select(FixedDecimal(number));
    294 }
    295 
    296 UnicodeString
    297 PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) const {
    298    DecimalQuantity dq;
    299    number.getDecimalQuantity(dq, status);
    300    if (U_FAILURE(status)) {
    301        return ICU_Utility::makeBogusString();
    302    }
    303    if (U_FAILURE(mInternalStatus)) {
    304        status = mInternalStatus;
    305        return ICU_Utility::makeBogusString();
    306    }
    307    return select(dq);
    308 }
    309 
    310 UnicodeString
    311 PluralRules::select(const IFixedDecimal &number) const {
    312    if (mRules == nullptr) {
    313        return UnicodeString(true, PLURAL_DEFAULT_RULE, -1);
    314    }
    315    else {
    316        return mRules->select(number);
    317    }
    318 }
    319 
    320 UnicodeString
    321 PluralRules::select(const number::FormattedNumberRange& range, UErrorCode& status) const {
    322    return select(range.getData(status), status);
    323 }
    324 
    325 UnicodeString
    326 PluralRules::select(const number::impl::UFormattedNumberRangeData* impl, UErrorCode& status) const {
    327    if (U_FAILURE(status)) {
    328        return ICU_Utility::makeBogusString();
    329    }
    330    if (U_FAILURE(mInternalStatus)) {
    331        status = mInternalStatus;
    332        return ICU_Utility::makeBogusString();
    333    }
    334    if (mStandardPluralRanges == nullptr) {
    335        // Happens if PluralRules was constructed via createRules()
    336        status = U_UNSUPPORTED_ERROR;
    337        return ICU_Utility::makeBogusString();
    338    }
    339    auto form1 = StandardPlural::fromString(select(impl->quantity1), status);
    340    auto form2 = StandardPlural::fromString(select(impl->quantity2), status);
    341    if (U_FAILURE(status)) {
    342        return ICU_Utility::makeBogusString();
    343    }
    344    auto result = mStandardPluralRanges->resolve(form1, form2);
    345    return UnicodeString(StandardPlural::getKeyword(result), -1, US_INV);
    346 }
    347 
    348 
    349 StringEnumeration*
    350 PluralRules::getKeywords(UErrorCode& status) const {
    351    if (U_FAILURE(status)) {
    352        return nullptr;
    353    }
    354    if (U_FAILURE(mInternalStatus)) {
    355        status = mInternalStatus;
    356        return nullptr;
    357    }
    358    LocalPointer<StringEnumeration> nameEnumerator(new PluralKeywordEnumeration(mRules, status), status);
    359    if (U_FAILURE(status)) {
    360        return nullptr;
    361    }
    362    return nameEnumerator.orphan();
    363 }
    364 
    365 double
    366 PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) {
    367  // Not Implemented.
    368  return UPLRULES_NO_UNIQUE_VALUE;
    369 }
    370 
    371 int32_t
    372 PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */,
    373                                 int32_t /* destCapacity */, UErrorCode& error) {
    374    error = U_UNSUPPORTED_ERROR;
    375    return 0;
    376 }
    377 
    378 /**
    379 * Helper method for the overrides of getSamples() for double and DecimalQuantity
    380 * return value types.  Provide only one of an allocated array of double or
    381 * DecimalQuantity, and a nullptr for the other.
    382 */
    383 static int32_t
    384 getSamplesFromString(const UnicodeString &samples, double *destDbl,
    385                        DecimalQuantity* destDq, int32_t destCapacity,
    386                        UErrorCode& status) {
    387 
    388    if ((destDbl == nullptr && destDq == nullptr)
    389            || (destDbl != nullptr && destDq != nullptr)) {
    390        status = U_INTERNAL_PROGRAM_ERROR;
    391        return 0;
    392    }
    393 
    394    bool isDouble = destDbl != nullptr;
    395    int32_t sampleCount = 0;
    396    int32_t sampleStartIdx = 0;
    397    int32_t sampleEndIdx = 0;
    398 
    399    //std::string ss;  // TODO: debugging.
    400    // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n";
    401    for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) {
    402        sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx);
    403        if (sampleEndIdx == -1) {
    404            sampleEndIdx = samples.length();
    405        }
    406        const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx);
    407        // ss.erase();
    408        // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n";
    409        int32_t tildeIndex = sampleRange.indexOf(TILDE);
    410        if (tildeIndex < 0) {
    411            DecimalQuantity dq = DecimalQuantity::fromExponentString(sampleRange, status);
    412            if (isDouble) {
    413                // See warning note below about lack of precision for floating point samples for numbers with
    414                // trailing zeroes in the decimal fraction representation.
    415                double dblValue = dq.toDouble();
    416                if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) {
    417                    destDbl[sampleCount++] = dblValue;
    418                }
    419            } else {
    420                destDq[sampleCount++] = dq;
    421            }
    422        } else {
    423            DecimalQuantity rangeLo =
    424                DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(0, tildeIndex), status);
    425            DecimalQuantity rangeHi = DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(tildeIndex+1), status);
    426            if (U_FAILURE(status)) {
    427                break;
    428            }
    429            if (rangeHi.toDouble() < rangeLo.toDouble()) {
    430                status = U_INVALID_FORMAT_ERROR;
    431                break;
    432            }
    433 
    434            DecimalQuantity incrementDq;
    435            incrementDq.setToInt(1);
    436            int32_t lowerDispMag = rangeLo.getLowerDisplayMagnitude();
    437            int32_t exponent = rangeLo.getExponent();
    438            int32_t incrementScale = lowerDispMag + exponent;
    439            incrementDq.adjustMagnitude(incrementScale);
    440            double incrementVal = incrementDq.toDouble();  // 10 ^ incrementScale
    441            
    442 
    443            DecimalQuantity dq(rangeLo);
    444            double dblValue = dq.toDouble();
    445            double end = rangeHi.toDouble();
    446 
    447            while (dblValue <= end) {
    448                if (isDouble) {
    449                    // Hack Alert: don't return any decimal samples with integer values that
    450                    //    originated from a format with trailing decimals.
    451                    //    This API is returning doubles, which can't distinguish having displayed
    452                    //    zeros to the right of the decimal.
    453                    //    This results in test failures with values mapping back to a different keyword.
    454                    if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) {
    455                        destDbl[sampleCount++] = dblValue;
    456                    }
    457                } else {
    458                    destDq[sampleCount++] = dq;
    459                }
    460                if (sampleCount >= destCapacity) {
    461                    break;
    462                }
    463 
    464                // Increment dq for next iteration
    465 
    466                // Because DecNum and DecimalQuantity do not support
    467                // add operations, we need to convert to/from double,
    468                // despite precision lossiness for decimal fractions like 0.1.
    469                dblValue += incrementVal;
    470                DecNum newDqDecNum;
    471                newDqDecNum.setTo(dblValue, status);
    472                DecimalQuantity newDq;             
    473                newDq.setToDecNum(newDqDecNum, status);
    474                newDq.setMinFraction(-lowerDispMag);
    475                newDq.roundToMagnitude(lowerDispMag, RoundingMode::UNUM_ROUND_HALFEVEN, status);
    476                newDq.adjustMagnitude(-exponent);
    477                newDq.adjustExponent(exponent);
    478                dblValue = newDq.toDouble();
    479                dq = newDq;
    480            }
    481        }
    482        sampleStartIdx = sampleEndIdx + 1;
    483    }
    484    return sampleCount;
    485 }
    486 
    487 int32_t
    488 PluralRules::getSamples(const UnicodeString &keyword, double *dest,
    489                        int32_t destCapacity, UErrorCode& status) {
    490    if (U_FAILURE(status)) {
    491        return 0;
    492    }
    493    if (U_FAILURE(mInternalStatus)) {
    494        status = mInternalStatus;
    495        return 0;
    496    }
    497    if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) {
    498        status = U_ILLEGAL_ARGUMENT_ERROR;
    499        return 0;
    500    }
    501    RuleChain *rc = rulesForKeyword(keyword);
    502    if (rc == nullptr) {
    503        return 0;
    504    }
    505    int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, nullptr, destCapacity, status);
    506    if (numSamples == 0) {
    507        numSamples = getSamplesFromString(rc->fDecimalSamples, dest, nullptr, destCapacity, status);
    508    }
    509    return numSamples;
    510 }
    511 
    512 int32_t
    513 PluralRules::getSamples(const UnicodeString &keyword, DecimalQuantity *dest,
    514                        int32_t destCapacity, UErrorCode& status) {
    515    if (U_FAILURE(status)) {
    516        return 0;
    517    }
    518    if (U_FAILURE(mInternalStatus)) {
    519        status = mInternalStatus;
    520        return 0;
    521    }
    522    if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) {
    523        status = U_ILLEGAL_ARGUMENT_ERROR;
    524        return 0;
    525    }
    526    RuleChain *rc = rulesForKeyword(keyword);
    527    if (rc == nullptr) {
    528        return 0;
    529    }
    530 
    531    int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, nullptr, dest, destCapacity, status);
    532    if (numSamples == 0) {
    533        numSamples = getSamplesFromString(rc->fDecimalSamples, nullptr, dest, destCapacity, status);
    534    }
    535    return numSamples;
    536 }
    537 
    538 
    539 RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const {
    540    RuleChain *rc;
    541    for (rc = mRules; rc != nullptr; rc = rc->fNext) {
    542        if (rc->fKeyword == keyword) {
    543            break;
    544        }
    545    }
    546    return rc;
    547 }
    548 
    549 
    550 UBool
    551 PluralRules::isKeyword(const UnicodeString& keyword) const {
    552    if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
    553        return true;
    554    }
    555    return rulesForKeyword(keyword) != nullptr;
    556 }
    557 
    558 UnicodeString
    559 PluralRules::getKeywordOther() const {
    560    return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5);
    561 }
    562 
    563 bool
    564 PluralRules::operator==(const PluralRules& other) const  {
    565    const UnicodeString *ptrKeyword;
    566    UErrorCode status= U_ZERO_ERROR;
    567 
    568    if ( this == &other ) {
    569        return true;
    570    }
    571    LocalPointer<StringEnumeration> myKeywordList(getKeywords(status));
    572    LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status));
    573    if (U_FAILURE(status)) {
    574        return false;
    575    }
    576 
    577    if (myKeywordList->count(status)!=otherKeywordList->count(status)) {
    578        return false;
    579    }
    580    myKeywordList->reset(status);
    581    while ((ptrKeyword=myKeywordList->snext(status))!=nullptr) {
    582        if (!other.isKeyword(*ptrKeyword)) {
    583            return false;
    584        }
    585    }
    586    otherKeywordList->reset(status);
    587    while ((ptrKeyword=otherKeywordList->snext(status))!=nullptr) {
    588        if (!this->isKeyword(*ptrKeyword)) {
    589            return false;
    590        }
    591    }
    592    if (U_FAILURE(status)) {
    593        return false;
    594    }
    595 
    596    return true;
    597 }
    598 
    599 
    600 void
    601 PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status)
    602 {
    603    if (U_FAILURE(status)) {
    604        return;
    605    }
    606    U_ASSERT(ruleIndex == 0);    // Parsers are good for a single use only!
    607    ruleSrc = &ruleData;
    608 
    609    while (ruleIndex< ruleSrc->length()) {
    610        getNextToken(status);
    611        if (U_FAILURE(status)) {
    612            return;
    613        }
    614        checkSyntax(status);
    615        if (U_FAILURE(status)) {
    616            return;
    617        }
    618        switch (type) {
    619        case tAnd:
    620            U_ASSERT(curAndConstraint != nullptr);
    621            curAndConstraint = curAndConstraint->add(status);
    622            break;
    623        case tOr:
    624            {
    625                U_ASSERT(currentChain != nullptr);
    626                OrConstraint *orNode=currentChain->ruleHeader;
    627                while (orNode->next != nullptr) {
    628                    orNode = orNode->next;
    629                }
    630                orNode->next= new OrConstraint();
    631                if (orNode->next == nullptr) {
    632                    status = U_MEMORY_ALLOCATION_ERROR;
    633                    break;
    634                }
    635                orNode=orNode->next;
    636                orNode->next=nullptr;
    637                curAndConstraint = orNode->add(status);
    638            }
    639            break;
    640        case tIs:
    641            U_ASSERT(curAndConstraint != nullptr);
    642            U_ASSERT(curAndConstraint->value == -1);
    643            U_ASSERT(curAndConstraint->rangeList == nullptr);
    644            break;
    645        case tNot:
    646            U_ASSERT(curAndConstraint != nullptr);
    647            curAndConstraint->negated=true;
    648            break;
    649 
    650        case tNotEqual:
    651            curAndConstraint->negated=true;
    652            U_FALLTHROUGH;
    653        case tIn:
    654        case tWithin:
    655        case tEqual:
    656            {
    657                U_ASSERT(curAndConstraint != nullptr);
    658                if (curAndConstraint->rangeList != nullptr) {
    659                    // Already get a '='.
    660                    status = U_UNEXPECTED_TOKEN;
    661                    break;
    662                }
    663                LocalPointer<UVector32> newRangeList(new UVector32(status), status);
    664                if (U_FAILURE(status)) {
    665                    break;
    666                }
    667                curAndConstraint->rangeList = newRangeList.orphan();
    668                curAndConstraint->rangeList->addElement(-1, status);  // range Low
    669                curAndConstraint->rangeList->addElement(-1, status);  // range Hi
    670                rangeLowIdx = 0;
    671                rangeHiIdx  = 1;
    672                curAndConstraint->value=PLURAL_RANGE_HIGH;
    673                curAndConstraint->integerOnly = (type != tWithin);
    674            }
    675            break;
    676        case tNumber:
    677            U_ASSERT(curAndConstraint != nullptr);
    678            if ( (curAndConstraint->op==AndConstraint::MOD)&&
    679                 (curAndConstraint->opNum == -1 ) ) {
    680                int32_t num = getNumberValue(token);
    681                if (num == -1) {
    682                    status = U_UNEXPECTED_TOKEN;
    683                    break;
    684                }
    685                curAndConstraint->opNum=num;
    686            }
    687            else {
    688                if (curAndConstraint->rangeList == nullptr) {
    689                    // this is for an 'is' rule
    690                    int32_t num = getNumberValue(token);
    691                    if (num == -1) {
    692                        status = U_UNEXPECTED_TOKEN;
    693                        break;
    694                    }
    695                    curAndConstraint->value = num;
    696                } else {
    697                    // this is for an 'in' or 'within' rule
    698                    if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) {
    699                        int32_t num = getNumberValue(token);
    700                        if (num == -1) {
    701                            status = U_UNEXPECTED_TOKEN;
    702                            break;
    703                        }
    704                        curAndConstraint->rangeList->setElementAt(num, rangeLowIdx);
    705                        curAndConstraint->rangeList->setElementAt(num, rangeHiIdx);
    706                    }
    707                    else {
    708                        int32_t num = getNumberValue(token);
    709                        if (num == -1) {
    710                            status = U_UNEXPECTED_TOKEN;
    711                            break;
    712                        }
    713                        curAndConstraint->rangeList->setElementAt(num, rangeHiIdx);
    714                        if (curAndConstraint->rangeList->elementAti(rangeLowIdx) >
    715                                curAndConstraint->rangeList->elementAti(rangeHiIdx)) {
    716                            // Range Lower bound > Range Upper bound.
    717                            // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently
    718                            // used for all plural rule parse errors.
    719                            status = U_UNEXPECTED_TOKEN;
    720                            break;
    721                        }
    722                    }
    723                }
    724            }
    725            break;
    726        case tComma:
    727            // TODO: rule syntax checking is inadequate, can happen with badly formed rules.
    728            //       Catch cases like "n mod 10, is 1" here instead.
    729            if (curAndConstraint == nullptr || curAndConstraint->rangeList == nullptr) {
    730                status = U_UNEXPECTED_TOKEN;
    731                break;
    732            }
    733            U_ASSERT(curAndConstraint->rangeList->size() >= 2);
    734            rangeLowIdx = curAndConstraint->rangeList->size();
    735            curAndConstraint->rangeList->addElement(-1, status);  // range Low
    736            rangeHiIdx = curAndConstraint->rangeList->size();
    737            curAndConstraint->rangeList->addElement(-1, status);  // range Hi
    738            break;
    739        case tMod:
    740            U_ASSERT(curAndConstraint != nullptr);
    741            curAndConstraint->op=AndConstraint::MOD;
    742            break;
    743        case tVariableN:
    744        case tVariableI:
    745        case tVariableF:
    746        case tVariableT:
    747        case tVariableE:
    748        case tVariableC:
    749        case tVariableV:
    750            U_ASSERT(curAndConstraint != nullptr);
    751            curAndConstraint->digitsType = type;
    752            break;
    753        case tKeyword:
    754            {
    755            RuleChain *newChain = new RuleChain;
    756            if (newChain == nullptr) {
    757                status = U_MEMORY_ALLOCATION_ERROR;
    758                break;
    759            }
    760            newChain->fKeyword = token;
    761            if (prules->mRules == nullptr) {
    762                prules->mRules = newChain;
    763            } else {
    764                // The new rule chain goes at the end of the linked list of rule chains,
    765                //   unless there is an "other" keyword & chain. "other" must remain last.
    766                RuleChain *insertAfter = prules->mRules;
    767                while (insertAfter->fNext!=nullptr &&
    768                       insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){
    769                    insertAfter=insertAfter->fNext;
    770                }
    771                newChain->fNext = insertAfter->fNext;
    772                insertAfter->fNext = newChain;
    773            }
    774            OrConstraint *orNode = new OrConstraint();
    775            if (orNode == nullptr) {
    776                status = U_MEMORY_ALLOCATION_ERROR;
    777                break;
    778            }
    779            newChain->ruleHeader = orNode;
    780            curAndConstraint = orNode->add(status);
    781            currentChain = newChain;
    782            }
    783            break;
    784 
    785        case tInteger:
    786            for (;;) {
    787                getNextToken(status);
    788                if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) {
    789                    break;
    790                }
    791                if (type == tEllipsis) {
    792                    currentChain->fIntegerSamplesUnbounded = true;
    793                    continue;
    794                }
    795                currentChain->fIntegerSamples.append(token);
    796            }
    797            break;
    798 
    799        case tDecimal:
    800            for (;;) {
    801                getNextToken(status);
    802                if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) {
    803                    break;
    804                }
    805                if (type == tEllipsis) {
    806                    currentChain->fDecimalSamplesUnbounded = true;
    807                    continue;
    808                }
    809                currentChain->fDecimalSamples.append(token);
    810            }
    811            break;
    812 
    813        default:
    814            break;
    815        }
    816        prevType=type;
    817        if (U_FAILURE(status)) {
    818            break;
    819        }
    820    }
    821 }
    822 
    823 UnicodeString
    824 PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) {
    825    UnicodeString emptyStr;
    826 
    827    if (U_FAILURE(errCode)) {
    828        return emptyStr;
    829    }
    830    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &errCode));
    831    if(U_FAILURE(errCode)) {
    832        return emptyStr;
    833    }
    834    const char *typeKey;
    835    switch (type) {
    836    case UPLURAL_TYPE_CARDINAL:
    837        typeKey = "locales";
    838        break;
    839    case UPLURAL_TYPE_ORDINAL:
    840        typeKey = "locales_ordinals";
    841        break;
    842    default:
    843        // Must not occur: The caller should have checked for valid types.
    844        errCode = U_ILLEGAL_ARGUMENT_ERROR;
    845        return emptyStr;
    846    }
    847    LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, nullptr, &errCode));
    848    if(U_FAILURE(errCode)) {
    849        return emptyStr;
    850    }
    851    int32_t resLen=0;
    852    const char *curLocaleName=locale.getBaseName();
    853    const char16_t* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode);
    854 
    855    if (s == nullptr) {
    856        // Check parent locales.
    857        UErrorCode status = U_ZERO_ERROR;
    858        const char *curLocaleName2=locale.getBaseName();
    859        CharString parentLocaleName(curLocaleName2, status);
    860 
    861        for (;;) {
    862            {
    863                CharString tmp = ulocimp_getParent(parentLocaleName.data(), status);
    864                if (tmp.isEmpty()) break;
    865                parentLocaleName = std::move(tmp);
    866            }
    867            resLen=0;
    868            s = ures_getStringByKey(locRes.getAlias(), parentLocaleName.data(), &resLen, &status);
    869            if (s != nullptr) {
    870                errCode = U_ZERO_ERROR;
    871                break;
    872            }
    873            status = U_ZERO_ERROR;
    874        }
    875    }
    876    if (s==nullptr) {
    877        return emptyStr;
    878    }
    879 
    880    char setKey[256];
    881    u_UCharsToChars(s, setKey, resLen + 1);
    882    // printf("\n PluralRule: %s\n", setKey);
    883 
    884    LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", nullptr, &errCode));
    885    if(U_FAILURE(errCode)) {
    886        return emptyStr;
    887    }
    888    LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, nullptr, &errCode));
    889    if (U_FAILURE(errCode)) {
    890        return emptyStr;
    891    }
    892 
    893    int32_t numberKeys = ures_getSize(setRes.getAlias());
    894    UnicodeString result;
    895    const char *key=nullptr;
    896    for(int32_t i=0; i<numberKeys; ++i) {   // Keys are zero, one, few, ...
    897        UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode);
    898        UnicodeString uKey(key, -1, US_INV);
    899        result.append(uKey);
    900        result.append(COLON);
    901        result.append(rules);
    902        result.append(SEMI_COLON);
    903    }
    904    return result;
    905 }
    906 
    907 
    908 UnicodeString
    909 PluralRules::getRules() const {
    910    UnicodeString rules;
    911    if (mRules != nullptr) {
    912        mRules->dumpRules(rules);
    913    }
    914    return rules;
    915 }
    916 
    917 AndConstraint::AndConstraint(const AndConstraint& other) {
    918    this->fInternalStatus = other.fInternalStatus;
    919    if (U_FAILURE(fInternalStatus)) {
    920        return; // stop early if the object we are copying from is invalid.
    921    }
    922    this->op = other.op;
    923    this->opNum=other.opNum;
    924    this->value=other.value;
    925    if (other.rangeList != nullptr) {
    926        LocalPointer<UVector32> newRangeList(new UVector32(fInternalStatus), fInternalStatus);
    927        if (U_FAILURE(fInternalStatus)) {
    928            return;
    929        }
    930        this->rangeList = newRangeList.orphan();
    931        this->rangeList->assign(*other.rangeList, fInternalStatus);
    932    }
    933    this->integerOnly=other.integerOnly;
    934    this->negated=other.negated;
    935    this->digitsType = other.digitsType;
    936    if (other.next != nullptr) {
    937        this->next = new AndConstraint(*other.next);
    938        if (this->next == nullptr) {
    939            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
    940        }
    941    }
    942 }
    943 
    944 AndConstraint::~AndConstraint() {
    945    delete rangeList;
    946    rangeList = nullptr;
    947    delete next;
    948    next = nullptr;
    949 }
    950 
    951 UBool
    952 AndConstraint::isFulfilled(const IFixedDecimal &number) {
    953    UBool result = true;
    954    if (digitsType == none) {
    955        // An empty AndConstraint, created by a rule with a keyword but no following expression.
    956        return true;
    957    }
    958 
    959    PluralOperand operand = tokenTypeToPluralOperand(digitsType);
    960    double n = number.getPluralOperand(operand);     // pulls n | i | v | f value for the number.
    961                                                     // Will always be positive.
    962                                                     // May be non-integer (n option only)
    963    do {
    964        if (integerOnly && n != uprv_floor(n)) {
    965            result = false;
    966            break;
    967        }
    968 
    969        if (op == MOD) {
    970            n = fmod(n, opNum);
    971        }
    972        if (rangeList == nullptr) {
    973            result = value == -1 ||    // empty rule
    974                     n == value;       //  'is' rule
    975            break;
    976        }
    977        result = false;                // 'in' or 'within' rule
    978        for (int32_t r=0; r<rangeList->size(); r+=2) {
    979            if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) {
    980                result = true;
    981                break;
    982            }
    983        }
    984    } while (false);
    985 
    986    if (negated) {
    987        result = !result;
    988    }
    989    return result;
    990 }
    991 
    992 AndConstraint*
    993 AndConstraint::add(UErrorCode& status) {
    994    if (U_FAILURE(fInternalStatus)) {
    995        status = fInternalStatus;
    996        return nullptr;
    997    }
    998    this->next = new AndConstraint();
    999    if (this->next == nullptr) {
   1000        status = U_MEMORY_ALLOCATION_ERROR;
   1001    }
   1002    return this->next;
   1003 }
   1004 
   1005 
   1006 OrConstraint::OrConstraint(const OrConstraint& other) {
   1007    this->fInternalStatus = other.fInternalStatus;
   1008    if (U_FAILURE(fInternalStatus)) {
   1009        return; // stop early if the object we are copying from is invalid.
   1010    }
   1011    if ( other.childNode != nullptr ) {
   1012        this->childNode = new AndConstraint(*(other.childNode));
   1013        if (this->childNode == nullptr) {
   1014            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
   1015            return;
   1016        }
   1017    }
   1018    if (other.next != nullptr ) {
   1019        this->next = new OrConstraint(*(other.next));
   1020        if (this->next == nullptr) {
   1021            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
   1022            return;
   1023        }
   1024        if (U_FAILURE(this->next->fInternalStatus)) {
   1025            this->fInternalStatus = this->next->fInternalStatus;
   1026        }
   1027    }
   1028 }
   1029 
   1030 OrConstraint::~OrConstraint() {
   1031    delete childNode;
   1032    childNode = nullptr;
   1033    delete next;
   1034    next = nullptr;
   1035 }
   1036 
   1037 AndConstraint*
   1038 OrConstraint::add(UErrorCode& status) {
   1039    if (U_FAILURE(fInternalStatus)) {
   1040        status = fInternalStatus;
   1041        return nullptr;
   1042    }
   1043    OrConstraint *curOrConstraint=this;
   1044    {
   1045        while (curOrConstraint->next!=nullptr) {
   1046            curOrConstraint = curOrConstraint->next;
   1047        }
   1048        U_ASSERT(curOrConstraint->childNode == nullptr);
   1049        curOrConstraint->childNode = new AndConstraint();
   1050        if (curOrConstraint->childNode == nullptr) {
   1051            status = U_MEMORY_ALLOCATION_ERROR;
   1052        }
   1053    }
   1054    return curOrConstraint->childNode;
   1055 }
   1056 
   1057 UBool
   1058 OrConstraint::isFulfilled(const IFixedDecimal &number) {
   1059    OrConstraint* orRule=this;
   1060    UBool result=false;
   1061 
   1062    while (orRule!=nullptr && !result) {
   1063        result=true;
   1064        AndConstraint* andRule = orRule->childNode;
   1065        while (andRule!=nullptr && result) {
   1066            result = andRule->isFulfilled(number);
   1067            andRule=andRule->next;
   1068        }
   1069        orRule = orRule->next;
   1070    }
   1071 
   1072    return result;
   1073 }
   1074 
   1075 
   1076 RuleChain::RuleChain(const RuleChain& other) :
   1077        fKeyword(other.fKeyword), fDecimalSamples(other.fDecimalSamples),
   1078        fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded),
   1079        fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded), fInternalStatus(other.fInternalStatus) {
   1080    if (U_FAILURE(this->fInternalStatus)) {
   1081        return; // stop early if the object we are copying from is invalid. 
   1082    }
   1083    if (other.ruleHeader != nullptr) {
   1084        this->ruleHeader = new OrConstraint(*(other.ruleHeader));
   1085        if (this->ruleHeader == nullptr) {
   1086            this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
   1087        }
   1088        else if (U_FAILURE(this->ruleHeader->fInternalStatus)) {
   1089            // If the OrConstraint wasn't fully copied, then set our status to failure as well.
   1090            this->fInternalStatus = this->ruleHeader->fInternalStatus;
   1091            return; // exit early.
   1092        }
   1093    }
   1094    if (other.fNext != nullptr ) {
   1095        this->fNext = new RuleChain(*other.fNext);
   1096        if (this->fNext == nullptr) {
   1097            this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
   1098        }
   1099        else if (U_FAILURE(this->fNext->fInternalStatus)) {
   1100            // If the RuleChain wasn't fully copied, then set our status to failure as well.
   1101            this->fInternalStatus = this->fNext->fInternalStatus;
   1102        }
   1103    }
   1104 }
   1105 
   1106 RuleChain::~RuleChain() {
   1107    delete fNext;
   1108    delete ruleHeader;
   1109 }
   1110 
   1111 UnicodeString
   1112 RuleChain::select(const IFixedDecimal &number) const {
   1113    if (!number.isNaN() && !number.isInfinite()) {
   1114        for (const RuleChain *rules = this; rules != nullptr; rules = rules->fNext) {
   1115             if (rules->ruleHeader->isFulfilled(number)) {
   1116                 return rules->fKeyword;
   1117             }
   1118        }
   1119    }
   1120    return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5);
   1121 }
   1122 
   1123 static UnicodeString tokenString(tokenType tok) {
   1124    UnicodeString s;
   1125    switch (tok) {
   1126      case tVariableN:
   1127        s.append(LOW_N); break;
   1128      case tVariableI:
   1129        s.append(LOW_I); break;
   1130      case tVariableF:
   1131        s.append(LOW_F); break;
   1132      case tVariableV:
   1133        s.append(LOW_V); break;
   1134      case tVariableT:
   1135        s.append(LOW_T); break;
   1136      case tVariableE:
   1137        s.append(LOW_E); break;
   1138    case tVariableC:
   1139        s.append(LOW_C); break;
   1140      default:
   1141        s.append(TILDE);
   1142    }
   1143    return s;
   1144 }
   1145 
   1146 void
   1147 RuleChain::dumpRules(UnicodeString& result) {
   1148    char16_t digitString[16];
   1149 
   1150    if ( ruleHeader != nullptr ) {
   1151        result +=  fKeyword;
   1152        result += COLON;
   1153        result += SPACE;
   1154        OrConstraint* orRule=ruleHeader;
   1155        while ( orRule != nullptr ) {
   1156            AndConstraint* andRule=orRule->childNode;
   1157            while ( andRule != nullptr ) {
   1158                if ((andRule->op==AndConstraint::NONE) &&  (andRule->rangeList==nullptr) && (andRule->value == -1)) {
   1159                    // Empty Rules.
   1160                } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) ) {
   1161                    result += tokenString(andRule->digitsType);
   1162                    result += UNICODE_STRING_SIMPLE(" is ");
   1163                    if (andRule->negated) {
   1164                        result += UNICODE_STRING_SIMPLE("not ");
   1165                    }
   1166                    uprv_itou(digitString,16, andRule->value,10,0);
   1167                    result += UnicodeString(digitString);
   1168                }
   1169                else {
   1170                    result += tokenString(andRule->digitsType);
   1171                    result += SPACE;
   1172                    if (andRule->op==AndConstraint::MOD) {
   1173                        result += UNICODE_STRING_SIMPLE("mod ");
   1174                        uprv_itou(digitString,16, andRule->opNum,10,0);
   1175                        result += UnicodeString(digitString);
   1176                    }
   1177                    if (andRule->rangeList==nullptr) {
   1178                        if (andRule->negated) {
   1179                            result += UNICODE_STRING_SIMPLE(" is not ");
   1180                            uprv_itou(digitString,16, andRule->value,10,0);
   1181                            result += UnicodeString(digitString);
   1182                        }
   1183                        else {
   1184                            result += UNICODE_STRING_SIMPLE(" is ");
   1185                            uprv_itou(digitString,16, andRule->value,10,0);
   1186                            result += UnicodeString(digitString);
   1187                        }
   1188                    }
   1189                    else {
   1190                        if (andRule->negated) {
   1191                            if ( andRule->integerOnly ) {
   1192                                result += UNICODE_STRING_SIMPLE(" not in ");
   1193                            }
   1194                            else {
   1195                                result += UNICODE_STRING_SIMPLE(" not within ");
   1196                            }
   1197                        }
   1198                        else {
   1199                            if ( andRule->integerOnly ) {
   1200                                result += UNICODE_STRING_SIMPLE(" in ");
   1201                            }
   1202                            else {
   1203                                result += UNICODE_STRING_SIMPLE(" within ");
   1204                            }
   1205                        }
   1206                        for (int32_t r=0; r<andRule->rangeList->size(); r+=2) {
   1207                            int32_t rangeLo = andRule->rangeList->elementAti(r);
   1208                            int32_t rangeHi = andRule->rangeList->elementAti(r+1);
   1209                            uprv_itou(digitString,16, rangeLo, 10, 0);
   1210                            result += UnicodeString(digitString);
   1211                            result += UNICODE_STRING_SIMPLE("..");
   1212                            uprv_itou(digitString,16, rangeHi, 10,0);
   1213                            result += UnicodeString(digitString);
   1214                            if (r+2 < andRule->rangeList->size()) {
   1215                                result += UNICODE_STRING_SIMPLE(", ");
   1216                            }
   1217                        }
   1218                    }
   1219                }
   1220                if ( (andRule=andRule->next) != nullptr) {
   1221                    result += UNICODE_STRING_SIMPLE(" and ");
   1222                }
   1223            }
   1224            if ( (orRule = orRule->next) != nullptr ) {
   1225                result += UNICODE_STRING_SIMPLE(" or ");
   1226            }
   1227        }
   1228    }
   1229    if ( fNext != nullptr ) {
   1230        result += UNICODE_STRING_SIMPLE("; ");
   1231        fNext->dumpRules(result);
   1232    }
   1233 }
   1234 
   1235 
   1236 UErrorCode
   1237 RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const {
   1238    if (U_FAILURE(fInternalStatus)) {
   1239        return fInternalStatus;
   1240    }
   1241    if ( arraySize < capacityOfKeywords-1 ) {
   1242        keywords[arraySize++]=fKeyword;
   1243    }
   1244    else {
   1245        return U_BUFFER_OVERFLOW_ERROR;
   1246    }
   1247 
   1248    if ( fNext != nullptr ) {
   1249        return fNext->getKeywords(capacityOfKeywords, keywords, arraySize);
   1250    }
   1251    else {
   1252        return U_ZERO_ERROR;
   1253    }
   1254 }
   1255 
   1256 UBool
   1257 RuleChain::isKeyword(const UnicodeString& keywordParam) const {
   1258    if ( fKeyword == keywordParam ) {
   1259        return true;
   1260    }
   1261 
   1262    if ( fNext != nullptr ) {
   1263        return fNext->isKeyword(keywordParam);
   1264    }
   1265    else {
   1266        return false;
   1267    }
   1268 }
   1269 
   1270 
   1271 PluralRuleParser::PluralRuleParser() :
   1272        ruleIndex(0), token(), type(none), prevType(none),
   1273        curAndConstraint(nullptr), currentChain(nullptr), rangeLowIdx(-1), rangeHiIdx(-1)
   1274 {
   1275 }
   1276 
   1277 PluralRuleParser::~PluralRuleParser() {
   1278 }
   1279 
   1280 
   1281 int32_t
   1282 PluralRuleParser::getNumberValue(const UnicodeString& token) {
   1283    int32_t pos = 0;
   1284    return ICU_Utility::parseNumber(token, pos, 10);
   1285 }
   1286 
   1287 
   1288 void
   1289 PluralRuleParser::checkSyntax(UErrorCode &status)
   1290 {
   1291    if (U_FAILURE(status)) {
   1292        return;
   1293    }
   1294    if (!(prevType==none || prevType==tSemiColon)) {
   1295        type = getKeyType(token, type);  // Switch token type from tKeyword if we scanned a reserved word,
   1296                                               //   and we are not at the start of a rule, where a
   1297                                               //   keyword is expected.
   1298    }
   1299 
   1300    switch(prevType) {
   1301    case none:
   1302    case tSemiColon:
   1303        if (type!=tKeyword && type != tEOF) {
   1304            status = U_UNEXPECTED_TOKEN;
   1305        }
   1306        break;
   1307    case tVariableN:
   1308    case tVariableI:
   1309    case tVariableF:
   1310    case tVariableT:
   1311    case tVariableE:
   1312    case tVariableC:
   1313    case tVariableV:
   1314        if (type != tIs && type != tMod && type != tIn &&
   1315            type != tNot && type != tWithin && type != tEqual && type != tNotEqual) {
   1316            status = U_UNEXPECTED_TOKEN;
   1317        }
   1318        break;
   1319    case tKeyword:
   1320        if (type != tColon) {
   1321            status = U_UNEXPECTED_TOKEN;
   1322        }
   1323        break;
   1324    case tColon:
   1325        if (!(type == tVariableN ||
   1326              type == tVariableI ||
   1327              type == tVariableF ||
   1328              type == tVariableT ||
   1329              type == tVariableE ||
   1330              type == tVariableC ||
   1331              type == tVariableV ||
   1332              type == tAt)) {
   1333            status = U_UNEXPECTED_TOKEN;
   1334        }
   1335        break;
   1336    case tIs:
   1337        if ( type != tNumber && type != tNot) {
   1338            status = U_UNEXPECTED_TOKEN;
   1339        }
   1340        break;
   1341    case tNot:
   1342        if (type != tNumber && type != tIn && type != tWithin) {
   1343            status = U_UNEXPECTED_TOKEN;
   1344        }
   1345        break;
   1346    case tMod:
   1347    case tDot2:
   1348    case tIn:
   1349    case tWithin:
   1350    case tEqual:
   1351    case tNotEqual:
   1352        if (type != tNumber) {
   1353            status = U_UNEXPECTED_TOKEN;
   1354        }
   1355        break;
   1356    case tAnd:
   1357    case tOr:
   1358        if ( type != tVariableN &&
   1359             type != tVariableI &&
   1360             type != tVariableF &&
   1361             type != tVariableT &&
   1362             type != tVariableE &&
   1363             type != tVariableC &&
   1364             type != tVariableV) {
   1365            status = U_UNEXPECTED_TOKEN;
   1366        }
   1367        break;
   1368    case tComma:
   1369        if (type != tNumber) {
   1370            status = U_UNEXPECTED_TOKEN;
   1371        }
   1372        break;
   1373    case tNumber:
   1374        if (type != tDot2  && type != tSemiColon && type != tIs       && type != tNot    &&
   1375            type != tIn    && type != tEqual     && type != tNotEqual && type != tWithin &&
   1376            type != tAnd   && type != tOr        && type != tComma    && type != tAt     &&
   1377            type != tEOF)
   1378        {
   1379            status = U_UNEXPECTED_TOKEN;
   1380        }
   1381        // TODO: a comma following a number that is not part of a range will be allowed.
   1382        //       It's not the only case of this sort of thing. Parser needs a re-write.
   1383        break;
   1384    case tAt:
   1385        if (type != tDecimal && type != tInteger) {
   1386            status = U_UNEXPECTED_TOKEN;
   1387        }
   1388        break;
   1389    default:
   1390        status = U_UNEXPECTED_TOKEN;
   1391        break;
   1392    }
   1393 }
   1394 
   1395 
   1396 /*
   1397 *  Scan the next token from the input rules.
   1398 *     rules and returned token type are in the parser state variables.
   1399 */
   1400 void
   1401 PluralRuleParser::getNextToken(UErrorCode &status)
   1402 {
   1403    if (U_FAILURE(status)) {
   1404        return;
   1405    }
   1406 
   1407    char16_t ch;
   1408    while (ruleIndex < ruleSrc->length()) {
   1409        ch = ruleSrc->charAt(ruleIndex);
   1410        type = charType(ch);
   1411        if (type != tSpace) {
   1412            break;
   1413        }
   1414        ++(ruleIndex);
   1415    }
   1416    if (ruleIndex >= ruleSrc->length()) {
   1417        type = tEOF;
   1418        return;
   1419    }
   1420    int32_t curIndex= ruleIndex;
   1421 
   1422    switch (type) {
   1423      case tColon:
   1424      case tSemiColon:
   1425      case tComma:
   1426      case tEllipsis:
   1427      case tTilde:   // scanned '~'
   1428      case tAt:      // scanned '@'
   1429      case tEqual:   // scanned '='
   1430      case tMod:     // scanned '%'
   1431        // Single character tokens.
   1432        ++curIndex;
   1433        break;
   1434 
   1435      case tNotEqual:  // scanned '!'
   1436        if (ruleSrc->charAt(curIndex+1) == EQUALS) {
   1437            curIndex += 2;
   1438        } else {
   1439            type = none;
   1440            curIndex += 1;
   1441        }
   1442        break;
   1443 
   1444      case tKeyword:
   1445         while (type == tKeyword && ++curIndex < ruleSrc->length()) {
   1446             ch = ruleSrc->charAt(curIndex);
   1447             type = charType(ch);
   1448         }
   1449         type = tKeyword;
   1450         break;
   1451 
   1452      case tNumber:
   1453         while (type == tNumber && ++curIndex < ruleSrc->length()) {
   1454             ch = ruleSrc->charAt(curIndex);
   1455             type = charType(ch);
   1456         }
   1457         type = tNumber;
   1458         break;
   1459 
   1460       case tDot:
   1461         // We could be looking at either ".." in a range, or "..." at the end of a sample.
   1462         if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) {
   1463             ++curIndex;
   1464             break; // Single dot
   1465         }
   1466         if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) {
   1467             curIndex += 2;
   1468             type = tDot2;
   1469             break; // double dot
   1470         }
   1471         type = tEllipsis;
   1472         curIndex += 3;
   1473         break;     // triple dot
   1474 
   1475       default:
   1476         status = U_UNEXPECTED_TOKEN;
   1477         ++curIndex;
   1478         break;
   1479    }
   1480 
   1481    U_ASSERT(ruleIndex <= ruleSrc->length());
   1482    U_ASSERT(curIndex <= ruleSrc->length());
   1483    token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex);
   1484    ruleIndex = curIndex;
   1485 }
   1486 
   1487 tokenType
   1488 PluralRuleParser::charType(char16_t ch) {
   1489    if ((ch>=U_ZERO) && (ch<=U_NINE)) {
   1490        return tNumber;
   1491    }
   1492    if (ch>=LOW_A && ch<=LOW_Z) {
   1493        return tKeyword;
   1494    }
   1495    switch (ch) {
   1496    case COLON:
   1497        return tColon;
   1498    case SPACE:
   1499        return tSpace;
   1500    case SEMI_COLON:
   1501        return tSemiColon;
   1502    case DOT:
   1503        return tDot;
   1504    case COMMA:
   1505        return tComma;
   1506    case EXCLAMATION:
   1507        return tNotEqual;
   1508    case EQUALS:
   1509        return tEqual;
   1510    case PERCENT_SIGN:
   1511        return tMod;
   1512    case AT:
   1513        return tAt;
   1514    case ELLIPSIS:
   1515        return tEllipsis;
   1516    case TILDE:
   1517        return tTilde;
   1518    default :
   1519        return none;
   1520    }
   1521 }
   1522 
   1523 
   1524 //  Set token type for reserved words in the Plural Rule syntax.
   1525 
   1526 tokenType
   1527 PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType)
   1528 {
   1529    if (keyType != tKeyword) {
   1530        return keyType;
   1531    }
   1532 
   1533    if (0 == token.compare(PK_VAR_N, 1)) {
   1534        keyType = tVariableN;
   1535    } else if (0 == token.compare(PK_VAR_I, 1)) {
   1536        keyType = tVariableI;
   1537    } else if (0 == token.compare(PK_VAR_F, 1)) {
   1538        keyType = tVariableF;
   1539    } else if (0 == token.compare(PK_VAR_T, 1)) {
   1540        keyType = tVariableT;
   1541    } else if (0 == token.compare(PK_VAR_E, 1)) {
   1542        keyType = tVariableE;
   1543    } else if (0 == token.compare(PK_VAR_C, 1)) {
   1544        keyType = tVariableC;
   1545    } else if (0 == token.compare(PK_VAR_V, 1)) {
   1546        keyType = tVariableV;
   1547    } else if (0 == token.compare(PK_IS, 2)) {
   1548        keyType = tIs;
   1549    } else if (0 == token.compare(PK_AND, 3)) {
   1550        keyType = tAnd;
   1551    } else if (0 == token.compare(PK_IN, 2)) {
   1552        keyType = tIn;
   1553    } else if (0 == token.compare(PK_WITHIN, 6)) {
   1554        keyType = tWithin;
   1555    } else if (0 == token.compare(PK_NOT, 3)) {
   1556        keyType = tNot;
   1557    } else if (0 == token.compare(PK_MOD, 3)) {
   1558        keyType = tMod;
   1559    } else if (0 == token.compare(PK_OR, 2)) {
   1560        keyType = tOr;
   1561    } else if (0 == token.compare(PK_DECIMAL, 7)) {
   1562        keyType = tDecimal;
   1563    } else if (0 == token.compare(PK_INTEGER, 7)) {
   1564        keyType = tInteger;
   1565    }
   1566    return keyType;
   1567 }
   1568 
   1569 
   1570 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status)
   1571        : pos(0), fKeywordNames(status) {
   1572    if (U_FAILURE(status)) {
   1573        return;
   1574    }
   1575    fKeywordNames.setDeleter(uprv_deleteUObject);
   1576    UBool  addKeywordOther = true;
   1577    RuleChain *node = header;
   1578    while (node != nullptr) {
   1579        LocalPointer<UnicodeString> newElem(node->fKeyword.clone(), status);
   1580        fKeywordNames.adoptElement(newElem.orphan(), status);
   1581        if (U_FAILURE(status)) {
   1582            return;
   1583        }
   1584        if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
   1585            addKeywordOther = false;
   1586        }
   1587        node = node->fNext;
   1588    }
   1589 
   1590    if (addKeywordOther) {
   1591        LocalPointer<UnicodeString> newElem(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
   1592        fKeywordNames.adoptElement(newElem.orphan(), status);
   1593        if (U_FAILURE(status)) {
   1594            return;
   1595        }
   1596    }
   1597 }
   1598 
   1599 const UnicodeString*
   1600 PluralKeywordEnumeration::snext(UErrorCode& status) {
   1601    if (U_SUCCESS(status) && pos < fKeywordNames.size()) {
   1602        return static_cast<const UnicodeString*>(fKeywordNames.elementAt(pos++));
   1603    }
   1604    return nullptr;
   1605 }
   1606 
   1607 void
   1608 PluralKeywordEnumeration::reset(UErrorCode& /*status*/) {
   1609    pos=0;
   1610 }
   1611 
   1612 int32_t
   1613 PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
   1614    return fKeywordNames.size();
   1615 }
   1616 
   1617 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
   1618 }
   1619 
   1620 PluralOperand tokenTypeToPluralOperand(tokenType tt) {
   1621    switch(tt) {
   1622    case tVariableN:
   1623        return PLURAL_OPERAND_N;
   1624    case tVariableI:
   1625        return PLURAL_OPERAND_I;
   1626    case tVariableF:
   1627        return PLURAL_OPERAND_F;
   1628    case tVariableV:
   1629        return PLURAL_OPERAND_V;
   1630    case tVariableT:
   1631        return PLURAL_OPERAND_T;
   1632    case tVariableE:
   1633        return PLURAL_OPERAND_E;
   1634    case tVariableC:
   1635        return PLURAL_OPERAND_E;
   1636    default:
   1637        UPRV_UNREACHABLE_EXIT;  // unexpected.
   1638    }
   1639 }
   1640 
   1641 FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c) {
   1642    init(n, v, f, e, c);
   1643 }
   1644 
   1645 FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) {
   1646    init(n, v, f, e);
   1647    // check values. TODO make into unit test.
   1648    //            
   1649    //            long visiblePower = (int) Math.pow(10.0, v);
   1650    //            if (decimalDigits > visiblePower) {
   1651    //                throw new IllegalArgumentException();
   1652    //            }
   1653    //            double fraction = intValue + (decimalDigits / (double) visiblePower);
   1654    //            if (fraction != source) {
   1655    //                double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source));
   1656    //                if (diff > 0.00000001d) {
   1657    //                    throw new IllegalArgumentException();
   1658    //                }
   1659    //            }
   1660 }
   1661 
   1662 FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
   1663    init(n, v, f);
   1664 }
   1665 
   1666 FixedDecimal::FixedDecimal(double n, int32_t v) {
   1667    // Ugly, but for samples we don't care.
   1668    init(n, v, getFractionalDigits(n, v));
   1669 }
   1670 
   1671 FixedDecimal::FixedDecimal(double n) {
   1672    init(n);
   1673 }
   1674 
   1675 FixedDecimal::FixedDecimal() {
   1676    init(0, 0, 0);
   1677 }
   1678 
   1679 
   1680 // Create a FixedDecimal from a UnicodeString containing a number.
   1681 //    Inefficient, but only used for samples, so simplicity trumps efficiency.
   1682 
   1683 FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) {
   1684    CharString cs;
   1685    int32_t parsedExponent = 0;
   1686    int32_t parsedCompactExponent = 0;
   1687 
   1688    int32_t exponentIdx = num.indexOf(u'e');
   1689    if (exponentIdx < 0) {
   1690        exponentIdx = num.indexOf(u'E');
   1691    }
   1692    int32_t compactExponentIdx = num.indexOf(u'c');
   1693    if (compactExponentIdx < 0) {
   1694        compactExponentIdx = num.indexOf(u'C');
   1695    }
   1696 
   1697    if (exponentIdx >= 0) {
   1698        cs.appendInvariantChars(num.tempSubString(0, exponentIdx), status);
   1699        int32_t expSubstrStart = exponentIdx + 1;
   1700        parsedExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart);
   1701    }
   1702    else if (compactExponentIdx >= 0) {
   1703        cs.appendInvariantChars(num.tempSubString(0, compactExponentIdx), status);
   1704        int32_t expSubstrStart = compactExponentIdx + 1;
   1705        parsedCompactExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart);
   1706 
   1707        parsedExponent = parsedCompactExponent;
   1708        exponentIdx = compactExponentIdx;
   1709    }
   1710    else {
   1711        cs.appendInvariantChars(num, status);
   1712    }
   1713 
   1714    DecimalQuantity dl;
   1715    dl.setToDecNumber(cs.toStringPiece(), status);
   1716    if (U_FAILURE(status)) {
   1717        init(0, 0, 0);
   1718        return;
   1719    }
   1720 
   1721    int32_t decimalPoint = num.indexOf(DOT);
   1722    double n = dl.toDouble();
   1723    if (decimalPoint == -1) {
   1724        init(n, 0, 0, parsedExponent);
   1725    } else {
   1726        int32_t fractionNumLength = exponentIdx < 0 ? num.length() : cs.length();
   1727        int32_t v = fractionNumLength - decimalPoint - 1;
   1728        init(n, v, getFractionalDigits(n, v), parsedExponent);
   1729    }
   1730 }
   1731 
   1732 
   1733 FixedDecimal::FixedDecimal(const FixedDecimal &other) {
   1734    source = other.source;
   1735    visibleDecimalDigitCount = other.visibleDecimalDigitCount;
   1736    decimalDigits = other.decimalDigits;
   1737    decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros;
   1738    intValue = other.intValue;
   1739    exponent = other.exponent;
   1740    _hasIntegerValue = other._hasIntegerValue;
   1741    isNegative = other.isNegative;
   1742    _isNaN = other._isNaN;
   1743    _isInfinite = other._isInfinite;
   1744 }
   1745 
   1746 FixedDecimal::~FixedDecimal() = default;
   1747 
   1748 FixedDecimal FixedDecimal::createWithExponent(double n, int32_t v, int32_t e) {
   1749    return FixedDecimal(n, v, getFractionalDigits(n, v), e);
   1750 }
   1751 
   1752 
   1753 void FixedDecimal::init(double n) {
   1754    int32_t numFractionDigits = decimals(n);
   1755    init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
   1756 }
   1757 
   1758 
   1759 void FixedDecimal::init(double n, int32_t v, int64_t f) {
   1760    int32_t exponent = 0;
   1761    init(n, v, f, exponent);
   1762 }
   1763 
   1764 void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e) {
   1765    // Currently, `c` is an alias for `e`
   1766    init(n, v, f, e, e);
   1767 }
   1768 
   1769 void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e, int32_t c) {
   1770    isNegative = n < 0.0;
   1771    source = fabs(n);
   1772    _isNaN = uprv_isNaN(source);
   1773    _isInfinite = uprv_isInfinite(source);
   1774    exponent = e;
   1775    if (exponent == 0) {
   1776        exponent = c;
   1777    }
   1778    if (_isNaN || _isInfinite ||
   1779        source > static_cast<double>(U_INT64_MAX) ||
   1780        source < static_cast<double>(U_INT64_MIN)) {
   1781        v = 0;
   1782        f = 0;
   1783        intValue = 0;
   1784        _hasIntegerValue = false;
   1785    } else {
   1786        intValue = static_cast<int64_t>(source);
   1787        _hasIntegerValue = (source == intValue);
   1788    }
   1789 
   1790    visibleDecimalDigitCount = v;
   1791    decimalDigits = f;
   1792    if (f == 0) {
   1793         decimalDigitsWithoutTrailingZeros = 0;
   1794    } else {
   1795        int64_t fdwtz = f;
   1796        while ((fdwtz%10) == 0) {
   1797            fdwtz /= 10;
   1798        }
   1799        decimalDigitsWithoutTrailingZeros = fdwtz;
   1800    }
   1801 }
   1802 
   1803 
   1804 //  Fast path only exact initialization. Return true if successful.
   1805 //     Note: Do not multiply by 10 each time through loop, rounding cruft can build
   1806 //           up that makes the check for an integer result fail.
   1807 //           A single multiply of the original number works more reliably.
   1808 static int32_t p10[] = {1, 10, 100, 1000, 10000};
   1809 UBool FixedDecimal::quickInit(double n) {
   1810    UBool success = false;
   1811    n = fabs(n);
   1812    int32_t numFractionDigits;
   1813    for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) {
   1814        double scaledN = n * p10[numFractionDigits];
   1815        if (scaledN == floor(scaledN)) {
   1816            success = true;
   1817            break;
   1818        }
   1819    }
   1820    if (success) {
   1821        init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
   1822    }
   1823    return success;
   1824 }
   1825 
   1826 
   1827 
   1828 int32_t FixedDecimal::decimals(double n) {
   1829    // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros.
   1830    // fastpath the common cases, integers or fractions with 3 or fewer digits
   1831    n = fabs(n);
   1832    for (int ndigits=0; ndigits<=3; ndigits++) {
   1833        double scaledN = n * p10[ndigits];
   1834        if (scaledN == floor(scaledN)) {
   1835            return ndigits;
   1836        }
   1837    }
   1838 
   1839    // Slow path, convert with snprintf, parse converted output.
   1840    char  buf[30] = {0};
   1841    snprintf(buf, sizeof(buf), "%1.15e", n);
   1842    // formatted number looks like this: 1.234567890123457e-01
   1843    int exponent = atoi(buf+18);
   1844    int numFractionDigits = 15;
   1845    for (int i=16; ; --i) {
   1846        if (buf[i] != '0') {
   1847            break;
   1848        }
   1849        --numFractionDigits;
   1850    }
   1851    numFractionDigits -= exponent;   // Fraction part of fixed point representation.
   1852    return numFractionDigits;
   1853 }
   1854 
   1855 
   1856 // Get the fraction digits of a double, represented as an integer.
   1857 //    v is the number of visible fraction digits in the displayed form of the number.
   1858 //       Example: n = 1001.234, v = 6, result = 234000
   1859 //    TODO: need to think through how this is used in the plural rule context.
   1860 //          This function can easily encounter integer overflow, 
   1861 //          and can easily return noise digits when the precision of a double is exceeded.
   1862 
   1863 int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) {
   1864    if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) {
   1865        return 0;
   1866    }
   1867    n = fabs(n);
   1868    double fract = n - floor(n);
   1869    switch (v) {
   1870      case 1: return static_cast<int64_t>(fract * 10.0 + 0.5);
   1871      case 2: return static_cast<int64_t>(fract * 100.0 + 0.5);
   1872      case 3: return static_cast<int64_t>(fract * 1000.0 + 0.5);
   1873      default:
   1874          double scaled = floor(fract * pow(10.0, static_cast<double>(v)) + 0.5);
   1875          if (scaled >= static_cast<double>(U_INT64_MAX)) {
   1876              // Note: a double cannot accurately represent U_INT64_MAX. Casting it to double
   1877              //       will round up to the next representable value, which is U_INT64_MAX + 1.
   1878              return U_INT64_MAX;
   1879          } else {
   1880              return static_cast<int64_t>(scaled);
   1881          }
   1882      }
   1883 }
   1884 
   1885 
   1886 void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) {
   1887    int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount;
   1888    if (numTrailingFractionZeros > 0) {
   1889        for (int32_t i=0; i<numTrailingFractionZeros; i++) {
   1890            // Do not let the decimalDigits value overflow if there are many trailing zeros.
   1891            // Limit the value to 18 digits, the most that a 64 bit int can fully represent.
   1892            if (decimalDigits >= 100000000000000000LL) {
   1893                break;
   1894            }
   1895            decimalDigits *= 10;
   1896        }
   1897        visibleDecimalDigitCount += numTrailingFractionZeros;
   1898    }
   1899 }
   1900 
   1901 
   1902 double FixedDecimal::getPluralOperand(PluralOperand operand) const {
   1903    switch(operand) {
   1904        case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10.0, exponent));
   1905        case PLURAL_OPERAND_I: return static_cast<double>(longValue());
   1906        case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits);
   1907        case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
   1908        case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
   1909        case PLURAL_OPERAND_E: return exponent;
   1910        case PLURAL_OPERAND_C: return exponent;
   1911        default:
   1912             UPRV_UNREACHABLE_EXIT;  // unexpected.
   1913    }
   1914 }
   1915 
   1916 bool FixedDecimal::isNaN() const {
   1917    return _isNaN;
   1918 }
   1919 
   1920 bool FixedDecimal::isInfinite() const {
   1921    return _isInfinite;
   1922 }
   1923 
   1924 bool FixedDecimal::hasIntegerValue() const {
   1925    return _hasIntegerValue;
   1926 }
   1927 
   1928 bool FixedDecimal::isNanOrInfinity() const {
   1929    return _isNaN || _isInfinite;
   1930 }
   1931 
   1932 int32_t FixedDecimal::getVisibleFractionDigitCount() const {
   1933    return visibleDecimalDigitCount;
   1934 }
   1935 
   1936 bool FixedDecimal::operator==(const FixedDecimal &other) const {
   1937    return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount
   1938        && decimalDigits == other.decimalDigits && exponent == other.exponent;
   1939 }
   1940 
   1941 UnicodeString FixedDecimal::toString() const {
   1942    char pattern[15];
   1943    char buffer[20];
   1944    if (exponent != 0) {
   1945        snprintf(pattern, sizeof(pattern), "%%.%dfe%%d", visibleDecimalDigitCount);
   1946        snprintf(buffer, sizeof(buffer), pattern, source, exponent);
   1947    } else {
   1948        snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount);
   1949        snprintf(buffer, sizeof(buffer), pattern, source);
   1950    }
   1951    return UnicodeString(buffer, -1, US_INV);
   1952 }
   1953 
   1954 double FixedDecimal::doubleValue() const {
   1955    return (isNegative ? -source : source) * pow(10.0, exponent);
   1956 }
   1957 
   1958 int64_t FixedDecimal::longValue() const {
   1959    if (exponent == 0) {
   1960        return intValue;
   1961    } else {
   1962        return static_cast<long>(pow(10.0, exponent) * intValue);
   1963    }
   1964 }
   1965 
   1966 
   1967 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
   1968    fOpenStatus = status;
   1969    if (U_FAILURE(status)) {
   1970        return;
   1971    }
   1972    fOpenStatus = U_ZERO_ERROR; // clear any warnings.
   1973    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &fOpenStatus));
   1974    fLocales = ures_getByKey(rb.getAlias(), "locales", nullptr, &fOpenStatus);
   1975 }
   1976 
   1977 PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() {
   1978    ures_close(fLocales);
   1979    ures_close(fRes);
   1980    fLocales = nullptr;
   1981    fRes = nullptr;
   1982 }
   1983 
   1984 const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) {
   1985    if (U_FAILURE(status)) {
   1986        return nullptr;
   1987    }
   1988    if (U_FAILURE(fOpenStatus)) {
   1989        status = fOpenStatus;
   1990        return nullptr;
   1991    }
   1992    fRes = ures_getNextResource(fLocales, fRes, &status);
   1993    if (fRes == nullptr || U_FAILURE(status)) {
   1994        if (status == U_INDEX_OUTOFBOUNDS_ERROR) {
   1995            status = U_ZERO_ERROR;
   1996        }
   1997        return nullptr;
   1998    }
   1999    const char *result = ures_getKey(fRes);
   2000    if (resultLength != nullptr) {
   2001        *resultLength = static_cast<int32_t>(uprv_strlen(result));
   2002    }
   2003    return result;
   2004 }
   2005 
   2006 
   2007 void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) {
   2008    if (U_FAILURE(status)) {
   2009       return;
   2010    }
   2011    if (U_FAILURE(fOpenStatus)) {
   2012        status = fOpenStatus;
   2013        return;
   2014    }
   2015    ures_resetIterator(fLocales);
   2016 }
   2017 
   2018 int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const {
   2019    if (U_FAILURE(status)) {
   2020        return 0;
   2021    }
   2022    if (U_FAILURE(fOpenStatus)) {
   2023        status = fOpenStatus;
   2024        return 0;
   2025    }
   2026    return ures_getSize(fLocales);
   2027 }
   2028 
   2029 U_NAMESPACE_END
   2030 
   2031 
   2032 #endif /* #if !UCONFIG_NO_FORMATTING */
   2033 
   2034 //eof