Coverage Report

Created: 2025-11-07 06:50

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