]> granicus.if.org Git - icu/commitdiff
ICU-22093 ICU4C: Add SimpleNumber and SimpleNumberFormatter
authorShane F. Carr <shane@unicode.org>
Wed, 23 Nov 2022 22:48:12 +0000 (22:48 +0000)
committerShane F. Carr <shane@unicode.org>
Tue, 29 Nov 2022 04:28:50 +0000 (20:28 -0800)
See #2241

32 files changed:
icu4c/source/i18n/dcfmtsym.cpp
icu4c/source/i18n/formattedval_impl.h
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/i18n_uwp.vcxproj
icu4c/source/i18n/number_capi.cpp
icu4c/source/i18n/number_formatimpl.cpp
icu4c/source/i18n/number_formatimpl.h
icu4c/source/i18n/number_microprops.h
icu4c/source/i18n/number_modifiers.cpp
icu4c/source/i18n/number_modifiers.h
icu4c/source/i18n/number_patternmodifier.cpp
icu4c/source/i18n/number_patternmodifier.h
icu4c/source/i18n/number_simple.cpp [new file with mode: 0644]
icu4c/source/i18n/number_utypes.h
icu4c/source/i18n/numrange_impl.cpp
icu4c/source/i18n/sources.txt
icu4c/source/i18n/unicode/dcfmtsym.h
icu4c/source/i18n/unicode/formattednumber.h [new file with mode: 0644]
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/i18n/unicode/simplenumberformatter.h [new file with mode: 0644]
icu4c/source/i18n/unicode/uformattednumber.h [new file with mode: 0644]
icu4c/source/i18n/unicode/unum.h
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/i18n/unicode/unumberoptions.h [new file with mode: 0644]
icu4c/source/i18n/unicode/usimplenumberformatter.h [new file with mode: 0644]
icu4c/source/test/depstest/dependencies.txt
icu4c/source/test/intltest/Makefile.in
icu4c/source/test/intltest/intltest.vcxproj
icu4c/source/test/intltest/intltest.vcxproj.filters
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_simple.cpp [new file with mode: 0644]

index fa5920aaa50776c6846075903e9ca5b2776cd2c5..3b2650c1e3762f4a47ddbdddae986da02ddb0150 100644 (file)
@@ -99,7 +99,7 @@ static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount]
 // Initializes this with the decimal format symbols in the default locale.
 
 DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
-        : UObject(), locale(), currPattern(NULL) {
+        : UObject(), locale() {
     initialize(locale, status, true);
 }
 
@@ -107,17 +107,17 @@ DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
 // Initializes this with the decimal format symbols in the desired locale.
 
 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
-        : UObject(), locale(loc), currPattern(NULL) {
+        : UObject(), locale(loc) {
     initialize(locale, status);
 }
 
 DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status)
-        : UObject(), locale(loc), currPattern(NULL) {
+        : UObject(), locale(loc) {
     initialize(locale, status, false, &ns);
 }
 
 DecimalFormatSymbols::DecimalFormatSymbols()
-        : UObject(), locale(Locale::getRoot()), currPattern(NULL) {
+        : UObject(), locale(Locale::getRoot()) {
     *validLocale = *actualLocale = 0;
     initialize();
 }
@@ -169,6 +169,7 @@ DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
         fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol; 
         fCodePointZero = rhs.fCodePointZero;
         currPattern = rhs.currPattern;
+        uprv_strcpy(nsName, rhs.nsName);
     }
     return *this;
 }
@@ -383,6 +384,7 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
     } else {
         nsName = gLatn;
     }
+    uprv_strcpy(this->nsName, nsName);
 
     // Open resource bundles
     const char* locStr = loc.getName();
@@ -517,7 +519,7 @@ DecimalFormatSymbols::initialize() {
     fCodePointZero = 0x30;
     U_ASSERT(fCodePointZero == fSymbols[kZeroDigitSymbol].char32At(0));
     currPattern = nullptr;
-
+    nsName[0] = 0;
 }
 
 void DecimalFormatSymbols::setCurrency(const UChar* currency, UErrorCode& status) {
index 2b9a3970d2e4ebaed20a7f3c9ca8dade9dfe0e7c..bc03af4627491dfca7a1ee9f425f639efc8a2a14 100644 (file)
@@ -153,6 +153,9 @@ public:
 
     virtual ~FormattedValueStringBuilderImpl();
 
+    FormattedValueStringBuilderImpl(FormattedValueStringBuilderImpl&&) = default;
+    FormattedValueStringBuilderImpl& operator=(FormattedValueStringBuilderImpl&&) = default;
+
     // Implementation of FormattedValue (const):
 
     UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
index eef2dd190e6eaed33c0d97452eaf20ed5c5a844a..73ab2d4651e6cc38de6050fbe0deefb3ea58d158 100644 (file)
     <ClCompile Include="number_mapper.cpp" />
     <ClCompile Include="number_multiplier.cpp" />
     <ClCompile Include="number_currencysymbols.cpp" />
+    <ClCompile Include="number_simple.cpp" />
     <ClCompile Include="number_skeletons.cpp" />
     <ClCompile Include="number_symbolswrapper.cpp" />
     <ClCompile Include="number_capi.cpp" />
index d2175c7c768d8dcb6b2fc2584dc81693d44cb757..e35edb5c1b19a0ff46161fd9e3bd0dce24feb92e 100644 (file)
     <ClCompile Include="number_currencysymbols.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="number_simple.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="number_skeletons.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
index 25c6c333d7bb23464fc85015aafb0536ed1b4c93..9afcb5ddb94093fd2ac6216199e3faea95c7dd75 100644 (file)
     <ClCompile Include="number_mapper.cpp" />
     <ClCompile Include="number_multiplier.cpp" />
     <ClCompile Include="number_currencysymbols.cpp" />
+    <ClCompile Include="number_simple.cpp" />
     <ClCompile Include="number_skeletons.cpp" />
     <ClCompile Include="number_symbolswrapper.cpp" />
     <ClCompile Include="number_capi.cpp" />
index 42bb05c06613d29a140efd1302e4cd2b8ab4b0b0..13b0cd0aef3fc37db388bca30edce045d0a28d73 100644 (file)
@@ -16,6 +16,8 @@
 #include "number_decnum.h"
 #include "unicode/numberformatter.h"
 #include "unicode/unumberformatter.h"
+#include "unicode/simplenumberformatter.h"
+#include "unicode/usimplenumberformatter.h"
 
 using namespace icu;
 using namespace icu::number;
@@ -35,6 +37,24 @@ struct UNumberFormatterData : public UMemory,
     LocalizedNumberFormatter fFormatter;
 };
 
+/**
+ * Implementation class for USimpleNumber. Wraps a SimpleNumberFormatter.
+ */
+struct USimpleNumberData : public UMemory,
+        // Magic number as ASCII == "SNM" (SimpleNuMber)
+        public IcuCApiHelper<USimpleNumber, USimpleNumberData, 0x534E4D00> {
+    SimpleNumber fNumber;
+};
+
+/**
+ * Implementation class for USimpleNumberFormatter. Wraps a SimpleNumberFormatter.
+ */
+struct USimpleNumberFormatterData : public UMemory,
+        // Magic number as ASCII == "SNF" (SimpleNumberFormatter)
+        public IcuCApiHelper<USimpleNumberFormatter, USimpleNumberFormatterData, 0x534E4600> {
+    SimpleNumberFormatter fFormatter;
+};
+
 struct UFormattedNumberImpl;
 
 // Magic number as ASCII == "FDN" (FormatteDNumber)
@@ -46,6 +66,8 @@ struct UFormattedNumberImpl : public UFormattedValueImpl, public UFormattedNumbe
 
     FormattedNumber fImpl;
     UFormattedNumberData fData;
+
+    void setTo(FormattedNumber value);
 };
 
 UFormattedNumberImpl::UFormattedNumberImpl()
@@ -58,6 +80,10 @@ UFormattedNumberImpl::~UFormattedNumberImpl() {
     fImpl.fData = nullptr;
 }
 
+void UFormattedNumberImpl::setTo(FormattedNumber value) {
+    fData = std::move(*value.fData);
+}
+
 }
 }
 U_NAMESPACE_END
@@ -225,6 +251,144 @@ unumf_close(UNumberFormatter* f) {
 }
 
 
+///// SIMPLE NUMBER FORMATTER /////
+
+U_CAPI USimpleNumber* U_EXPORT2
+usnum_openForInt64(int64_t value, UErrorCode* ec) {
+    auto* impl = new USimpleNumberData();
+    if (impl == nullptr) {
+        *ec = U_MEMORY_ALLOCATION_ERROR;
+        return nullptr;
+    }
+    impl->fNumber = SimpleNumber::forInt64(value, *ec);
+    return impl->exportForC();
+}
+
+U_CAPI void U_EXPORT2
+usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.multiplyByPowerOfTen(power, *ec);
+}
+
+U_CAPI void U_EXPORT2
+usnum_roundTo(USimpleNumber* unumber, int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.roundTo(position, roundingMode, *ec);
+}
+
+U_CAPI void U_EXPORT2
+usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.setMinimumIntegerDigits(minimumIntegerDigits, *ec);
+}
+
+U_CAPI void U_EXPORT2
+usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.setMinimumFractionDigits(minimumFractionDigits, *ec);
+}
+
+U_CAPI void U_EXPORT2
+usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.truncateStart(maximumIntegerDigits, *ec);
+}
+
+U_CAPI void U_EXPORT2
+usnum_setSign(USimpleNumber* unumber, USimpleNumberSign sign, UErrorCode* ec) {
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    number->fNumber.setSign(sign, *ec);
+}
+
+U_CAPI USimpleNumberFormatter* U_EXPORT2
+usnumf_openForLocale(const char* locale, UErrorCode* ec) {
+    auto* impl = new USimpleNumberFormatterData();
+    if (impl == nullptr) {
+        *ec = U_MEMORY_ALLOCATION_ERROR;
+        return nullptr;
+    }
+    impl->fFormatter = SimpleNumberFormatter::forLocale(locale, *ec);
+    return impl->exportForC();
+}
+
+U_CAPI USimpleNumberFormatter* U_EXPORT2
+usnumf_openForLocaleAndGroupingStrategy(
+       const char* locale, UNumberGroupingStrategy groupingStrategy, UErrorCode* ec) {
+    auto* impl = new USimpleNumberFormatterData();
+    if (impl == nullptr) {
+        *ec = U_MEMORY_ALLOCATION_ERROR;
+        return nullptr;
+    }
+    impl->fFormatter = SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, groupingStrategy, *ec);
+    return impl->exportForC();
+}
+
+U_CAPI void U_EXPORT2
+usnumf_formatAndAdoptNumber(
+        const USimpleNumberFormatter* uformatter,
+        USimpleNumber* unumber,
+        UFormattedNumber* uresult,
+        UErrorCode* ec) {
+    auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec);
+    auto* number = USimpleNumberData::validate(unumber, *ec);
+    auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
+    if (U_FAILURE(*ec)) {
+        delete number;
+        return;
+    }
+    auto localResult = formatter->fFormatter.format(std::move(number->fNumber), *ec);
+    result->setTo(std::move(localResult)); 
+    delete number;
+}
+
+U_CAPI void U_EXPORT2
+usnumf_formatInt64(
+        const USimpleNumberFormatter* uformatter,
+        int64_t value,
+        UFormattedNumber* uresult,
+        UErrorCode* ec) {
+    auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec);
+    auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
+    if (U_FAILURE(*ec)) {
+        return;
+    }
+    auto localResult = formatter->fFormatter.formatInt64(value, *ec);
+    result->setTo(std::move(localResult)); 
+}
+
+U_CAPI void U_EXPORT2
+usnum_close(USimpleNumber* unumber) {
+    UErrorCode localStatus = U_ZERO_ERROR;
+    const USimpleNumberData* impl = USimpleNumberData::validate(unumber, localStatus);
+    delete impl;
+}
+
+U_CAPI void U_EXPORT2
+usnumf_close(USimpleNumberFormatter* uformatter) {
+    UErrorCode localStatus = U_ZERO_ERROR;
+    const USimpleNumberFormatterData* impl = USimpleNumberFormatterData::validate(uformatter, localStatus);
+    delete impl;
+}
+
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
 
index 4fb190b744b7c15fa60114b75b6159c3d9e78d6b..53bac49a55c522692e751276e9c7c33a9f326b12 100644 (file)
@@ -36,7 +36,7 @@ int32_t NumberFormatterImpl::formatStatic(const MacroProps &macros, UFormattedNu
     NumberFormatterImpl impl(macros, false, status);
     MicroProps& micros = impl.preProcessUnsafe(inValue, status);
     if (U_FAILURE(status)) { return 0; }
-    int32_t length = writeNumber(micros, inValue, outString, 0, status);
+    int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
     length += writeAffixes(micros, outString, 0, length, status);
     results->outputUnit = std::move(micros.outputUnit);
     results->gender = micros.gender;
@@ -61,7 +61,7 @@ int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &s
     MicroProps micros;
     preProcess(inValue, micros, status);
     if (U_FAILURE(status)) { return 0; }
-    int32_t length = writeNumber(micros, inValue, outString, 0, status);
+    int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
     length += writeAffixes(micros, outString, 0, length, status);
     results->outputUnit = std::move(micros.outputUnit);
     results->gender = micros.gender;
@@ -186,7 +186,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
 
     // Resolve the symbols. Do this here because currency may need to customize them.
     if (macros.symbols.isDecimalFormatSymbols()) {
-        fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
+        fMicros.simple.symbols = macros.symbols.getDecimalFormatSymbols();
     } else {
         LocalPointer<DecimalFormatSymbols> newSymbols(
             new DecimalFormatSymbols(macros.locale, *ns, status), status);
@@ -199,15 +199,15 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
                 return nullptr;
             }
         }
-        fMicros.symbols = newSymbols.getAlias();
+        fMicros.simple.symbols = newSymbols.getAlias();
         fSymbols.adoptInstead(newSymbols.orphan());
     }
 
     // Load and parse the pattern string. It is used for grouping sizes and affixes only.
     // If we are formatting currency, check for a currency-specific pattern.
     const char16_t* pattern = nullptr;
-    if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
-        pattern = fMicros.symbols->getCurrencyPattern();
+    if (isCurrency && fMicros.simple.symbols->getCurrencyPattern() != nullptr) {
+        pattern = fMicros.simple.symbols->getCurrencyPattern();
     }
     if (pattern == nullptr) {
         CldrPatternStyle patternStyle;
@@ -291,14 +291,14 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
 
     // Grouping strategy
     if (!macros.grouper.isBogus()) {
-        fMicros.grouping = macros.grouper;
+        fMicros.simple.grouping = macros.grouper;
     } else if (isCompactNotation) {
         // Compact notation uses minGrouping by default since ICU 59
-        fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
+        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
     } else {
-        fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
+        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
     }
-    fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);
+    fMicros.simple.grouping.setLocaleData(*fPatternInfo, macros.locale);
 
     // Padding strategy
     if (!macros.padder.isBogus()) {
@@ -323,17 +323,17 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
 
     // Decimal mark display
     if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
-        fMicros.decimal = macros.decimal;
+        fMicros.simple.decimal = macros.decimal;
     } else {
-        fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
+        fMicros.simple.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
     }
 
     // Use monetary separator symbols
-    fMicros.useCurrency = isCurrency;
+    fMicros.simple.useCurrency = isCurrency;
 
     // Inner modifier (scientific notation)
     if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
-        auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.symbols, chain);
+        auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.simple.symbols, chain);
         if (newScientificHandler == nullptr) {
             status = U_MEMORY_ALLOCATION_ERROR;
             return nullptr;
@@ -362,13 +362,13 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately);
     if (patternModifier->needsPlurals()) {
         patternModifier->setSymbols(
-                fMicros.symbols,
+                fMicros.simple.symbols,
                 currency,
                 unitWidth,
                 resolvePluralRules(macros.rules, macros.locale, status),
                 status);
     } else {
-        patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
+        patternModifier->setSymbols(fMicros.simple.symbols, currency, unitWidth, nullptr, status);
     }
     if (safe) {
         fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
@@ -380,7 +380,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
 
     // currencyAsDecimal
     if (affixProvider->currencyAsDecimal()) {
-        fMicros.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status);
+        fMicros.simple.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status);
     }
 
     // Outer modifier (CLDR units and currency long names)
@@ -481,8 +481,10 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
 }
 
 const PluralRules*
-NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
-                                        UErrorCode& status) {
+NumberFormatterImpl::resolvePluralRules(
+        const PluralRules* rulesPtr,
+        const Locale& locale,
+        UErrorCode& status) {
     if (rulesPtr != nullptr) {
         return rulesPtr;
     }
@@ -493,8 +495,12 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Local
     return fRules.getAlias();
 }
 
-int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,
-                                          int32_t start, int32_t end, UErrorCode& status) {
+int32_t NumberFormatterImpl::writeAffixes(
+        const MicroProps& micros,
+        FormattedStringBuilder& string,
+        int32_t start,
+        int32_t end,
+        UErrorCode& status) {
     U_ASSERT(micros.modOuter != nullptr);
     // Always apply the inner modifier (which is "strong").
     int32_t length = micros.modInner->apply(string, start, end, status);
@@ -508,9 +514,12 @@ int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStr
     return length;
 }
 
-int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
-                                         FormattedStringBuilder& string, int32_t index,
-                                         UErrorCode& status) {
+int32_t NumberFormatterImpl::writeNumber(
+        const SimpleMicroProps& micros,
+        DecimalQuantity& quantity,
+        FormattedStringBuilder& string,
+        int32_t index,
+        UErrorCode& status) {
     int32_t length = 0;
     if (quantity.isInfinite()) {
         length += string.insert(
@@ -528,7 +537,12 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti
 
     } else {
         // Add the integer digits
-        length += writeIntegerDigits(micros, quantity, string, length + index, status);
+        length += writeIntegerDigits(
+            micros,
+            quantity,
+            string,
+            length + index,
+            status);
 
         // Add the decimal point
         if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
@@ -573,9 +587,12 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti
     return length;
 }
 
-int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
-                                                FormattedStringBuilder& string, int32_t index,
-                                                UErrorCode& status) {
+int32_t NumberFormatterImpl::writeIntegerDigits(
+        const SimpleMicroProps& micros,
+        DecimalQuantity& quantity,
+        FormattedStringBuilder& string,
+        int32_t index,
+        UErrorCode& status) {
     int length = 0;
     int integerCount = quantity.getUpperDisplayMagnitude() + 1;
     for (int i = 0; i < integerCount; i++) {
@@ -605,9 +622,12 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, Decima
     return length;
 }
 
-int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
-                                                 FormattedStringBuilder& string, int32_t index,
-                                                 UErrorCode& status) {
+int32_t NumberFormatterImpl::writeFractionDigits(
+        const SimpleMicroProps& micros,
+        DecimalQuantity& quantity,
+        FormattedStringBuilder& string,
+        int32_t index,
+        UErrorCode& status) {
     int length = 0;
     int fractionCount = -quantity.getLowerDisplayMagnitude();
     for (int i = 0; i < fractionCount; i++) {
index d7be1468b6d7af01ef807619f38a2967c05fd68a..62d532126161fa50205936563245fe7a63229cb7 100644 (file)
@@ -79,14 +79,22 @@ class NumberFormatterImpl : public UMemory {
      * Synthesizes the output string from a MicroProps and DecimalQuantity.
      * This method formats only the main number, not affixes.
      */
-    static int32_t writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
-                               FormattedStringBuilder& string, int32_t index, UErrorCode& status);
+    static int32_t writeNumber(
+        const SimpleMicroProps& micros,
+        DecimalQuantity& quantity,
+        FormattedStringBuilder& string,
+        int32_t index,
+        UErrorCode& status);
 
     /**
      * Adds the affixes.  Intended to be called immediately after formatNumber.
      */
-    static int32_t writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, int32_t start,
-                                int32_t end, UErrorCode& status);
+    static int32_t writeAffixes(
+        const MicroProps& micros,
+        FormattedStringBuilder& string,
+        int32_t start,
+        int32_t end,
+        UErrorCode& status);
 
   private:
     // Head of the MicroPropsGenerator linked list. Subclasses' processQuantity
@@ -146,12 +154,20 @@ class NumberFormatterImpl : public UMemory {
     macrosToMicroGenerator(const MacroProps &macros, bool safe, UErrorCode &status);
 
     static int32_t
-    writeIntegerDigits(const MicroProps &micros, DecimalQuantity &quantity, FormattedStringBuilder &string,
-                       int32_t index, UErrorCode &status);
+    writeIntegerDigits(
+        const SimpleMicroProps& micros,
+        DecimalQuantity &quantity,
+        FormattedStringBuilder &string,
+        int32_t index,
+        UErrorCode &status);
 
     static int32_t
-    writeFractionDigits(const MicroProps &micros, DecimalQuantity &quantity, FormattedStringBuilder &string,
-                        int32_t index, UErrorCode &status);
+    writeFractionDigits(
+        const SimpleMicroProps& micros,
+        DecimalQuantity &quantity,
+        FormattedStringBuilder &string,
+        int32_t index,
+        UErrorCode &status);
 };
 
 }  // namespace impl
index c34e7c17e97aa5d82aaf36257af48d46e4ed648d..8d213548d1d7e478e6a81b002d9bdac3be0e7098 100644 (file)
@@ -67,33 +67,39 @@ class IntMeasures : public MaybeStackArray<int64_t, 2> {
     UErrorCode status = U_ZERO_ERROR;
 };
 
+struct SimpleMicroProps : public UMemory {
+    Grouper grouping;
+    bool useCurrency = false;
+    UNumberDecimalSeparatorDisplay decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
+
+    // Currency symbol to be used as the decimal separator
+    UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString();
+
+    // Note: This struct has no direct ownership of the following pointer.
+    const DecimalFormatSymbols* symbols = nullptr;
+};
+
 /**
  * MicroProps is the first MicroPropsGenerator that should be should be called,
  * producing an initialized MicroProps instance that will be passed on and
  * modified throughout the rest of the chain of MicroPropsGenerator instances.
  */
 struct MicroProps : public MicroPropsGenerator {
+    SimpleMicroProps simple;
 
     // NOTE: All of these fields are properly initialized in NumberFormatterImpl.
     RoundingImpl rounder;
-    Grouper grouping;
     Padder padding;
     IntegerWidth integerWidth;
     UNumberSignDisplay sign;
-    UNumberDecimalSeparatorDisplay decimal;
-    bool useCurrency;
     char nsName[9];
 
-    // Currency symbol to be used as the decimal separator
-    UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString();
-
     // No ownership: must point at a string which will outlive MicroProps
     // instances, e.g. a string with static storage duration, or just a string
     // that will never be deallocated or modified.
     const char *gender;
 
     // Note: This struct has no direct ownership of the following pointers.
-    const DecimalFormatSymbols* symbols;
 
     // Pointers to Modifiers provided by the number formatting pipeline (when
     // the value is known):
index 092b66ff579363013843053625957ef771d98db0..d2c58660883f2a8ac1dfe821c26d121c25652c17 100644 (file)
@@ -62,12 +62,21 @@ Modifier::Parameters::Parameters(
 
 ModifierStore::~ModifierStore() = default;
 
-AdoptingModifierStore::~AdoptingModifierStore()  {
+AdoptingSignumModifierStore::~AdoptingSignumModifierStore()  {
     for (const Modifier *mod : mods) {
         delete mod;
     }
 }
 
+AdoptingSignumModifierStore&
+AdoptingSignumModifierStore::operator=(AdoptingSignumModifierStore&& other) U_NOEXCEPT {
+    for (size_t i=0; i<SIGNUM_COUNT; i++) {
+        this->mods[i] = other.mods[i];
+        other.mods[i] = nullptr;
+    }
+    return *this;
+}
+
 
 int32_t ConstantAffixModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex,
                                      UErrorCode &status) const {
index 09af3f48813c88091146d5ad7012495c5232104b..fa8dd52913e3d61f3d13869a519e83c7d1c2f189 100644 (file)
@@ -272,13 +272,45 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory {
     bool fStrong;
 };
 
+/** An adopting Modifier store that varies by signum but not plural form. */
+class U_I18N_API AdoptingSignumModifierStore : public UMemory {
+  public:
+    virtual ~AdoptingSignumModifierStore();
+
+    AdoptingSignumModifierStore() = default;
+
+    // No copying!
+    AdoptingSignumModifierStore(const AdoptingSignumModifierStore &other) = delete;
+    AdoptingSignumModifierStore& operator=(const AdoptingSignumModifierStore& other) = delete;
+
+    // Moving is OK
+    AdoptingSignumModifierStore(AdoptingSignumModifierStore &&other) U_NOEXCEPT {
+        *this = std::move(other);
+    }
+    AdoptingSignumModifierStore& operator=(AdoptingSignumModifierStore&& other) U_NOEXCEPT;
+
+    /** Take ownership of the Modifier and slot it in at the given Signum. */
+    void adoptModifier(Signum signum, const Modifier* mod) {
+        U_ASSERT(mods[signum] == nullptr);
+        mods[signum] = mod;
+    }
+
+    inline const Modifier*& operator[](Signum signum) {
+        return mods[signum];
+    }
+    inline Modifier const* operator[](Signum signum) const {
+        return mods[signum];
+    }
+
+  private:
+    const Modifier* mods[SIGNUM_COUNT] = {};
+};
+
 /**
  * This implementation of ModifierStore adopts Modifier pointers.
  */
 class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
   public:
-    virtual ~AdoptingModifierStore();
-
     static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER;
 
     AdoptingModifierStore() = default;
@@ -286,46 +318,36 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
     // No copying!
     AdoptingModifierStore(const AdoptingModifierStore &other) = delete;
 
-    /**
-     * Sets the Modifier with the specified signum and plural form.
-     */
-    void adoptModifier(Signum signum, StandardPlural::Form plural, const Modifier *mod) {
-        U_ASSERT(mods[getModIndex(signum, plural)] == nullptr);
-        mods[getModIndex(signum, plural)] = mod;
+    // Moving is OK
+    AdoptingModifierStore(AdoptingModifierStore &&other) = default;
+
+    /** Sets the modifiers for a specific plural form. */
+    void adoptSignumModifierStore(StandardPlural::Form plural, AdoptingSignumModifierStore other) {
+        mods[plural] = std::move(other);
     }
 
-    /**
-     * Sets the Modifier with the specified signum.
-     * The modifier will apply to all plural forms.
-     */
-    void adoptModifierWithoutPlural(Signum signum, const Modifier *mod) {
-        U_ASSERT(mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] == nullptr);
-        mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod;
+    /** Sets the modifiers for the default plural form. */
+    void adoptSignumModifierStoreNoPlural(AdoptingSignumModifierStore other) {
+        mods[DEFAULT_STANDARD_PLURAL] = std::move(other);
     }
 
     /** Returns a reference to the modifier; no ownership change. */
     const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE {
-        const Modifier* modifier = mods[getModIndex(signum, plural)];
+        const Modifier* modifier = mods[plural][signum];
         if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) {
-            modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
+            modifier = mods[DEFAULT_STANDARD_PLURAL][signum];
         }
         return modifier;
     }
 
     /** Returns a reference to the modifier; no ownership change. */
     const Modifier *getModifierWithoutPlural(Signum signum) const {
-        return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
+        return mods[DEFAULT_STANDARD_PLURAL][signum];
     }
 
   private:
     // NOTE: mods is zero-initialized (to nullptr)
-    const Modifier *mods[4 * StandardPlural::COUNT] = {};
-
-    inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) {
-        U_ASSERT(signum >= 0 && signum < SIGNUM_COUNT);
-        U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT);
-        return static_cast<int32_t>(plural) * SIGNUM_COUNT + signum;
-    }
+    AdoptingSignumModifierStore mods[StandardPlural::COUNT] = {};
 };
 
 } // namespace impl
index 088a30ecd7ff6f331e8b89785cb8f1474e62ab5a..6f398c6acf0dec07c804a43c12e417906cf96d18 100644 (file)
@@ -60,6 +60,21 @@ bool MutablePatternModifier::needsPlurals() const {
     // Silently ignore any error codes.
 }
 
+AdoptingSignumModifierStore MutablePatternModifier::createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status) {
+    AdoptingSignumModifierStore pm;
+
+    setNumberProperties(SIGNUM_POS, plural);
+    pm.adoptModifier(SIGNUM_POS, createConstantModifier(status));
+    setNumberProperties(SIGNUM_NEG_ZERO, plural);
+    pm.adoptModifier(SIGNUM_NEG_ZERO, createConstantModifier(status));
+    setNumberProperties(SIGNUM_POS_ZERO, plural);
+    pm.adoptModifier(SIGNUM_POS_ZERO, createConstantModifier(status));
+    setNumberProperties(SIGNUM_NEG, plural);
+    pm.adoptModifier(SIGNUM_NEG, createConstantModifier(status));
+
+    return pm;
+}
+
 ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
     // TODO: Move StandardPlural VALUES to standardplural.h
     static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
@@ -79,14 +94,7 @@ ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& st
     if (needsPlurals()) {
         // Slower path when we require the plural keyword.
         for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
-            setNumberProperties(SIGNUM_POS, plural);
-            pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status));
-            setNumberProperties(SIGNUM_NEG_ZERO, plural);
-            pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status));
-            setNumberProperties(SIGNUM_POS_ZERO, plural);
-            pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status));
-            setNumberProperties(SIGNUM_NEG, plural);
-            pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
+            pm->adoptSignumModifierStore(plural, createImmutableForPlural(plural, status));
         }
         if (U_FAILURE(status)) {
             delete pm;
@@ -95,14 +103,7 @@ ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& st
         return new ImmutablePatternModifier(pm, fRules);  // adopts pm
     } else {
         // Faster path when plural keyword is not needed.
-        setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
-        pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
-        setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
-        pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status));
-        setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
-        pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status));
-        setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT);
-        pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
+        pm->adoptSignumModifierStoreNoPlural(createImmutableForPlural(StandardPlural::Form::COUNT, status));
         if (U_FAILURE(status)) {
             delete pm;
             return nullptr;
index 4f825e1ed2191646545c6ad8a4c0f81023e93133..45a72669ace0471d169988eceecd10045bfa1bae 100644 (file)
@@ -156,6 +156,9 @@ class U_I18N_API MutablePatternModifier
      */
     bool needsPlurals() const;
 
+    /** Creates a quantity-dependent Modifier for the specified plural form. */
+    AdoptingSignumModifierStore createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status);
+
     /**
      * Creates a new quantity-dependent Modifier that behaves the same as the current instance, but which is immutable
      * and can be saved for future use. The number properties in the current instance are mutated; all other properties
diff --git a/icu4c/source/i18n/number_simple.cpp b/icu4c/source/i18n/number_simple.cpp
new file mode 100644 (file)
index 0000000..a2af6be
--- /dev/null
@@ -0,0 +1,255 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/numberformatter.h"
+#include "unicode/simplenumberformatter.h"
+#include "number_formatimpl.h"
+#include "number_utils.h"
+#include "number_patternmodifier.h"
+#include "number_utypes.h"
+
+using namespace icu;
+using namespace icu::number;
+using namespace icu::number::impl;
+
+
+SimpleNumber
+SimpleNumber::forInt64(int64_t value, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return SimpleNumber();
+    }
+    auto results = new UFormattedNumberData();
+    if (results == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return SimpleNumber();
+    }
+    results->quantity.setToLong(value);
+    return SimpleNumber(results, status);
+}
+
+SimpleNumber::SimpleNumber(UFormattedNumberData* data, UErrorCode& status) : fData(data) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+    if (fData->quantity.isNegative()) {
+        fSign = UNUM_SIMPLE_NUMBER_MINUS_SIGN;
+    } else {
+        fSign = UNUM_SIMPLE_NUMBER_NO_SIGN;
+    }
+}
+
+void SimpleNumber::cleanup() {
+    delete fData;
+    fData = nullptr;
+}
+
+void SimpleNumber::multiplyByPowerOfTen(int32_t power, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fData->quantity.adjustMagnitude(power);
+}
+
+void SimpleNumber::roundTo(int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fData->quantity.roundToMagnitude(position, roundingMode, status);
+}
+
+void SimpleNumber::setMinimumIntegerDigits(uint32_t position, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fData->quantity.setMinInteger(position);
+}
+
+void SimpleNumber::setMinimumFractionDigits(uint32_t position, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fData->quantity.setMinFraction(position);
+}
+
+void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fData->quantity.applyMaxInteger(position);
+}
+
+void SimpleNumber::setSign(USimpleNumberSign sign, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fData == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+    fSign = sign;
+}
+
+
+void SimpleNumberFormatter::cleanup() {
+    delete fOwnedSymbols;
+    delete fMicros;
+    delete fPatternModifier;
+    fOwnedSymbols = nullptr;
+    fMicros = nullptr;
+    fPatternModifier = nullptr;
+}
+
+SimpleNumberFormatter SimpleNumberFormatter::forLocale(const icu::Locale &locale, UErrorCode &status) {
+    return SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, UNUM_GROUPING_AUTO, status);
+}
+
+SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndGroupingStrategy(
+        const icu::Locale &locale,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status) {
+    SimpleNumberFormatter retval;
+    retval.fOwnedSymbols = new DecimalFormatSymbols(locale, status);
+    if (U_FAILURE(status)) {
+        return retval;
+    }
+    if (retval.fOwnedSymbols == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return retval;
+    }
+    retval.initialize(locale, *retval.fOwnedSymbols, groupingStrategy, status);
+    return retval;
+}
+
+
+SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy(
+        const icu::Locale &locale,
+        const DecimalFormatSymbols &symbols,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status) {
+    SimpleNumberFormatter retval;
+    retval.initialize(locale, symbols, groupingStrategy, status);
+    return retval;
+}
+
+
+void SimpleNumberFormatter::initialize(
+        const icu::Locale &locale,
+        const DecimalFormatSymbols &symbols,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    fMicros = new SimpleMicroProps();
+    if (fMicros == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+    fMicros->symbols = &symbols;
+
+    auto pattern = utils::getPatternForStyle(
+        locale,
+        symbols.getNumberingSystemName(),
+        CLDR_PATTERN_STYLE_DECIMAL,
+        status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    ParsedPatternInfo patternInfo;
+    PatternParser::parseToPatternInfo(UnicodeString(pattern), patternInfo, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    auto grouper = Grouper::forStrategy(groupingStrategy);
+    grouper.setLocaleData(patternInfo, locale);
+    fMicros->grouping = grouper;
+
+    MutablePatternModifier patternModifier(false);
+    patternModifier.setPatternInfo(&patternInfo, kUndefinedField);
+    patternModifier.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false, false);
+    patternModifier.setSymbols(fMicros->symbols, {}, UNUM_UNIT_WIDTH_SHORT, nullptr, status);
+
+    fPatternModifier = new AdoptingSignumModifierStore(patternModifier.createImmutableForPlural(StandardPlural::COUNT, status));
+
+    fGroupingStrategy = groupingStrategy;
+    return;
+}
+
+FormattedNumber SimpleNumberFormatter::format(SimpleNumber value, UErrorCode &status) const {
+    formatImpl(value.fData, value.fSign, status);
+
+    // Do not save the results object if we encountered a failure.
+    if (U_SUCCESS(status)) {
+        auto temp = value.fData;
+        value.fData = nullptr;
+        return FormattedNumber(temp);
+    } else {
+        return FormattedNumber(status);
+    }
+}
+
+void SimpleNumberFormatter::formatImpl(UFormattedNumberData* data, USimpleNumberSign sign, UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (data == nullptr) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+    if (fPatternModifier == nullptr || fMicros == nullptr) {
+        status = U_INVALID_STATE_ERROR;
+        return;
+    }
+
+    Signum signum;
+    if (sign == UNUM_SIMPLE_NUMBER_MINUS_SIGN) {
+        signum = SIGNUM_NEG;
+    } else if (sign == UNUM_SIMPLE_NUMBER_PLUS_SIGN) {
+        signum = SIGNUM_POS;
+    } else {
+        signum = SIGNUM_POS_ZERO;
+    }
+
+    const Modifier* modifier = (*fPatternModifier)[signum];
+    auto length = NumberFormatterImpl::writeNumber(
+        *fMicros,
+        data->quantity,
+        data->getStringRef(),
+        0,
+        status);
+    length += modifier->apply(data->getStringRef(), 0, length, status);
+    data->getStringRef().writeTerminator(status);
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
index 50c861787f4ed9c2b6300fb2a87aacbea624c191..0c130401894af72e57c10c73a6af36fe3bdd6fe5 100644 (file)
@@ -35,6 +35,9 @@ public:
     UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {}
     virtual ~UFormattedNumberData();
 
+    UFormattedNumberData(UFormattedNumberData&&) = default;
+    UFormattedNumberData& operator=(UFormattedNumberData&&) = default;
+
     // The formatted quantity.
     DecimalQuantity quantity;
 
index 06efc7b2815e7afe5976744396676896a0ac0e8f..ffee174dcca532ae2b8b8e2bd5c3751d4f754815 100644 (file)
@@ -239,7 +239,7 @@ void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data
                                                  UErrorCode& status) const {
     if (U_FAILURE(status)) { return; }
     if (fSameFormatters) {
-        int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.getStringRef(), 0, status);
+        int32_t length = NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, data.getStringRef(), 0, status);
         NumberFormatterImpl::writeAffixes(micros1, data.getStringRef(), 0, length, status);
     } else {
         formatRange(data, micros1, micros2, status);
@@ -256,7 +256,7 @@ void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& d
         MicroProps microsAppx;
         data.quantity1.resetExponent();
         fApproximatelyFormatter.preProcess(data.quantity1, microsAppx, status);
-        int32_t length = NumberFormatterImpl::writeNumber(microsAppx, data.quantity1, data.getStringRef(), 0, status);
+        int32_t length = NumberFormatterImpl::writeNumber(microsAppx.simple, data.quantity1, data.getStringRef(), 0, status);
         length += microsAppx.modInner->apply(data.getStringRef(), 0, length, status);
         length += microsAppx.modMiddle->apply(data.getStringRef(), 0, length, status);
         microsAppx.modOuter->apply(data.getStringRef(), 0, length, status);
@@ -384,10 +384,10 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
         }
     }
 
-    length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status);
+    length1 += NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, string, UPRV_INDEX_0, status);
     // ICU-21684: Write the second number to a temp string to avoid repeated insert operations
     FormattedStringBuilder tempString;
-    NumberFormatterImpl::writeNumber(micros2, data.quantity2, tempString, 0, status);
+    NumberFormatterImpl::writeNumber(micros2.simple, data.quantity2, tempString, 0, status);
     length2 += string.insert(UPRV_INDEX_2, tempString, status);
 
     // TODO: Support padding?
index 69705c87b4e25f8b366c2873d49abb32988872d3..a1af43b93e84fe2d146165a67d0f45b64181b3f6 100644 (file)
@@ -122,6 +122,7 @@ number_patternmodifier.cpp
 number_patternstring.cpp
 number_rounding.cpp
 number_scientific.cpp
+number_simple.cpp
 number_skeletons.cpp
 number_symbolswrapper.cpp
 number_usageprefs.cpp
index c5f7404416fda3ad3d0bfcdc0392fd6eca8a25f6..c0a94c42bc68e1b4dc42196242efb001df9e962a 100644 (file)
@@ -456,6 +456,12 @@ public:
      * @internal
      */
     inline const char16_t* getCurrencyPattern(void) const;
+
+    /**
+     * Returns the numbering system with which this DecimalFormatSymbols was initialized.
+     * @internal
+     */
+    inline const char* getNumberingSystemName(void) const;
 #endif  /* U_HIDE_INTERNAL_API */
 
 private:
@@ -500,12 +506,13 @@ private:
 
     char actualLocale[ULOC_FULLNAME_CAPACITY];
     char validLocale[ULOC_FULLNAME_CAPACITY];
-    const char16_t* currPattern;
+    const char16_t* currPattern = nullptr;
 
     UnicodeString currencySpcBeforeSym[UNUM_CURRENCY_SPACING_COUNT];
     UnicodeString currencySpcAfterSym[UNUM_CURRENCY_SPACING_COUNT];
     UBool fIsCustomCurrencySymbol;
     UBool fIsCustomIntlCurrencySymbol;
+    char nsName[kInternalNumSysNameCapacity+1] = {};
 };
 
 // -------------------------------------
@@ -591,6 +598,10 @@ inline const char16_t*
 DecimalFormatSymbols::getCurrencyPattern() const {
     return currPattern;
 }
+inline const char*
+DecimalFormatSymbols::getNumberingSystemName() const {
+    return nsName;
+}
 #endif /* U_HIDE_INTERNAL_API */
 
 U_NAMESPACE_END
diff --git a/icu4c/source/i18n/unicode/formattednumber.h b/icu4c/source/i18n/unicode/formattednumber.h
new file mode 100644 (file)
index 0000000..af72862
--- /dev/null
@@ -0,0 +1,215 @@
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __FORMATTEDNUMBER_H__
+#define __FORMATTEDNUMBER_H__
+
+#include "unicode/utypes.h"
+
+#if U_SHOW_CPLUSPLUS_API
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/uobject.h"
+#include "unicode/formattedvalue.h"
+#include "unicode/measunit.h"
+#include "unicode/udisplayoptions.h"
+
+/**
+ * \file
+ * \brief C API: Formatted number result from various number formatting functions.
+ *
+ * See also {@link icu::FormattedValue} for additional things you can do with a FormattedNumber.
+ */
+
+U_NAMESPACE_BEGIN
+
+class FieldPositionIteratorHandler;
+
+namespace number {  // icu::number
+
+namespace impl {
+class DecimalQuantity;
+class UFormattedNumberData;
+struct UFormattedNumberImpl;
+}  // icu::number::impl
+
+
+
+/**
+ * The result of a number formatting operation. This class allows the result to be exported in several data types,
+ * including a UnicodeString and a FieldPositionIterator.
+ *
+ * Instances of this class are immutable and thread-safe.
+ *
+ * @stable ICU 60
+ */
+class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
+  public:
+
+    /**
+     * Default constructor; makes an empty FormattedNumber.
+     * @stable ICU 64
+     */
+    FormattedNumber()
+        : fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {}
+
+    /**
+     * Move constructor: Leaves the source FormattedNumber in an undefined state.
+     * @stable ICU 62
+     */
+    FormattedNumber(FormattedNumber&& src) U_NOEXCEPT;
+
+    /**
+     * Destruct an instance of FormattedNumber.
+     * @stable ICU 60
+     */
+    virtual ~FormattedNumber() U_OVERRIDE;
+
+    /** Copying not supported; use move constructor instead. */
+    FormattedNumber(const FormattedNumber&) = delete;
+
+    /** Copying not supported; use move assignment instead. */
+    FormattedNumber& operator=(const FormattedNumber&) = delete;
+
+    /**
+     * Move assignment: Leaves the source FormattedNumber in an undefined state.
+     * @stable ICU 62
+     */
+    FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT;
+
+    // Copybrief: this method is older than the parent method
+    /**
+     * @copybrief FormattedValue::toString()
+     *
+     * For more information, see FormattedValue::toString()
+     *
+     * @stable ICU 62
+     */
+    UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
+
+    // Copydoc: this method is new in ICU 64
+    /** @copydoc FormattedValue::toTempString() */
+    UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
+
+    // Copybrief: this method is older than the parent method
+    /**
+     * @copybrief FormattedValue::appendTo()
+     *
+     * For more information, see FormattedValue::appendTo()
+     *
+     * @stable ICU 62
+     */
+    Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
+
+    // Copydoc: this method is new in ICU 64
+    /** @copydoc FormattedValue::nextPosition() */
+    UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
+
+    /**
+     * Export the formatted number as a "numeric string" conforming to the
+     * syntax defined in the Decimal Arithmetic Specification, available at
+     * http://speleotrove.com/decimal
+     *
+     * This endpoint is useful for obtaining the exact number being printed
+     * after scaling and rounding have been applied by the number formatter.
+     *
+     * Example call site:
+     *
+     *     auto decimalNumber = fn.toDecimalNumber<std::string>(status);
+     *
+     * @tparam StringClass A string class compatible with StringByteSink;
+     *         for example, std::string.
+     * @param status Set if an error occurs.
+     * @return A StringClass containing the numeric string.
+     * @stable ICU 65
+     */
+    template<typename StringClass>
+    inline StringClass toDecimalNumber(UErrorCode& status) const;
+
+       /**
+     * Gets the resolved output unit.
+     *
+     * The output unit is dependent upon the localized preferences for the usage
+     * specified via NumberFormatterSettings::usage(), and may be a unit with
+     * UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
+     * as "foot-and-inch" or "hour-and-minute-and-second".
+     *
+     * @return `MeasureUnit`.
+     * @stable ICU 68
+     */
+    MeasureUnit getOutputUnit(UErrorCode& status) const;
+
+#ifndef U_HIDE_DRAFT_API
+
+    /**
+     * Gets the noun class of the formatted output. Returns `UNDEFINED` when the noun class
+     * is not supported yet.
+     *
+     * @return UDisplayOptionsNounClass
+     * @draft ICU 72
+     */
+    UDisplayOptionsNounClass getNounClass(UErrorCode &status) const;
+
+#endif // U_HIDE_DRAFT_API
+
+#ifndef U_HIDE_INTERNAL_API
+
+    /**
+     *  Gets the raw DecimalQuantity for plural rule selection.
+     *  @internal
+     */
+    void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const;
+
+    /**
+     * Populates the mutable builder type FieldPositionIteratorHandler.
+     * @internal
+     */
+    void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
+
+#endif  /* U_HIDE_INTERNAL_API */
+
+  private:
+    // Can't use LocalPointer because UFormattedNumberData is forward-declared
+    impl::UFormattedNumberData *fData;
+
+    // Error code for the terminal methods
+    UErrorCode fErrorCode;
+
+    /**
+     * Internal constructor from data type. Adopts the data pointer.
+     * @internal (private)
+     */
+    explicit FormattedNumber(impl::UFormattedNumberData *results)
+        : fData(results), fErrorCode(U_ZERO_ERROR) {}
+
+    explicit FormattedNumber(UErrorCode errorCode)
+        : fData(nullptr), fErrorCode(errorCode) {}
+
+    void toDecimalNumber(ByteSink& sink, UErrorCode& status) const;
+
+    // To give LocalizedNumberFormatter format methods access to this class's constructor:
+    friend class LocalizedNumberFormatter;
+    friend class SimpleNumberFormatter;
+
+    // To give C API access to internals
+    friend struct impl::UFormattedNumberImpl;
+};
+
+template<typename StringClass>
+StringClass FormattedNumber::toDecimalNumber(UErrorCode& status) const {
+    StringClass result;
+    StringByteSink<StringClass> sink(&result);
+    toDecimalNumber(sink, status);
+    return result;
+}
+
+}  // namespace number
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif /* U_SHOW_CPLUSPLUS_API */
+
+#endif // __FORMATTEDNUMBER_H__
+
index be2056be36dca9f916382ad6e8f135606c87d628..486af2869fd0496569b39e119c4437e9b8ede369 100644 (file)
 #include "unicode/dcfmtsym.h"
 #include "unicode/displayoptions.h"
 #include "unicode/fieldpos.h"
-#include "unicode/formattedvalue.h"
 #include "unicode/fpositer.h"
 #include "unicode/measunit.h"
 #include "unicode/nounit.h"
 #include "unicode/parseerr.h"
 #include "unicode/plurrule.h"
 #include "unicode/ucurr.h"
-#include "unicode/udisplayoptions.h"
 #include "unicode/unum.h"
 #include "unicode/unumberformatter.h"
 #include "unicode/uobject.h"
+#include "unicode/unumberoptions.h"
+#include "unicode/formattednumber.h"
 
 /**
  * \file
@@ -112,6 +112,7 @@ namespace number {  // icu::number
 // Forward declarations:
 class UnlocalizedNumberFormatter;
 class LocalizedNumberFormatter;
+class SimpleNumberFormatter;
 class FormattedNumber;
 class Notation;
 class ScientificNotation;
@@ -166,6 +167,8 @@ struct UFormattedNumberImpl;
 class MutablePatternModifier;
 class ImmutablePatternModifier;
 struct DecimalFormatWarehouse;
+struct SimpleMicroProps;
+class AdoptingSignumModifierStore;
 
 /**
  * Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
@@ -1436,9 +1439,11 @@ class U_I18N_API Grouper : public UMemory {
     // To allow MacroProps/MicroProps to initialize empty instances:
     friend struct MacroProps;
     friend struct MicroProps;
+    friend struct SimpleMicroProps;
 
     // To allow NumberFormatterImpl to access isBogus() and perform other operations:
     friend class NumberFormatterImpl;
+    friend class ::icu::number::SimpleNumberFormatter;
 
     // To allow NumberParserImpl to perform setLocaleData():
     friend class ::icu::numparse::impl::NumberParserImpl;
@@ -2692,173 +2697,6 @@ class U_I18N_API LocalizedNumberFormatter
 #pragma warning(pop)
 #endif
 
-/**
- * The result of a number formatting operation. This class allows the result to be exported in several data types,
- * including a UnicodeString and a FieldPositionIterator.
- *
- * Instances of this class are immutable and thread-safe.
- *
- * @stable ICU 60
- */
-class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
-  public:
-
-    /**
-     * Default constructor; makes an empty FormattedNumber.
-     * @stable ICU 64
-     */
-    FormattedNumber()
-        : fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {}
-
-    /**
-     * Move constructor: Leaves the source FormattedNumber in an undefined state.
-     * @stable ICU 62
-     */
-    FormattedNumber(FormattedNumber&& src) U_NOEXCEPT;
-
-    /**
-     * Destruct an instance of FormattedNumber.
-     * @stable ICU 60
-     */
-    virtual ~FormattedNumber() U_OVERRIDE;
-
-    /** Copying not supported; use move constructor instead. */
-    FormattedNumber(const FormattedNumber&) = delete;
-
-    /** Copying not supported; use move assignment instead. */
-    FormattedNumber& operator=(const FormattedNumber&) = delete;
-
-    /**
-     * Move assignment: Leaves the source FormattedNumber in an undefined state.
-     * @stable ICU 62
-     */
-    FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT;
-
-    // Copybrief: this method is older than the parent method
-    /**
-     * @copybrief FormattedValue::toString()
-     *
-     * For more information, see FormattedValue::toString()
-     *
-     * @stable ICU 62
-     */
-    UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
-
-    // Copydoc: this method is new in ICU 64
-    /** @copydoc FormattedValue::toTempString() */
-    UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
-
-    // Copybrief: this method is older than the parent method
-    /**
-     * @copybrief FormattedValue::appendTo()
-     *
-     * For more information, see FormattedValue::appendTo()
-     *
-     * @stable ICU 62
-     */
-    Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
-
-    // Copydoc: this method is new in ICU 64
-    /** @copydoc FormattedValue::nextPosition() */
-    UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
-
-    /**
-     * Export the formatted number as a "numeric string" conforming to the
-     * syntax defined in the Decimal Arithmetic Specification, available at
-     * http://speleotrove.com/decimal
-     *
-     * This endpoint is useful for obtaining the exact number being printed
-     * after scaling and rounding have been applied by the number formatter.
-     *
-     * Example call site:
-     *
-     *     auto decimalNumber = fn.toDecimalNumber<std::string>(status);
-     *
-     * @tparam StringClass A string class compatible with StringByteSink;
-     *         for example, std::string.
-     * @param status Set if an error occurs.
-     * @return A StringClass containing the numeric string.
-     * @stable ICU 65
-     */
-    template<typename StringClass>
-    inline StringClass toDecimalNumber(UErrorCode& status) const;
-
-       /**
-     * Gets the resolved output unit.
-     *
-     * The output unit is dependent upon the localized preferences for the usage
-     * specified via NumberFormatterSettings::usage(), and may be a unit with
-     * UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
-     * as "foot-and-inch" or "hour-and-minute-and-second".
-     *
-     * @return `MeasureUnit`.
-     * @stable ICU 68
-     */
-    MeasureUnit getOutputUnit(UErrorCode& status) const;
-
-#ifndef U_HIDE_DRAFT_API
-
-    /**
-     * Gets the noun class of the formatted output. Returns `UNDEFINED` when the noun class
-     * is not supported yet.
-     *
-     * @return UDisplayOptionsNounClass
-     * @draft ICU 72
-     */
-    UDisplayOptionsNounClass getNounClass(UErrorCode &status) const;
-
-#endif // U_HIDE_DRAFT_API
-
-#ifndef U_HIDE_INTERNAL_API
-
-    /**
-     *  Gets the raw DecimalQuantity for plural rule selection.
-     *  @internal
-     */
-    void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const;
-
-    /**
-     * Populates the mutable builder type FieldPositionIteratorHandler.
-     * @internal
-     */
-    void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
-
-#endif  /* U_HIDE_INTERNAL_API */
-
-  private:
-    // Can't use LocalPointer because UFormattedNumberData is forward-declared
-    const impl::UFormattedNumberData *fData;
-
-    // Error code for the terminal methods
-    UErrorCode fErrorCode;
-
-    /**
-     * Internal constructor from data type. Adopts the data pointer.
-     * @internal (private)
-     */
-    explicit FormattedNumber(impl::UFormattedNumberData *results)
-        : fData(results), fErrorCode(U_ZERO_ERROR) {}
-
-    explicit FormattedNumber(UErrorCode errorCode)
-        : fData(nullptr), fErrorCode(errorCode) {}
-
-    void toDecimalNumber(ByteSink& sink, UErrorCode& status) const;
-
-    // To give LocalizedNumberFormatter format methods access to this class's constructor:
-    friend class LocalizedNumberFormatter;
-
-    // To give C API access to internals
-    friend struct impl::UFormattedNumberImpl;
-};
-
-template<typename StringClass>
-StringClass FormattedNumber::toDecimalNumber(UErrorCode& status) const {
-    StringClass result;
-    StringByteSink<StringClass> sink(&result);
-    toDecimalNumber(sink, status);
-    return result;
-}
-
 /**
  * See the main description in numberformatter.h for documentation and examples.
  *
diff --git a/icu4c/source/i18n/unicode/simplenumberformatter.h b/icu4c/source/i18n/unicode/simplenumberformatter.h
new file mode 100644 (file)
index 0000000..45ad42c
--- /dev/null
@@ -0,0 +1,329 @@
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __SIMPLENUMBERFORMATTERH__
+#define __SIMPLENUMBERFORMATTERH__
+
+#include "unicode/utypes.h"
+
+#if U_SHOW_CPLUSPLUS_API
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/dcfmtsym.h"
+#include "unicode/usimplenumberformatter.h"
+#include "unicode/formattednumber.h"
+
+/**
+ * \file
+ * \brief C++ API: Simple number formatting focused on low memory and code size.
+ *
+ * These functions render locale-aware number strings but without the bells and whistles found in
+ * other number formatting APIs such as those in numberformatter.h, like units and currencies.
+ *
+ * <pre>
+ * SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("de-CH", status);
+ * FormattedNumber result = snf.formatInt64(-1000007, status);
+ * assertEquals("", u"-1’000’007", result.toString(status));
+ * </pre>
+ */
+
+U_NAMESPACE_BEGIN
+
+
+namespace number {  // icu::number
+
+
+namespace impl {
+class UFormattedNumberData;
+struct SimpleMicroProps;
+class AdoptingSignumModifierStore;
+}  // icu::number::impl
+
+
+#ifndef U_HIDE_DRAFT_API
+
+
+/**
+ * An input type for SimpleNumberFormatter.
+ *
+ * This class is mutable and not intended for public subclassing. This class is movable but not copyable.
+ *
+ * @draft ICU 73
+ */
+class U_I18N_API SimpleNumber : public UMemory {
+  public:
+    /**
+     * Creates a SimpleNumber for an integer.
+     *
+     * @draft ICU 73
+     */
+    static SimpleNumber forInt64(int64_t value, UErrorCode& status);
+
+    /**
+     * Changes the value of the SimpleNumber by a power of 10.
+     *
+     * This function immediately mutates the inner value.
+     *
+     * @draft ICU 73
+     */
+    void multiplyByPowerOfTen(int32_t power, UErrorCode& status);
+
+    /**
+     * Rounds the value currently stored in the SimpleNumber to the given power of 10.
+     *
+     * This function immediately mutates the inner value.
+     *
+     * @draft ICU 73
+     */
+    void roundTo(int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode& status);
+
+    /**
+     * Truncates the most significant digits to the given maximum number of integer digits.
+     *
+     * This function immediately mutates the inner value.
+     *
+     * @draft ICU 73
+     */
+    void truncateStart(uint32_t maximumIntegerDigits, UErrorCode& status);
+
+    /**
+     * Pads the beginning of the number with zeros up to the given minimum number of integer digits.
+     *
+     * This setting is applied upon formatting the number. 
+     *
+     * @draft ICU 73
+     */
+    void setMinimumIntegerDigits(uint32_t minimumIntegerDigits, UErrorCode& status);
+
+    /**
+     * Pads the end of the number with zeros up to the given minimum number of fraction digits.
+     *
+     * This setting is applied upon formatting the number.
+     *
+     * @draft ICU 73
+     */
+    void setMinimumFractionDigits(uint32_t minimumFractionDigits, UErrorCode& status);
+
+    /**
+     * Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.
+     *
+     * This setting is applied upon formatting the number.
+     *
+     * NOTE: This does not support accounting sign notation.
+     *
+     * @draft ICU 73
+     */
+    void setSign(USimpleNumberSign sign, UErrorCode& status);
+
+    /**
+     * Creates a new, empty SimpleNumber that does not contain a value.
+     * 
+     * NOTE: This number will fail to format; use forInt64() to create a SimpleNumber with a value.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumber() = default;
+
+    /**
+     * Destruct this SimpleNumber, cleaning up any memory it might own.
+     *
+     * @draft ICU 73
+     */
+    ~SimpleNumber() {
+        cleanup();
+    }
+
+    /**
+     * SimpleNumber move constructor.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumber(SimpleNumber&& other) U_NOEXCEPT {
+        fData = other.fData;
+        fSign = other.fSign;
+        other.fData = nullptr;
+    }
+
+    /**
+     * SimpleNumber move assignment.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumber& operator=(SimpleNumber&& other) U_NOEXCEPT {
+        cleanup();
+        fData = other.fData;
+        fSign = other.fSign;
+        other.fData = nullptr;
+        return *this;
+    }
+
+  private:
+    SimpleNumber(impl::UFormattedNumberData* data, UErrorCode& status);
+    SimpleNumber(const SimpleNumber&) = delete;
+    SimpleNumber& operator=(const SimpleNumber&) = delete;
+
+    void cleanup();
+
+    impl::UFormattedNumberData* fData = nullptr;
+    USimpleNumberSign fSign = UNUM_SIMPLE_NUMBER_NO_SIGN;
+
+    friend class SimpleNumberFormatter;
+};
+
+
+/**
+ * A special NumberFormatter focused on smaller binary size and memory use.
+ * 
+ * SimpleNumberFormatter is capable of basic number formatting, including grouping separators,
+ * sign display, and rounding. It is not capable of currencies, compact notation, or units.
+ *
+ * This class is immutable and not intended for public subclassing. This class is movable but not copyable.
+ *
+ * @draft ICU 73
+ */
+class U_I18N_API SimpleNumberFormatter : public UMemory {
+  public:
+    /**
+     * Creates a new SimpleNumberFormatter with all locale defaults.
+     *
+     * @draft ICU 73
+     */
+    static SimpleNumberFormatter forLocale(
+        const icu::Locale &locale,
+        UErrorCode &status);
+
+    /**
+     * Creates a new SimpleNumberFormatter, overriding the grouping strategy.
+     *
+     * @draft ICU 73
+     */
+    static SimpleNumberFormatter forLocaleAndGroupingStrategy(
+        const icu::Locale &locale,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status);
+
+    /**
+     * Creates a new SimpleNumberFormatter, overriding the grouping strategy and symbols.
+     *
+     * IMPORTANT: For efficiency, this function borrows the symbols. The symbols MUST remain valid
+     * for the lifetime of the SimpleNumberFormatter.
+     *
+     * @draft ICU 73
+     */
+    static SimpleNumberFormatter forLocaleAndSymbolsAndGroupingStrategy(
+        const icu::Locale &locale,
+        const DecimalFormatSymbols &symbols,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status);
+
+    /**
+     * Formats a value using this SimpleNumberFormatter.
+     *
+     * The SimpleNumber argument is "consumed". A new SimpleNumber object should be created for
+     * every formatting operation.
+     *
+     * @draft ICU 73
+     */
+    FormattedNumber format(SimpleNumber value, UErrorCode &status) const;
+
+    /**
+     * Formats an integer using this SimpleNumberFormatter.
+     *
+     * For more control over the formatting, use SimpleNumber.
+     *
+     * @draft ICU 73
+     */
+    FormattedNumber formatInt64(int64_t value, UErrorCode &status) const {
+        return format(SimpleNumber::forInt64(value, status), status);
+    }
+
+#ifndef U_HIDE_INTERNAL_API
+    /**
+     * Run the formatter with the internal types.
+     * @internal
+     */
+    void formatImpl(impl::UFormattedNumberData* data, USimpleNumberSign sign, UErrorCode& status) const;
+#endif // U_HIDE_INTERNAL_API
+
+    /**
+     * Destruct this SimpleNumberFormatter, cleaning up any memory it might own.
+     *
+     * @draft ICU 73
+     */
+    ~SimpleNumberFormatter() {
+        cleanup();
+    }
+
+    /**
+     * Creates a shell, initialized but non-functional SimpleNumberFormatter.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumberFormatter() = default;
+
+    /**
+     * SimpleNumberFormatter: Move constructor.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumberFormatter(SimpleNumberFormatter&& other) U_NOEXCEPT {
+        fGroupingStrategy = other.fGroupingStrategy;
+        fOwnedSymbols = other.fOwnedSymbols;
+        fMicros = other.fMicros;
+        fPatternModifier = other.fPatternModifier;
+        other.fOwnedSymbols = nullptr;
+        other.fMicros = nullptr;
+        other.fPatternModifier = nullptr;
+    }
+
+    /**
+     * SimpleNumberFormatter: Move assignment.
+     *
+     * @draft ICU 73
+     */
+    SimpleNumberFormatter& operator=(SimpleNumberFormatter&& other) U_NOEXCEPT {
+        cleanup();
+        fGroupingStrategy = other.fGroupingStrategy;
+        fOwnedSymbols = other.fOwnedSymbols;
+        fMicros = other.fMicros;
+        fPatternModifier = other.fPatternModifier;
+        other.fOwnedSymbols = nullptr;
+        other.fMicros = nullptr;
+        other.fPatternModifier = nullptr;
+        return *this;
+    }
+
+  private:
+    void initialize(
+        const icu::Locale &locale,
+        const DecimalFormatSymbols &symbols,
+        UNumberGroupingStrategy groupingStrategy,
+        UErrorCode &status);
+
+    void cleanup();
+
+    SimpleNumberFormatter(const SimpleNumberFormatter&) = delete;
+
+    SimpleNumberFormatter& operator=(const SimpleNumberFormatter&) = delete;
+
+    UNumberGroupingStrategy fGroupingStrategy = UNUM_GROUPING_AUTO;
+
+    // Owned Pointers:
+    DecimalFormatSymbols* fOwnedSymbols = nullptr; // can be empty
+    impl::SimpleMicroProps* fMicros = nullptr;
+    impl::AdoptingSignumModifierStore* fPatternModifier = nullptr;
+};
+
+
+#endif // U_HIDE_DRAFT_API
+
+}  // namespace number
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif /* U_SHOW_CPLUSPLUS_API */
+
+#endif // __SIMPLENUMBERFORMATTERH__
+
diff --git a/icu4c/source/i18n/unicode/uformattednumber.h b/icu4c/source/i18n/unicode/uformattednumber.h
new file mode 100644 (file)
index 0000000..174a040
--- /dev/null
@@ -0,0 +1,224 @@
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __UFORMATTEDNUMBER_H__
+#define __UFORMATTEDNUMBER_H__
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/ufieldpositer.h"
+#include "unicode/uformattedvalue.h"
+#include "unicode/umisc.h"
+
+/**
+ * \file
+ * \brief C API: Formatted number result from various number formatting functions.
+ *
+ * Create a `UFormattedNumber` to hold the result of a number formatting operation. The same
+ * `UFormattedNumber` can be reused multiple times.
+ *
+ * <pre>
+ * LocalUFormattedNumberPointer uresult(unumf_openResult(status));
+ *
+ * // pass uresult.getAlias() to your number formatter
+ *
+ * int32_t length;
+ * const UChar* s = ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), &length, status));
+ *
+ * // The string result is in `s` with the given `length` (it is also NUL-terminated).
+ * </pre>
+ */
+
+
+struct UFormattedNumber;
+/**
+ * C-compatible version of icu::number::FormattedNumber.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
+ *
+ * @stable ICU 62
+ */
+typedef struct UFormattedNumber UFormattedNumber;
+
+
+/**
+ * Creates an object to hold the result of a UNumberFormatter
+ * operation. The object can be used repeatedly; it is cleared whenever
+ * passed to a format function.
+ *
+ * @param ec Set if an error occurs.
+ * @stable ICU 62
+ */
+U_CAPI UFormattedNumber* U_EXPORT2
+unumf_openResult(UErrorCode* ec);
+
+
+/**
+ * Returns a representation of a UFormattedNumber as a UFormattedValue,
+ * which can be subsequently passed to any API requiring that type.
+ *
+ * The returned object is owned by the UFormattedNumber and is valid
+ * only as long as the UFormattedNumber is present and unchanged in memory.
+ *
+ * You can think of this method as a cast between types.
+ *
+ * @param uresult The object containing the formatted string.
+ * @param ec Set if an error occurs.
+ * @return A UFormattedValue owned by the input object.
+ * @stable ICU 64
+ */
+U_CAPI const UFormattedValue* U_EXPORT2
+unumf_resultAsValue(const UFormattedNumber* uresult, UErrorCode* ec);
+
+
+/**
+ * Extracts the result number string out of a UFormattedNumber to a UChar buffer if possible.
+ * If bufferCapacity is greater than the required length, a terminating NUL is written.
+ * If bufferCapacity is less than the required length, an error code is set.
+ *
+ * Also see ufmtval_getString, which returns a NUL-terminated string:
+ *
+ *     int32_t len;
+ *     const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
+ *
+ * @param uresult The object containing the formatted number.
+ * @param buffer Where to save the string output.
+ * @param bufferCapacity The number of UChars available in the buffer.
+ * @param ec Set if an error occurs.
+ * @return The required length.
+ * @stable ICU 62
+ */
+U_CAPI int32_t U_EXPORT2
+unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
+                     UErrorCode* ec);
+
+
+/**
+ * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
+ * output string. This allows you to determine the locations of, for example, the integer part,
+ * fraction part, or symbols.
+ *
+ * This is a simpler but less powerful alternative to {@link ufmtval_nextPosition}.
+ *
+ * If a field occurs just once, calling this method will find that occurrence and return it. If a
+ * field occurs multiple times, this method may be called repeatedly with the following pattern:
+ *
+ * <pre>
+ * UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
+ * while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
+ *   // do something with ufpos.
+ * }
+ * </pre>
+ *
+ * This method is useful if you know which field to query. If you want all available field position
+ * information, use unumf_resultGetAllFieldPositions().
+ *
+ * NOTE: All fields of the UFieldPosition must be initialized before calling this method.
+ *
+ * @param uresult The object containing the formatted number.
+ * @param ufpos
+ *            Input+output variable. On input, the "field" property determines which field to look up,
+ *            and the "endIndex" property determines where to begin the search. On output, the
+ *            "beginIndex" field is set to the beginning of the first occurrence of the field after the
+ *            input "endIndex", and "endIndex" is set to the end of that occurrence of the field
+ *            (exclusive index). If a field position is not found, the FieldPosition is not changed and
+ *            the method returns false.
+ * @param ec Set if an error occurs.
+ * @stable ICU 62
+ */
+U_CAPI UBool U_EXPORT2
+unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
+
+
+/**
+ * Populates the given iterator with all fields in the formatted output string. This allows you to
+ * determine the locations of the integer part, fraction part, and sign.
+ *
+ * This is an alternative to the more powerful {@link ufmtval_nextPosition} API.
+ *
+ * If you need information on only one field, use {@link ufmtval_nextPosition} or
+ * {@link unumf_resultNextFieldPosition}.
+ *
+ * @param uresult The object containing the formatted number.
+ * @param ufpositer
+ *         A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration
+ *         information already present in the UFieldPositionIterator is deleted, and the iterator is reset
+ *         to apply to the fields in the formatted string created by this function call. The field values
+ *         and indexes returned by {@link #ufieldpositer_next} represent fields denoted by
+ *         the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot
+ *         overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a
+ *         grouping separator field for ',' and an integer field encompassing the entire string.
+ * @param ec Set if an error occurs.
+ * @stable ICU 62
+ */
+U_CAPI void U_EXPORT2
+unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
+                                 UErrorCode* ec);
+
+
+/**
+ * Extracts the formatted number as a "numeric string" conforming to the
+ * syntax defined in the Decimal Arithmetic Specification, available at
+ * http://speleotrove.com/decimal
+ *
+ * This endpoint is useful for obtaining the exact number being printed
+ * after scaling and rounding have been applied by the number formatter.
+ *
+ * @param uresult        The input object containing the formatted number.
+ * @param  dest          the 8-bit char buffer into which the decimal number is placed
+ * @param  destCapacity  The size, in chars, of the destination buffer.  May be zero
+ *                       for precomputing the required size.
+ * @param  ec            receives any error status.
+ *                       If U_BUFFER_OVERFLOW_ERROR: Returns number of chars for
+ *                       preflighting.
+ * @return Number of chars in the data.  Does not include a trailing NUL.
+ * @stable ICU 68
+ */
+U_CAPI int32_t U_EXPORT2
+unumf_resultToDecimalNumber(
+       const UFormattedNumber* uresult,
+       char* dest,
+       int32_t destCapacity,
+       UErrorCode* ec);
+
+
+/**
+ * Releases the UFormattedNumber created by unumf_openResult().
+ *
+ * @param uresult An object created by unumf_openResult().
+ * @stable ICU 62
+ */
+U_CAPI void U_EXPORT2
+unumf_closeResult(UFormattedNumber* uresult);
+
+
+#if U_SHOW_CPLUSPLUS_API
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUFormattedNumberPointer
+ * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
+ * // no need to explicitly call unumf_closeResult()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @stable ICU 62
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult);
+
+U_NAMESPACE_END
+#endif // U_SHOW_CPLUSPLUS_API
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UFORMATTEDNUMBER_H__
index a392afaaed2c2a8ba80b18ad67dd04a399c1167d..0c2e378e6d6888c3ec080e985aa8aa2c24eb75f7 100644 (file)
@@ -25,6 +25,7 @@
 #include "unicode/uformattable.h"
 #include "unicode/udisplaycontext.h"
 #include "unicode/ufieldpositer.h"
+#include "unicode/unumberoptions.h"
 
 #if U_SHOW_CPLUSPLUS_API
 #include "unicode/localpointer.h"
@@ -271,55 +272,6 @@ typedef enum UNumberFormatStyle {
     UNUM_IGNORE = UNUM_PATTERN_DECIMAL
 } UNumberFormatStyle;
 
-/** The possible number format rounding modes.
- *
- * <p>
- * For more detail on rounding modes, see:
- * https://unicode-org.github.io/icu/userguide/format_parse/numbers/rounding-modes
- *
- * @stable ICU 2.0
- */
-typedef enum UNumberFormatRoundingMode {
-    UNUM_ROUND_CEILING,
-    UNUM_ROUND_FLOOR,
-    UNUM_ROUND_DOWN,
-    UNUM_ROUND_UP,
-    /**
-     * Half-even rounding
-     * @stable, ICU 3.8
-     */
-    UNUM_ROUND_HALFEVEN,
-#ifndef U_HIDE_DEPRECATED_API
-    /**
-     * Half-even rounding, misspelled name
-     * @deprecated, ICU 3.8
-     */
-    UNUM_FOUND_HALFEVEN = UNUM_ROUND_HALFEVEN,
-#endif  /* U_HIDE_DEPRECATED_API */
-    UNUM_ROUND_HALFDOWN = UNUM_ROUND_HALFEVEN + 1,
-    UNUM_ROUND_HALFUP,
-    /** 
-      * ROUND_UNNECESSARY reports an error if formatted result is not exact.
-      * @stable ICU 4.8
-      */
-    UNUM_ROUND_UNNECESSARY,
-    /**
-     * Rounds ties toward the odd number.
-     * @stable ICU 69
-     */
-    UNUM_ROUND_HALF_ODD,
-    /**
-     * Rounds ties toward +∞.
-     * @stable ICU 69
-     */
-    UNUM_ROUND_HALF_CEILING,
-    /**
-     * Rounds ties toward -∞.
-     * @stable ICU 69
-     */
-    UNUM_ROUND_HALF_FLOOR,
-} UNumberFormatRoundingMode;
-
 /** The possible number format pad positions. 
  *  @stable ICU 2.0
  */
index 253b30b5342e077df1d2c3f7315d81836808608a..09fa000b826b243327166558ec28a7359ca3187b 100644 (file)
@@ -9,9 +9,8 @@
 #if !UCONFIG_NO_FORMATTING
 
 #include "unicode/parseerr.h"
-#include "unicode/ufieldpositer.h"
-#include "unicode/umisc.h"
-#include "unicode/uformattedvalue.h"
+#include "unicode/unumberoptions.h"
+#include "unicode/uformattednumber.h"
 
 
 /**
@@ -243,107 +242,6 @@ typedef enum UNumberUnitWidth {
             UNUM_UNIT_WIDTH_COUNT = 7
 } UNumberUnitWidth;
 
-/**
- * An enum declaring the strategy for when and how to display grouping separators (i.e., the
- * separator, often a comma or period, after every 2-3 powers of ten). The choices are several
- * pre-built strategies for different use cases that employ locale data whenever possible. Example
- * outputs for 1234 and 1234567 in <em>en-IN</em>:
- *
- * <ul>
- * <li>OFF: 1234 and 12345
- * <li>MIN2: 1234 and 12,34,567
- * <li>AUTO: 1,234 and 12,34,567
- * <li>ON_ALIGNED: 1,234 and 12,34,567
- * <li>THOUSANDS: 1,234 and 1,234,567
- * </ul>
- *
- * <p>
- * The default is AUTO, which displays grouping separators unless the locale data says that grouping
- * is not customary. To force grouping for all numbers greater than 1000 consistently across locales,
- * use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2
- * or OFF. See the docs of each option for details.
- *
- * <p>
- * Note: This enum specifies the strategy for grouping sizes. To set which character to use as the
- * grouping separator, use the "symbols" setter.
- *
- * @stable ICU 63
- */
-typedef enum UNumberGroupingStrategy {
-    /**
-     * Do not display grouping separators in any locale.
-     *
-     * @stable ICU 61
-     */
-            UNUM_GROUPING_OFF,
-
-    /**
-     * Display grouping using locale defaults, except do not show grouping on values smaller than
-     * 10000 (such that there is a <em>minimum of two digits</em> before the first separator).
-     *
-     * <p>
-     * Note that locales may restrict grouping separators to be displayed only on 1 million or
-     * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
-     *
-     * <p>
-     * Locale data is used to determine whether to separate larger numbers into groups of 2
-     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
-     *
-     * @stable ICU 61
-     */
-            UNUM_GROUPING_MIN2,
-
-    /**
-     * Display grouping using the default strategy for all locales. This is the default behavior.
-     *
-     * <p>
-     * Note that locales may restrict grouping separators to be displayed only on 1 million or
-     * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
-     *
-     * <p>
-     * Locale data is used to determine whether to separate larger numbers into groups of 2
-     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
-     *
-     * @stable ICU 61
-     */
-            UNUM_GROUPING_AUTO,
-
-    /**
-     * Always display the grouping separator on values of at least 1000.
-     *
-     * <p>
-     * This option ignores the locale data that restricts or disables grouping, described in MIN2 and
-     * AUTO. This option may be useful to normalize the alignment of numbers, such as in a
-     * spreadsheet.
-     *
-     * <p>
-     * Locale data is used to determine whether to separate larger numbers into groups of 2
-     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
-     *
-     * @stable ICU 61
-     */
-            UNUM_GROUPING_ON_ALIGNED,
-
-    /**
-     * Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use
-     * locale data for determining the grouping strategy.
-     *
-     * @stable ICU 61
-     */
-            UNUM_GROUPING_THOUSANDS
-
-#ifndef U_HIDE_INTERNAL_API
-    ,
-    /**
-     * One more than the highest UNumberGroupingStrategy value.
-     *
-     * @internal ICU 62: The numeric value may change over time; see ICU ticket #12420.
-     */
-            UNUM_GROUPING_COUNT
-#endif  /* U_HIDE_INTERNAL_API */
-
-} UNumberGroupingStrategy;
-
 /**
  * An enum declaring how to denote positive and negative numbers. Example outputs when formatting
  * 123, 0, and -123 in <em>en-US</em>:
@@ -528,16 +426,6 @@ struct UNumberFormatter;
  */
 typedef struct UNumberFormatter UNumberFormatter;
 
-struct UFormattedNumber;
-/**
- * C-compatible version of icu::number::FormattedNumber.
- *
- * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
- *
- * @stable ICU 62
- */
-typedef struct UFormattedNumber UFormattedNumber;
-
 
 /**
  * Creates a new UNumberFormatter for the given skeleton string and locale. This is currently the only
@@ -584,17 +472,6 @@ unumf_openForSkeletonAndLocaleWithError(
        const UChar* skeleton, int32_t skeletonLen, const char* locale, UParseError* perror, UErrorCode* ec);
 
 
-/**
- * Creates an object to hold the result of a UNumberFormatter
- * operation. The object can be used repeatedly; it is cleared whenever
- * passed to a format function.
- *
- * @param ec Set if an error occurs.
- * @stable ICU 62
- */
-U_CAPI UFormattedNumber* U_EXPORT2
-unumf_openResult(UErrorCode* ec);
-
 
 /**
  * Uses a UNumberFormatter to format an integer to a UFormattedNumber. A string, field position, and other
@@ -659,135 +536,6 @@ U_CAPI void U_EXPORT2
 unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32_t valueLen,
                     UFormattedNumber* uresult, UErrorCode* ec);
 
-/**
- * Returns a representation of a UFormattedNumber as a UFormattedValue,
- * which can be subsequently passed to any API requiring that type.
- *
- * The returned object is owned by the UFormattedNumber and is valid
- * only as long as the UFormattedNumber is present and unchanged in memory.
- *
- * You can think of this method as a cast between types.
- *
- * @param uresult The object containing the formatted string.
- * @param ec Set if an error occurs.
- * @return A UFormattedValue owned by the input object.
- * @stable ICU 64
- */
-U_CAPI const UFormattedValue* U_EXPORT2
-unumf_resultAsValue(const UFormattedNumber* uresult, UErrorCode* ec);
-
-
-/**
- * Extracts the result number string out of a UFormattedNumber to a UChar buffer if possible.
- * If bufferCapacity is greater than the required length, a terminating NUL is written.
- * If bufferCapacity is less than the required length, an error code is set.
- *
- * Also see ufmtval_getString, which returns a NUL-terminated string:
- *
- *     int32_t len;
- *     const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
- *
- * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
- *
- * @param uresult The object containing the formatted number.
- * @param buffer Where to save the string output.
- * @param bufferCapacity The number of UChars available in the buffer.
- * @param ec Set if an error occurs.
- * @return The required length.
- * @stable ICU 62
- */
-U_CAPI int32_t U_EXPORT2
-unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
-                     UErrorCode* ec);
-
-
-/**
- * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
- * output string. This allows you to determine the locations of, for example, the integer part,
- * fraction part, or symbols.
- *
- * This is a simpler but less powerful alternative to {@link ufmtval_nextPosition}.
- *
- * If a field occurs just once, calling this method will find that occurrence and return it. If a
- * field occurs multiple times, this method may be called repeatedly with the following pattern:
- *
- * <pre>
- * UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
- * while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
- *   // do something with ufpos.
- * }
- * </pre>
- *
- * This method is useful if you know which field to query. If you want all available field position
- * information, use unumf_resultGetAllFieldPositions().
- *
- * NOTE: All fields of the UFieldPosition must be initialized before calling this method.
- *
- * @param uresult The object containing the formatted number.
- * @param ufpos
- *            Input+output variable. On input, the "field" property determines which field to look up,
- *            and the "endIndex" property determines where to begin the search. On output, the
- *            "beginIndex" field is set to the beginning of the first occurrence of the field after the
- *            input "endIndex", and "endIndex" is set to the end of that occurrence of the field
- *            (exclusive index). If a field position is not found, the FieldPosition is not changed and
- *            the method returns false.
- * @param ec Set if an error occurs.
- * @stable ICU 62
- */
-U_CAPI UBool U_EXPORT2
-unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
-
-
-/**
- * Populates the given iterator with all fields in the formatted output string. This allows you to
- * determine the locations of the integer part, fraction part, and sign.
- *
- * This is an alternative to the more powerful {@link ufmtval_nextPosition} API.
- *
- * If you need information on only one field, use {@link ufmtval_nextPosition} or
- * {@link unumf_resultNextFieldPosition}.
- *
- * @param uresult The object containing the formatted number.
- * @param ufpositer
- *         A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration
- *         information already present in the UFieldPositionIterator is deleted, and the iterator is reset
- *         to apply to the fields in the formatted string created by this function call. The field values
- *         and indexes returned by {@link #ufieldpositer_next} represent fields denoted by
- *         the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot
- *         overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a
- *         grouping separator field for ',' and an integer field encompassing the entire string.
- * @param ec Set if an error occurs.
- * @stable ICU 62
- */
-U_CAPI void U_EXPORT2
-unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
-                                 UErrorCode* ec);
-
-
-/**
- * Extracts the formatted number as a "numeric string" conforming to the
- * syntax defined in the Decimal Arithmetic Specification, available at
- * http://speleotrove.com/decimal
- *
- * This endpoint is useful for obtaining the exact number being printed
- * after scaling and rounding have been applied by the number formatter.
- *
- * @param uresult        The input object containing the formatted number.
- * @param  dest          the 8-bit char buffer into which the decimal number is placed
- * @param  destCapacity  The size, in chars, of the destination buffer.  May be zero
- *                       for precomputing the required size.
- * @param  ec            receives any error status.
- *                       If U_BUFFER_OVERFLOW_ERROR: Returns number of chars for
- *                       preflighting.
- * @return Number of chars in the data.  Does not include a trailing NUL.
- * @stable ICU 68
- */
-U_CAPI int32_t U_EXPORT2
-unumf_resultToDecimalNumber(
-       const UFormattedNumber* uresult,
-       char* dest,
-       int32_t destCapacity,
-       UErrorCode* ec);
 
 
 /**
@@ -800,15 +548,6 @@ U_CAPI void U_EXPORT2
 unumf_close(UNumberFormatter* uformatter);
 
 
-/**
- * Releases the UFormattedNumber created by unumf_openResult().
- *
- * @param uresult An object created by unumf_openResult().
- * @stable ICU 62
- */
-U_CAPI void U_EXPORT2
-unumf_closeResult(UFormattedNumber* uresult);
-
 
 #if U_SHOW_CPLUSPLUS_API
 U_NAMESPACE_BEGIN
@@ -830,23 +569,6 @@ U_NAMESPACE_BEGIN
  */
 U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberFormatterPointer, UNumberFormatter, unumf_close);
 
-/**
- * \class LocalUFormattedNumberPointer
- * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
- * For most methods see the LocalPointerBase base class.
- *
- * Usage:
- * <pre>
- * LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
- * // no need to explicitly call unumf_closeResult()
- * </pre>
- *
- * @see LocalPointerBase
- * @see LocalPointer
- * @stable ICU 62
- */
-U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult);
-
 U_NAMESPACE_END
 #endif // U_SHOW_CPLUSPLUS_API
 
diff --git a/icu4c/source/i18n/unicode/unumberoptions.h b/icu4c/source/i18n/unicode/unumberoptions.h
new file mode 100644 (file)
index 0000000..3fa8df5
--- /dev/null
@@ -0,0 +1,173 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __UNUMBEROPTIONS_H__
+#define __UNUMBEROPTIONS_H__
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+/**
+ * \file
+ * \brief C API: Header-only input options for various number formatting APIs.
+ *
+ * You do not normally need to include this header file directly, because it is included in all
+ * files that use these enums.
+ */
+
+
+/** The possible number format rounding modes.
+ *
+ * <p>
+ * For more detail on rounding modes, see:
+ * https://unicode-org.github.io/icu/userguide/format_parse/numbers/rounding-modes
+ *
+ * @stable ICU 2.0
+ */
+typedef enum UNumberFormatRoundingMode {
+    UNUM_ROUND_CEILING,
+    UNUM_ROUND_FLOOR,
+    UNUM_ROUND_DOWN,
+    UNUM_ROUND_UP,
+    /**
+     * Half-even rounding
+     * @stable, ICU 3.8
+     */
+    UNUM_ROUND_HALFEVEN,
+#ifndef U_HIDE_DEPRECATED_API
+    /**
+     * Half-even rounding, misspelled name
+     * @deprecated, ICU 3.8
+     */
+    UNUM_FOUND_HALFEVEN = UNUM_ROUND_HALFEVEN,
+#endif  /* U_HIDE_DEPRECATED_API */
+    UNUM_ROUND_HALFDOWN = UNUM_ROUND_HALFEVEN + 1,
+    UNUM_ROUND_HALFUP,
+    /** 
+      * ROUND_UNNECESSARY reports an error if formatted result is not exact.
+      * @stable ICU 4.8
+      */
+    UNUM_ROUND_UNNECESSARY,
+    /**
+     * Rounds ties toward the odd number.
+     * @stable ICU 69
+     */
+    UNUM_ROUND_HALF_ODD,
+    /**
+     * Rounds ties toward +∞.
+     * @stable ICU 69
+     */
+    UNUM_ROUND_HALF_CEILING,
+    /**
+     * Rounds ties toward -∞.
+     * @stable ICU 69
+     */
+    UNUM_ROUND_HALF_FLOOR,
+} UNumberFormatRoundingMode;
+
+
+/**
+ * An enum declaring the strategy for when and how to display grouping separators (i.e., the
+ * separator, often a comma or period, after every 2-3 powers of ten). The choices are several
+ * pre-built strategies for different use cases that employ locale data whenever possible. Example
+ * outputs for 1234 and 1234567 in <em>en-IN</em>:
+ *
+ * <ul>
+ * <li>OFF: 1234 and 12345
+ * <li>MIN2: 1234 and 12,34,567
+ * <li>AUTO: 1,234 and 12,34,567
+ * <li>ON_ALIGNED: 1,234 and 12,34,567
+ * <li>THOUSANDS: 1,234 and 1,234,567
+ * </ul>
+ *
+ * <p>
+ * The default is AUTO, which displays grouping separators unless the locale data says that grouping
+ * is not customary. To force grouping for all numbers greater than 1000 consistently across locales,
+ * use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2
+ * or OFF. See the docs of each option for details.
+ *
+ * <p>
+ * Note: This enum specifies the strategy for grouping sizes. To set which character to use as the
+ * grouping separator, use the "symbols" setter.
+ *
+ * @stable ICU 63
+ */
+typedef enum UNumberGroupingStrategy {
+    /**
+     * Do not display grouping separators in any locale.
+     *
+     * @stable ICU 61
+     */
+            UNUM_GROUPING_OFF,
+
+    /**
+     * Display grouping using locale defaults, except do not show grouping on values smaller than
+     * 10000 (such that there is a <em>minimum of two digits</em> before the first separator).
+     *
+     * <p>
+     * Note that locales may restrict grouping separators to be displayed only on 1 million or
+     * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
+     *
+     * <p>
+     * Locale data is used to determine whether to separate larger numbers into groups of 2
+     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
+     *
+     * @stable ICU 61
+     */
+            UNUM_GROUPING_MIN2,
+
+    /**
+     * Display grouping using the default strategy for all locales. This is the default behavior.
+     *
+     * <p>
+     * Note that locales may restrict grouping separators to be displayed only on 1 million or
+     * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
+     *
+     * <p>
+     * Locale data is used to determine whether to separate larger numbers into groups of 2
+     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
+     *
+     * @stable ICU 61
+     */
+            UNUM_GROUPING_AUTO,
+
+    /**
+     * Always display the grouping separator on values of at least 1000.
+     *
+     * <p>
+     * This option ignores the locale data that restricts or disables grouping, described in MIN2 and
+     * AUTO. This option may be useful to normalize the alignment of numbers, such as in a
+     * spreadsheet.
+     *
+     * <p>
+     * Locale data is used to determine whether to separate larger numbers into groups of 2
+     * (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
+     *
+     * @stable ICU 61
+     */
+            UNUM_GROUPING_ON_ALIGNED,
+
+    /**
+     * Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use
+     * locale data for determining the grouping strategy.
+     *
+     * @stable ICU 61
+     */
+            UNUM_GROUPING_THOUSANDS
+
+#ifndef U_HIDE_INTERNAL_API
+    ,
+    /**
+     * One more than the highest UNumberGroupingStrategy value.
+     *
+     * @internal ICU 62: The numeric value may change over time; see ICU ticket #12420.
+     */
+            UNUM_GROUPING_COUNT
+#endif  /* U_HIDE_INTERNAL_API */
+
+} UNumberGroupingStrategy;
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UNUMBEROPTIONS_H__
diff --git a/icu4c/source/i18n/unicode/usimplenumberformatter.h b/icu4c/source/i18n/unicode/usimplenumberformatter.h
new file mode 100644 (file)
index 0000000..c045d7a
--- /dev/null
@@ -0,0 +1,273 @@
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __USIMPLENUMBERFORMATTER_H__
+#define __USIMPLENUMBERFORMATTER_H__
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/uformattednumber.h"
+#include "unicode/unumberoptions.h"
+
+/**
+ * \file
+ * \brief C API: Simple number formatting focused on low memory and code size.
+ *
+ * These functions render locale-aware number strings but without the bells and whistles found in
+ * other number formatting APIs such as those in unumberformatter.h, like units and currencies.
+ *
+ * <pre>
+ * LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale("de-CH", status));
+ * LocalUFormattedNumberPointer uresult(unumf_openResult(status));
+ * usnumf_formatInt64(uformatter.getAlias(), 55, uresult.getAlias(), status);
+ * assertEquals("",
+ *     u"55",
+ *     ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
+ * </pre>
+ */
+
+#ifndef U_HIDE_DRAFT_API
+
+
+/**
+ * An explicit sign option for a SimpleNumber.
+ *
+ * @draft ICU 73
+ */
+typedef enum USimpleNumberSign {
+    /**
+     * Render a plus sign.
+     *
+     * @draft ICU 73
+     */
+    UNUM_SIMPLE_NUMBER_PLUS_SIGN,
+    /**
+     * Render no sign.
+     *
+     * @draft ICU 73
+     */
+    UNUM_SIMPLE_NUMBER_NO_SIGN,
+    /**
+     * Render a minus sign.
+     *
+     * @draft ICU 73
+     */
+    UNUM_SIMPLE_NUMBER_MINUS_SIGN,
+} USimpleNumberSign;
+
+
+struct USimpleNumber;
+/**
+ * C-compatible version of icu::number::SimpleNumber.
+ *
+ * @draft ICU 73
+ */
+typedef struct USimpleNumber USimpleNumber;
+
+
+struct USimpleNumberFormatter;
+/**
+ * C-compatible version of icu::number::SimpleNumberFormatter.
+ *
+ * @draft ICU 73
+ */
+typedef struct USimpleNumberFormatter USimpleNumberFormatter;
+
+
+/**
+ * Creates a new USimpleNumber to be formatted with a USimpleNumberFormatter.
+ *
+ * @draft ICU 73
+ */
+U_CAPI USimpleNumber* U_EXPORT2
+usnum_openForInt64(int64_t value, UErrorCode* ec);
+
+
+/**
+ * Changes the value of the USimpleNumber by a power of 10.
+ *
+ * This function immediately mutates the inner value.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec);
+
+
+/**
+ * Rounds the value currently stored in the USimpleNumber to the given power of 10.
+ *
+ * This function immediately mutates the inner value.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_roundTo(USimpleNumber* unumber, int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode* ec);
+
+
+/**
+ * Pads the beginning of the number with zeros up to the given minimum number of integer digits.
+ *
+ * This setting is applied upon formatting the number.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec);
+
+
+/**
+ * Pads the end of the number with zeros up to the given minimum number of fraction digits.
+ *
+ * This setting is applied upon formatting the number.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec);
+
+
+/**
+ * Truncates digits from the beginning of the number to the given maximum number of integer digits.
+ *
+ * This function immediately mutates the inner value.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec);
+
+
+/**
+ * Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.
+ *
+ * This setting is applied upon formatting the number.
+ *
+ * NOTE: This does not support accounting sign notation.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_setSign(USimpleNumber* unumber, USimpleNumberSign sign, UErrorCode* ec);
+
+
+/**
+ * Creates a new USimpleNumberFormatter with all locale defaults.
+ *
+ * @draft ICU 73
+ */
+U_CAPI USimpleNumberFormatter* U_EXPORT2
+usnumf_openForLocale(const char* locale, UErrorCode* ec);
+
+
+/**
+ * Creates a new USimpleNumberFormatter, overriding the grouping strategy.
+ *
+ * @draft ICU 73
+ */
+U_CAPI USimpleNumberFormatter* U_EXPORT2
+usnumf_openForLocaleAndGroupingStrategy(
+       const char* locale, UNumberGroupingStrategy groupingStrategy, UErrorCode* ec);
+
+
+/**
+ * Formats a number using this SimpleNumberFormatter.
+ *
+ * The USimpleNumber is adopted and must not be freed after calling this function,
+ * even if the function sets an error code. If you use LocalUSimpleNumberPointer,
+ * call `.orphan()` when passing it to this function.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnumf_formatAndAdoptNumber(
+    const USimpleNumberFormatter* uformatter,
+    USimpleNumber* unumber,
+    UFormattedNumber* uresult,
+    UErrorCode* ec);
+
+
+/**
+ * Formats an integer using this SimpleNumberFormatter.
+ *
+ * For more control over the formatting, use USimpleNumber.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnumf_formatInt64(
+    const USimpleNumberFormatter* uformatter,
+    int64_t value,
+    UFormattedNumber* uresult,
+    UErrorCode* ec);
+
+
+/**
+ * Frees the memory held by a USimpleNumber.
+ *
+ * NOTE: Normally, a USimpleNumber should be adopted by usnumf_formatAndAdoptNumber.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnum_close(USimpleNumber* unumber);
+
+
+/**
+ * Frees the memory held by a USimpleNumberFormatter.
+ *
+ * @draft ICU 73
+ */
+U_CAPI void U_EXPORT2
+usnumf_close(USimpleNumberFormatter* uformatter);
+
+
+#if U_SHOW_CPLUSPLUS_API
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUSimpleNumberPointer
+ * "Smart pointer" class; closes a USimpleNumber via usnum_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * NOTE: Normally, a USimpleNumber should be adopted by usnumf_formatAndAdoptNumber.
+ * If you use LocalUSimpleNumberPointer, call `.orphan()` when passing to that function.
+ *
+ * Usage:
+ * <pre>
+ * LocalUSimpleNumberPointer uformatter(usnumf_openForInteger(...));
+ * // no need to explicitly call usnum_close()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 73
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUSimpleNumberPointer, USimpleNumber, usnum_close);
+
+/**
+ * \class LocalUSimpleNumberFormatterPointer
+ * "Smart pointer" class; closes a USimpleNumberFormatter via usnumf_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale(...));
+ * // no need to explicitly call usnumf_close()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 73
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUSimpleNumberFormatterPointer, USimpleNumberFormatter, usnumf_close);
+
+U_NAMESPACE_END
+#endif // U_SHOW_CPLUSPLUS_API
+
+#endif // U_HIDE_DRAFT_API
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__USIMPLENUMBERFORMATTER_H__
index 878dd95860aec42f468d81fe6daf9416adac1699..9676ed4856d7ce56523ab4330f28d3e7ed5e4ca9 100644 (file)
@@ -1007,6 +1007,9 @@ group: numberformatter
     number_scientific.o
     currpinf.o
     numrange_fluent.o numrange_impl.o
+    # NOTE: This could go into its own dependency block, but it would require
+    # refactoring more of the dependencies (e.g. removing class methods).
+    number_simple.o
   deps
     decnumber double_conversion formattable units unitsformatter
     listformatter number_representation number_output
index c4b5f6097bf446ed7f5444fa6f92311276483ab6..8007d3c18800422eefd34a01a3d1b52f781a3846 100644 (file)
@@ -69,7 +69,8 @@ string_segment_test.o \
 numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
 static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \
 formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \
-units_data_test.o units_router_test.o units_test.o displayoptions_test.o
+units_data_test.o units_router_test.o units_test.o displayoptions_test.o \
+numbertest_simple.o
 
 DEPS = $(OBJECTS:.o=.d)
 
index dd3cc6f1cbc393133d2d04e99d97dbd08408f92b..0985ba1e808c1ca4466e9832645700b3d9a1e184 100644 (file)
     <ClCompile Include="numbertest_doubleconversion.cpp" />
     <ClCompile Include="numbertest_skeletons.cpp" />
     <ClCompile Include="numbertest_range.cpp" />
+    <ClCompile Include="numbertest_simple.cpp" />
     <ClCompile Include="numbertest_permutation.cpp" />
     <ClCompile Include="numfmtst.cpp" />
     <ClCompile Include="numfmtdatadriventest.cpp" />
index ebf3c7ec2aa3004b2c5aef2d46bec1d3be89d3a2..ffe9bc1467d3de09f5e3c1c5e9aad9b8bc72fa5b 100644 (file)
     <ClCompile Include="numbertest_range.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="numbertest_simple.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="numbertest_permutation.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
index f1d3b50a2a421a1af2508b104dd8b9687f0fb177..84a8e77cda199847833f089d9ef4b1bd6a4795bf 100644 (file)
@@ -365,6 +365,18 @@ class NumberRangeFormatterTest : public IntlTestWithFieldPosition {
       const char16_t* expected);
 };
 
+class SimpleNumberFormatterTest : public IntlTestWithFieldPosition {
+  public:
+    void testBasic();
+    void testWithOptions();
+    void testSymbols();
+    void testSign();
+    void testCopyMove();
+    void testCAPI();
+
+    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
+};
+
 class NumberPermutationTest : public IntlTest {
   public:
     void testPermutations();
@@ -403,7 +415,8 @@ class NumberTest : public IntlTest {
         TESTCLASS(7, NumberParserTest);
         TESTCLASS(8, NumberSkeletonTest);
         TESTCLASS(9, NumberRangeFormatterTest);
-        TESTCLASS(10, NumberPermutationTest);
+        TESTCLASS(10, SimpleNumberFormatterTest);
+        TESTCLASS(11, NumberPermutationTest);
         default: name = ""; break; // needed to end loop
         }
     }
diff --git a/icu4c/source/test/intltest/numbertest_simple.cpp b/icu4c/source/test/intltest/numbertest_simple.cpp
new file mode 100644 (file)
index 0000000..e6e42e7
--- /dev/null
@@ -0,0 +1,206 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include <cmath>
+
+#include "numbertest.h"
+#include "unicode/simplenumberformatter.h"
+
+
+void SimpleNumberFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
+    if (exec) {
+        logln("TestSuite SimpleNumberFormatterTest: ");
+    }
+    TESTCASE_AUTO_BEGIN;
+        TESTCASE_AUTO(testBasic);
+        TESTCASE_AUTO(testWithOptions);
+        TESTCASE_AUTO(testSymbols);
+        TESTCASE_AUTO(testSign);
+        TESTCASE_AUTO(testCopyMove);
+        TESTCASE_AUTO(testCAPI);
+    TESTCASE_AUTO_END;
+}
+
+void SimpleNumberFormatterTest::testBasic() {
+    IcuTestErrorCode status(*this, "testBasic");
+
+    SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("de-CH", status);
+    FormattedNumber result = snf.formatInt64(-1000007, status);
+
+    static const UFieldPosition expectedFieldPositions[] = {
+        // field, begin index, end index
+        {UNUM_SIGN_FIELD, 0, 1},
+        {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
+        {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
+        {UNUM_INTEGER_FIELD, 1, 10},
+    };
+    checkFormattedValue(
+        u"testBasic",
+        result,
+        u"-1’000’007",
+        UFIELD_CATEGORY_NUMBER,
+        expectedFieldPositions,
+        UPRV_LENGTHOF(expectedFieldPositions));
+}
+
+void SimpleNumberFormatterTest::testWithOptions() {
+    IcuTestErrorCode status(*this, "testWithOptions");
+
+    SimpleNumber num = SimpleNumber::forInt64(1250000, status);
+    num.setMinimumIntegerDigits(6, status);
+    num.setMinimumFractionDigits(2, status);
+    num.multiplyByPowerOfTen(-2, status);
+    num.roundTo(3, UNUM_ROUND_HALFUP, status);
+    num.truncateStart(4, status);
+    SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("bn", status);
+    FormattedNumber result = snf.format(std::move(num), status);
+
+    static const UFieldPosition expectedFieldPositions[] = {
+        // field, begin index, end index
+        {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
+        {UNUM_GROUPING_SEPARATOR_FIELD, 4, 5},
+        {UNUM_INTEGER_FIELD, 0, 8},
+        {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
+        {UNUM_FRACTION_FIELD, 9, 11},
+    };
+    checkFormattedValue(
+        u"testWithOptions",
+        result,
+        u"০,০৩,০০০.০০",
+        UFIELD_CATEGORY_NUMBER,
+        expectedFieldPositions,
+        UPRV_LENGTHOF(expectedFieldPositions));
+}
+
+void SimpleNumberFormatterTest::testSymbols() {
+    IcuTestErrorCode status(*this, "testSymbols");
+
+    LocalPointer<DecimalFormatSymbols> symbols(new DecimalFormatSymbols("bn", status), status);
+    SimpleNumberFormatter snf = SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy(
+        "en-US",
+        *symbols,
+        UNUM_GROUPING_ON_ALIGNED,
+        status
+    );
+    auto result = snf.formatInt64(987654321, status);
+
+    assertEquals("bn symbols with en-US pattern",
+        u"৯৮৭,৬৫৪,৩২১",
+        result.toTempString(status));
+}
+
+void SimpleNumberFormatterTest::testSign() {
+    IcuTestErrorCode status(*this, "testSign");
+
+    SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("und", status);
+
+    struct TestCase {
+        int64_t input;
+        USimpleNumberSign sign;
+        const char16_t* expected;
+    } cases[] = {
+        { 1, UNUM_SIMPLE_NUMBER_NO_SIGN, u"1" },
+        { 1, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+1" },
+        { 1, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-1" },
+        { 0, UNUM_SIMPLE_NUMBER_NO_SIGN, u"0" },
+        { 0, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+0" },
+        { 0, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-0" },
+        { -1, UNUM_SIMPLE_NUMBER_NO_SIGN, u"1" },
+        { -1, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+1" },
+        { -1, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-1" },
+    };
+    for (auto& cas : cases) {
+        SimpleNumber num = SimpleNumber::forInt64(cas.input, status);
+        num.setSign(cas.sign, status);
+        auto result = snf.format(std::move(num), status);
+        assertEquals("", cas.expected, result.toTempString(status));
+    }
+}
+
+void SimpleNumberFormatterTest::testCopyMove() {
+    IcuTestErrorCode status(*this, "testCopyMove");
+
+    SimpleNumberFormatter snf0 = SimpleNumberFormatter::forLocale("und", status);
+
+    SimpleNumber sn0 = SimpleNumber::forInt64(55, status);
+    SimpleNumber sn1 = std::move(sn0);
+
+    snf0.format(std::move(sn0), status);
+    status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR, "Use of moved number");
+
+    assertEquals("Move number constructor",
+        u"55",
+        snf0.format(std::move(sn1), status).toTempString(status));
+
+    SimpleNumber sn2;
+    snf0.format(std::move(sn2), status);
+    status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR, "Default constructed number");
+
+    sn0 = SimpleNumber::forInt64(44, status);
+
+    assertEquals("Move number assignment",
+        u"44",
+        snf0.format(std::move(sn0), status).toTempString(status));
+
+    SimpleNumberFormatter snf1 = std::move(snf0);
+
+    snf0.format(SimpleNumber::forInt64(22, status), status);
+    status.expectErrorAndReset(U_INVALID_STATE_ERROR, "Use of moved formatter");
+
+    assertEquals("Move formatter constructor",
+        u"33",
+        snf1.format(SimpleNumber::forInt64(33, status), status).toTempString(status));
+
+    SimpleNumberFormatter snf2;
+    snf2.format(SimpleNumber::forInt64(22, status), status);
+    status.expectErrorAndReset(U_INVALID_STATE_ERROR, "Default constructed formatter");
+
+    snf0 = std::move(snf1);
+
+    assertEquals("Move formatter assignment",
+        u"22",
+        snf0.format(SimpleNumber::forInt64(22, status), status).toTempString(status));
+
+    snf0 = SimpleNumberFormatter::forLocale("de", status);
+    sn0 = SimpleNumber::forInt64(22, status);
+    sn0 = SimpleNumber::forInt64(11, status);
+
+    assertEquals("Move assignment with nonempty fields",
+        u"11",
+        snf0.format(std::move(sn0), status).toTempString(status));
+}
+
+void SimpleNumberFormatterTest::testCAPI() {
+    IcuTestErrorCode status(*this, "testCAPI");
+
+    LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale("de-CH", status));
+    LocalUFormattedNumberPointer uresult(unumf_openResult(status));
+    usnumf_formatInt64(uformatter.getAlias(), 55, uresult.getAlias(), status);
+    assertEquals("",
+        u"55",
+        ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
+
+    LocalUSimpleNumberPointer unumber(usnum_openForInt64(44, status));
+    usnumf_formatAndAdoptNumber(uformatter.getAlias(), unumber.orphan(), uresult.getAlias(), status);
+    assertEquals("",
+        u"44",
+        ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
+
+    unumber.adoptInstead(usnum_openForInt64(2335, status));
+    usnum_multiplyByPowerOfTen(unumber.getAlias(), -2, status);
+    usnum_roundTo(unumber.getAlias(), -1, UNUM_ROUND_HALFEVEN, status);
+    usnum_truncateStart(unumber.getAlias(), 1, status);
+    usnum_setMinimumFractionDigits(unumber.getAlias(), 3, status);
+    usnum_setMinimumIntegerDigits(unumber.getAlias(), 3, status);
+    usnumf_formatAndAdoptNumber(uformatter.getAlias(), unumber.orphan(), uresult.getAlias(), status);
+    assertEquals("",
+        u"003.400",
+        ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
+}
+
+
+#endif