1ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// © 2017 and later: Unicode, Inc. and others.
2ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html
3ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
4ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/utypes.h"
5ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
6ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
7ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
8ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "resource.h"
9ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "number_compact.h"
10ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/ustring.h"
11ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/ures.h"
12ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "cstring.h"
13ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "charstr.h"
14ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "uresimp.h"
15ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
16ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu;
17ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu::number;
18ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu::number::impl;
19ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
20ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertnamespace {
21ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
22ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// A dummy object used when a "0" compact decimal entry is encountered. This is necessary
23ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// in order to prevent falling back to root. Object equality ("==") is intended.
24ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertconst UChar *USE_FALLBACK = u"<USE FALLBACK>";
25ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
26ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */
27ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid getResourceBundleKey(const char *nsName, CompactStyle compactStyle, CompactType compactType,
28ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                                 CharString &sb, UErrorCode &status) {
29ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    sb.clear();
30ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    sb.append("NumberElements/", status);
31ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    sb.append(nsName, status);
32ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    sb.append(compactStyle == CompactStyle::UNUM_SHORT ? "/patternsShort" : "/patternsLong", status);
33ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    sb.append(compactType == CompactType::TYPE_DECIMAL ? "/decimalFormat" : "/currencyFormat", status);
34ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
35ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
36ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t getIndex(int32_t magnitude, StandardPlural::Form plural) {
37ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    return magnitude * StandardPlural::COUNT + plural;
38ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
39ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
40ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t countZeros(const UChar *patternString, int32_t patternLength) {
41ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // NOTE: This strategy for computing the number of zeros is a hack for efficiency.
42ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // It could break if there are any 0s that aren't part of the main pattern.
43ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    int32_t numZeros = 0;
44ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    for (int32_t i = 0; i < patternLength; i++) {
45ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (patternString[i] == u'0') {
46ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            numZeros++;
47ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        } else if (numZeros > 0) {
48ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            break; // zeros should always be contiguous
49ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
50ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
51ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    return numZeros;
52ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
53ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
54ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} // namespace
55ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
56ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// NOTE: patterns and multipliers both get zero-initialized.
57ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactData::CompactData() : patterns(), multipliers(), largestMagnitude(0), isEmpty(TRUE) {
58ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
59ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
60ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::populate(const Locale &locale, const char *nsName, CompactStyle compactStyle,
61ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                           CompactType compactType, UErrorCode &status) {
62ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    CompactDataSink sink(*this);
63ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &status));
64ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
65ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
66ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    bool nsIsLatn = strcmp(nsName, "latn") == 0;
67ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    bool compactIsShort = compactStyle == CompactStyle::UNUM_SHORT;
68ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
69ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // Fall back to latn numbering system and/or short compact style.
70ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    CharString resourceKey;
71ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    getResourceBundleKey(nsName, compactStyle, compactType, resourceKey, status);
72ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    UErrorCode localStatus = U_ZERO_ERROR;
73ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus);
74ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (isEmpty && !nsIsLatn) {
75ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        getResourceBundleKey("latn", compactStyle, compactType, resourceKey, status);
76ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        localStatus = U_ZERO_ERROR;
77ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus);
78ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
79ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (isEmpty && !compactIsShort) {
80ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        getResourceBundleKey(nsName, CompactStyle::UNUM_SHORT, compactType, resourceKey, status);
81ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        localStatus = U_ZERO_ERROR;
82ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus);
83ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
84ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (isEmpty && !nsIsLatn && !compactIsShort) {
85ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        getResourceBundleKey("latn", CompactStyle::UNUM_SHORT, compactType, resourceKey, status);
86ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        localStatus = U_ZERO_ERROR;
87ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus);
88ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
89ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
90ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // The last fallback should be guaranteed to return data.
91ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (isEmpty) {
92ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        status = U_INTERNAL_PROGRAM_ERROR;
93ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
94ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
95ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
96ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t CompactData::getMultiplier(int32_t magnitude) const {
97ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (magnitude < 0) {
98ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        return 0;
99ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
100ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (magnitude > largestMagnitude) {
101ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        magnitude = largestMagnitude;
102ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
103ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    return multipliers[magnitude];
104ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
105ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
106ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertconst UChar *CompactData::getPattern(int32_t magnitude, StandardPlural::Form plural) const {
107ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (magnitude < 0) {
108ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        return nullptr;
109ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
110ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (magnitude > largestMagnitude) {
111ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        magnitude = largestMagnitude;
112ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
113ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    const UChar *patternString = patterns[getIndex(magnitude, plural)];
114ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (patternString == nullptr && plural != StandardPlural::OTHER) {
115ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Fall back to "other" plural variant
116ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        patternString = patterns[getIndex(magnitude, StandardPlural::OTHER)];
117ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
118ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (patternString == USE_FALLBACK) { // == is intended
119ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Return null if USE_FALLBACK is present
120ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        patternString = nullptr;
121ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
122ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    return patternString;
123ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
124ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
125ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::getUniquePatterns(UVector &output, UErrorCode &status) const {
126ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    U_ASSERT(output.isEmpty());
127ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // NOTE: In C++, this is done more manually with a UVector.
128ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // In Java, we can take advantage of JDK HashSet.
129ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    for (auto pattern : patterns) {
130ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (pattern == nullptr || pattern == USE_FALLBACK) {
131ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            continue;
132ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
133ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
134ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Insert pattern into the UVector if the UVector does not already contain the pattern.
135ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Search the UVector from the end since identical patterns are likely to be adjacent.
136ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        for (int32_t i = output.size() - 1; i >= 0; i--) {
137ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (u_strcmp(pattern, static_cast<const UChar *>(output[i])) == 0) {
138ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                goto continue_outer;
139ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
140ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
141ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
142ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // The string was not found; add it to the UVector.
143ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // ANDY: This requires a const_cast.  Why?
144ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        output.addElement(const_cast<UChar *>(pattern), status);
145ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
146ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        continue_outer:
147ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        continue;
148ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
149ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
150ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
151ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::CompactDataSink::put(const char *key, ResourceValue &value, UBool /*noFallback*/,
152ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                                       UErrorCode &status) {
153ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // traverse into the table of powers of ten
154ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    ResourceTable powersOfTenTable = value.getTable(status);
155ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
156ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) {
157ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
158ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Assumes that the keys are always of the form "10000" where the magnitude is the
159ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // length of the key minus one.  We expect magnitudes to be less than MAX_DIGITS.
160ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        auto magnitude = static_cast<int8_t> (strlen(key) - 1);
161ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        int8_t multiplier = data.multipliers[magnitude];
162ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        U_ASSERT(magnitude < COMPACT_MAX_DIGITS);
163ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
164ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Iterate over the plural variants ("one", "other", etc)
165ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ResourceTable pluralVariantsTable = value.getTable(status);
166ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (U_FAILURE(status)) { return; }
167ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) {
168ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
169ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // Skip this magnitude/plural if we already have it from a child locale.
170ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // Note: This also skips USE_FALLBACK entries.
171ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            StandardPlural::Form plural = StandardPlural::fromString(key, status);
172ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (U_FAILURE(status)) { return; }
173ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (data.patterns[getIndex(magnitude, plural)] != nullptr) {
174ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                continue;
175ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
176ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
177ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // The value "0" means that we need to use the default pattern and not fall back
178ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // to parent locales. Example locale where this is relevant: 'it'.
179ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            int32_t patternLength;
180ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            const UChar *patternString = value.getString(patternLength, status);
181ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (U_FAILURE(status)) { return; }
182ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (u_strcmp(patternString, u"0") == 0) {
183ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                patternString = USE_FALLBACK;
184ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                patternLength = 0;
185ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
186ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
187ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // Save the pattern string. We will parse it lazily.
188ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            data.patterns[getIndex(magnitude, plural)] = patternString;
189ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
190ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // If necessary, compute the multiplier: the difference between the magnitude
191ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            // and the number of zeros in the pattern.
192ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (multiplier == 0) {
193ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                int32_t numZeros = countZeros(patternString, patternLength);
194ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
195ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                    multiplier = static_cast<int8_t> (numZeros - magnitude - 1);
196ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                }
197ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
198ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
199ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
200ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Save the multiplier.
201ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (data.multipliers[magnitude] == 0) {
202ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            data.multipliers[magnitude] = multiplier;
203ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (magnitude > data.largestMagnitude) {
204ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                data.largestMagnitude = magnitude;
205ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
206ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            data.isEmpty = false;
207ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        } else {
208ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            U_ASSERT(data.multipliers[magnitude] == multiplier);
209ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
210ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
211ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
212ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
213ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert///////////////////////////////////////////////////////////
214ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/// END OF CompactData.java; BEGIN CompactNotation.java ///
215ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert///////////////////////////////////////////////////////////
216ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
217ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactHandler::CompactHandler(CompactStyle compactStyle, const Locale &locale, const char *nsName,
218ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                               CompactType compactType, const PluralRules *rules,
219ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                               MutablePatternModifier *buildReference, const MicroPropsGenerator *parent,
220ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                               UErrorCode &status)
221ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        : rules(rules), parent(parent) {
222ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    data.populate(locale, nsName, compactStyle, compactType, status);
223ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (buildReference != nullptr) {
224ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Safe code path
225ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        precomputeAllModifiers(*buildReference, status);
226ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        safe = TRUE;
227ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    } else {
228ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Unsafe code path
229ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        safe = FALSE;
230ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
231ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
232ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
233ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactHandler::~CompactHandler() {
234ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    for (int32_t i = 0; i < precomputedModsLength; i++) {
235ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        delete precomputedMods[i].mod;
236ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
237ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
238ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
239ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactHandler::precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status) {
240ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
241ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
242ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T
243ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    UVector allPatterns(12, status);
244ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
245ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    data.getUniquePatterns(allPatterns, status);
246ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
247ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
248ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // C++ only: ensure that precomputedMods has room.
249ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    precomputedModsLength = allPatterns.size();
250ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (precomputedMods.getCapacity() < precomputedModsLength) {
251ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        precomputedMods.resize(allPatterns.size(), status);
252ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (U_FAILURE(status)) { return; }
253ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
254ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
255ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    for (int32_t i = 0; i < precomputedModsLength; i++) {
256ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        auto patternString = static_cast<const UChar *>(allPatterns[i]);
257ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        UnicodeString hello(patternString);
258ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        CompactModInfo &info = precomputedMods[i];
259ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ParsedPatternInfo patternInfo;
260ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status);
261ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (U_FAILURE(status)) { return; }
262ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        buildReference.setPatternInfo(&patternInfo);
263ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        info.mod = buildReference.createImmutable(status);
264ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        if (U_FAILURE(status)) { return; }
265ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        info.numDigits = patternInfo.positive.integerTotal;
266ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        info.patternString = patternString;
267ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
268ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
269ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
270ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
271ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                                     UErrorCode &status) const {
272ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    parent->processQuantity(quantity, micros, status);
273ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (U_FAILURE(status)) { return; }
274ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
275ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // Treat zero as if it had magnitude 0
276ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    int magnitude;
277ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (quantity.isZero()) {
278ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        magnitude = 0;
279ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        micros.rounding.apply(quantity, status);
280ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    } else {
281ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // TODO: Revisit chooseMultiplierAndApply
282ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data, status);
283ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
284ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        magnitude -= multiplier;
285ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
286ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
287ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    StandardPlural::Form plural = quantity.getStandardPlural(rules);
288ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    const UChar *patternString = data.getPattern(magnitude, plural);
289ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    int numDigits = -1;
290ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    if (patternString == nullptr) {
291ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Use the default (non-compact) modifier.
292ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // No need to take any action.
293ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    } else if (safe) {
294ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Safe code path.
295ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Java uses a hash set here for O(1) lookup.  C++ uses a linear search.
296ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // TODO: Benchmark this and maybe change to a binary search or hash table.
297ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        int32_t i = 0;
298ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        for (; i < precomputedModsLength; i++) {
299ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            const CompactModInfo &info = precomputedMods[i];
300ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            if (u_strcmp(patternString, info.patternString) == 0) {
301ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                info.mod->applyToMicros(micros, quantity);
302ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                numDigits = info.numDigits;
303ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert                break;
304ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            }
305ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        }
306ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // It should be guaranteed that we found the entry.
307ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        U_ASSERT(i < precomputedModsLength);
308ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    } else {
309ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Unsafe code path.
310ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // Overwrite the PatternInfo in the existing modMiddle.
311ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        // C++ Note: Use unsafePatternInfo for proper lifecycle.
312ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        ParsedPatternInfo &patternInfo = const_cast<CompactHandler *>(this)->unsafePatternInfo;
313ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status);
314ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        static_cast<MutablePatternModifier*>(const_cast<Modifier*>(micros.modMiddle))
315ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert            ->setPatternInfo(&patternInfo);
316ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert        numDigits = patternInfo.positive.integerTotal;
317ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    }
318ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
319ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // FIXME: Deal with numDigits == 0 (Awaiting a test case)
320ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    (void)numDigits;
321ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
322ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    // We already performed rounding. Do not perform it again.
323ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert    micros.rounding = Rounder::constructPassThrough();
324ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert}
325ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert
326ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#endif /* #if !UCONFIG_NO_FORMATTING */
327