1// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3
4#include "unicode/utypes.h"
5
6#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
7
8#include <cstdlib>
9#include "number_scientific.h"
10#include "number_utils.h"
11#include "number_stringbuilder.h"
12#include "unicode/unum.h"
13
14using namespace icu;
15using namespace icu::number;
16using namespace icu::number::impl;
17
18// NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
19//
20// During formatting, we need to provide an object with state (the exponent) as the inner modifier.
21//
22// In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
23// ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
24// instances.  This scheme reduces the number of object creations by 1 in both safe and unsafe.
25//
26// In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
27// the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
28
29ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
30
31void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) {
32    // ScientificModifier should be set only once.
33    U_ASSERT(fHandler == nullptr);
34    fExponent = exponent;
35    fHandler = handler;
36}
37
38int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
39                                  UErrorCode &status) const {
40    // FIXME: Localized exponent separator location.
41    int i = rightIndex;
42    // Append the exponent separator and sign
43    i += output.insert(
44            i,
45            fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
46            UNUM_EXPONENT_SYMBOL_FIELD,
47            status);
48    if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) {
49        i += output.insert(
50                i,
51                fHandler->fSymbols
52                        ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),
53                UNUM_EXPONENT_SIGN_FIELD,
54                status);
55    } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) {
56        i += output.insert(
57                i,
58                fHandler->fSymbols
59                        ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),
60                UNUM_EXPONENT_SIGN_FIELD,
61                status);
62    }
63    // Append the exponent digits (using a simple inline algorithm)
64    int32_t disp = std::abs(fExponent);
65    for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
66        auto d = static_cast<int8_t>(disp % 10);
67        const UnicodeString &digitString = getDigitFromSymbols(d, *fHandler->fSymbols);
68        i += output.insert(i - j, digitString, UNUM_EXPONENT_FIELD, status);
69    }
70    return i - rightIndex;
71}
72
73int32_t ScientificModifier::getPrefixLength(UErrorCode &status) const {
74    (void)status;
75    // TODO: Localized exponent separator location.
76    return 0;
77}
78
79int32_t ScientificModifier::getCodePointCount(UErrorCode &status) const {
80    (void)status;
81    // This method is not used for strong modifiers.
82    U_ASSERT(false);
83    return 0;
84}
85
86bool ScientificModifier::isStrong() const {
87    // Scientific is always strong
88    return true;
89}
90
91// Note: Visual Studio does not compile this function without full name space. Why?
92icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
93	const MicroPropsGenerator *parent) :
94	fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
95
96void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
97                                        UErrorCode &status) const {
98    fParent->processQuantity(quantity, micros, status);
99    if (U_FAILURE(status)) { return; }
100
101    // Treat zero as if it had magnitude 0
102    int32_t exponent;
103    if (quantity.isZero()) {
104        if (fSettings.fRequireMinInt && micros.rounding.fType == Rounder::RND_SIGNIFICANT) {
105            // Show "00.000E0" on pattern "00.000E0"
106            micros.rounding.apply(quantity, fSettings.fEngineeringInterval, status);
107            exponent = 0;
108        } else {
109            micros.rounding.apply(quantity, status);
110            exponent = 0;
111        }
112    } else {
113        exponent = -micros.rounding.chooseMultiplierAndApply(quantity, *this, status);
114    }
115
116    // Use MicroProps's helper ScientificModifier and save it as the modInner.
117    ScientificModifier &mod = micros.helpers.scientificModifier;
118    mod.set(exponent, this);
119    micros.modInner = &mod;
120}
121
122int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
123    int32_t interval = fSettings.fEngineeringInterval;
124    int32_t digitsShown;
125    if (fSettings.fRequireMinInt) {
126        // For patterns like "000.00E0" and ".00E0"
127        digitsShown = interval;
128    } else if (interval <= 1) {
129        // For patterns like "0.00E0" and "@@@E0"
130        digitsShown = 1;
131    } else {
132        // For patterns like "##0.00"
133        digitsShown = ((magnitude % interval + interval) % interval) + 1;
134    }
135    return digitsShown - magnitude - 1;
136}
137
138#endif /* #if !UCONFIG_NO_FORMATTING */
139