tor-browser

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

nfsubs.cpp (55739B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 ******************************************************************************
      5 *   Copyright (C) 1997-2015, International Business Machines
      6 *   Corporation and others.  All Rights Reserved.
      7 ******************************************************************************
      8 *   file name:  nfsubs.cpp
      9 *   encoding:   UTF-8
     10 *   tab size:   8 (not used)
     11 *   indentation:4
     12 *
     13 * Modification history
     14 * Date        Name      Comments
     15 * 10/11/2001  Doug      Ported from ICU4J
     16 */
     17 
     18 #include <stdio.h>
     19 #include "utypeinfo.h"  // for 'typeid' to work
     20 
     21 #include "nfsubs.h"
     22 #include "fmtableimp.h"
     23 #include "putilimp.h"
     24 #include "number_decimalquantity.h"
     25 
     26 #if U_HAVE_RBNF
     27 
     28 static const char16_t gLessThan = 0x003c;
     29 static const char16_t gEquals = 0x003d;
     30 static const char16_t gGreaterThan = 0x003e;
     31 static const char16_t gPercent = 0x0025;
     32 static const char16_t gPound = 0x0023;
     33 static const char16_t gZero = 0x0030;
     34 static const char16_t gSpace = 0x0020;
     35 
     36 static const char16_t gEqualsEquals[] =
     37 {
     38    0x3D, 0x3D, 0
     39 }; /* "==" */
     40 static const char16_t gGreaterGreaterGreaterThan[] =
     41 {
     42    0x3E, 0x3E, 0x3E, 0
     43 }; /* ">>>" */
     44 static const char16_t gGreaterGreaterThan[] =
     45 {
     46    0x3E, 0x3E, 0
     47 }; /* ">>" */
     48 
     49 U_NAMESPACE_BEGIN
     50 
     51 using number::impl::DecimalQuantity;
     52 
     53 class SameValueSubstitution : public NFSubstitution {
     54 public:
     55    SameValueSubstitution(int32_t pos,
     56        const NFRuleSet* ruleset,
     57        const UnicodeString& description,
     58        UErrorCode& status);
     59    virtual ~SameValueSubstitution();
     60 
     61    virtual int64_t transformNumber(int64_t number) const override { return number; }
     62    virtual double transformNumber(double number) const override { return number; }
     63    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return newRuleValue; }
     64    virtual double calcUpperBound(double oldUpperBound) const override { return oldUpperBound; }
     65    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003d); } // '='
     66 
     67 public:
     68    static UClassID getStaticClassID();
     69    virtual UClassID getDynamicClassID() const override;
     70 };
     71 
     72 SameValueSubstitution::~SameValueSubstitution() {}
     73 
     74 class MultiplierSubstitution : public NFSubstitution {
     75    int64_t divisor;
     76    const NFRule* owningRule;
     77 
     78 public:
     79    MultiplierSubstitution(int32_t _pos,
     80        const NFRule *rule,
     81        const NFRuleSet* _ruleSet,
     82        const UnicodeString& description,
     83        UErrorCode& status)
     84        : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor()), owningRule(rule)
     85    {
     86        if (divisor == 0) {
     87            status = U_PARSE_ERROR;
     88        }
     89    }
     90    virtual ~MultiplierSubstitution();
     91 
     92    virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override {
     93        divisor = util64_pow(radix, exponent);
     94 
     95        if(divisor == 0) {
     96            status = U_PARSE_ERROR;
     97        }
     98    }
     99 
    100    virtual bool operator==(const NFSubstitution& rhs) const override;
    101 
    102    virtual int64_t transformNumber(int64_t number) const override {
    103        return number / divisor;
    104    }
    105 
    106    virtual double transformNumber(double number) const override {
    107        // Most of the time, when a number is handled by an NFSubstitution, we do a floor() on it, but
    108        // if a substitution uses a DecimalFormat to format the number instead of a ruleset, we generally
    109        // don't want to do a floor()-- we want to keep the value intact so that the DecimalFormat can
    110        // either include the fractional part or round properly.  The big exception to this is here in
    111        // MultiplierSubstitution.  If the rule includes two substitutions, the MultiplierSubstitution
    112        // (which is handling the larger part of the number) really _does_ want to do a floor(), because
    113        // the ModulusSubstitution (which is handling the smaller part of the number) will take
    114        // care of the fractional part.  (Consider something like `1/12: <0< feet >0.0> inches;`.)
    115        // But if there is no ModulusSubstitution, we're shortening the number in some way-- the "larger part"
    116        // of the number is the only part we're keeping.  Even if the DecimalFormat doesn't include the
    117        // fractional part in its output, we still want it to round.  (Consider something like `1/1000: <0<K;`.)
    118        // (TODO: The kRoundFloor thing is a kludge to preserve the previous floor-always behavior.  What we
    119        // probably really want to do is just set the rounding mode on the DecimalFormat to match the rounding
    120        // mode on the RuleBasedNumberFormat and then pass the number to it whole and let it do its own rounding.
    121        // But before making that change, we'd have to make sure it didn't have undesirable side effects.)
    122        if (getRuleSet() != nullptr || owningRule->hasModulusSubstitution() || owningRule->formatter->getRoundingMode() == NumberFormat::kRoundFloor) {
    123            return uprv_floor(number / divisor);
    124        } else {
    125            return number / divisor;
    126        }
    127    }
    128 
    129    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override {
    130        return newRuleValue * divisor;
    131    }
    132 
    133    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast<double>(divisor); }
    134 
    135    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
    136 
    137 public:
    138    static UClassID getStaticClassID();
    139    virtual UClassID getDynamicClassID() const override;
    140 };
    141 
    142 MultiplierSubstitution::~MultiplierSubstitution() {}
    143 
    144 class ModulusSubstitution : public NFSubstitution {
    145    int64_t  divisor;
    146    const NFRule* ruleToUse;
    147 public:
    148    ModulusSubstitution(int32_t pos,
    149        const NFRule* rule,
    150        const NFRule* rulePredecessor,
    151        const NFRuleSet* ruleSet,
    152        const UnicodeString& description,
    153        UErrorCode& status);
    154    virtual ~ModulusSubstitution();
    155 
    156    virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override {
    157        divisor = util64_pow(radix, exponent);
    158 
    159        if (divisor == 0) {
    160            status = U_PARSE_ERROR;
    161        }
    162    }
    163 
    164    virtual bool operator==(const NFSubstitution& rhs) const override;
    165 
    166    virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
    167    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
    168 
    169    virtual int64_t transformNumber(int64_t number) const override { return number % divisor; }
    170    virtual double transformNumber(double number) const override { return uprv_fmod(number, static_cast<double>(divisor)); }
    171 
    172    virtual UBool doParse(const UnicodeString& text, 
    173        ParsePosition& parsePosition,
    174        double baseValue,
    175        double upperBound,
    176        UBool lenientParse,
    177        uint32_t nonNumericalExecutedRuleMask,
    178        int32_t recursionCount,
    179        Formattable& result) const override;
    180 
    181    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override {
    182        return oldRuleValue - uprv_fmod(oldRuleValue, static_cast<double>(divisor)) + newRuleValue;
    183    }
    184 
    185    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast<double>(divisor); }
    186 
    187    virtual UBool isModulusSubstitution() const override { return true; }
    188 
    189    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
    190 
    191    virtual void toString(UnicodeString& result) const override;
    192 
    193 public:
    194    static UClassID getStaticClassID();
    195    virtual UClassID getDynamicClassID() const override;
    196 };
    197 
    198 ModulusSubstitution::~ModulusSubstitution() {}
    199 
    200 class IntegralPartSubstitution : public NFSubstitution {
    201 public:
    202    IntegralPartSubstitution(int32_t _pos,
    203        const NFRuleSet* _ruleSet,
    204        const UnicodeString& description,
    205        UErrorCode& status)
    206        : NFSubstitution(_pos, _ruleSet, description, status) {}
    207    virtual ~IntegralPartSubstitution();
    208 
    209    virtual int64_t transformNumber(int64_t number) const override { return number; }
    210    virtual double transformNumber(double number) const override { return uprv_floor(number); }
    211    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; }
    212    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; }
    213    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
    214 
    215 public:
    216    static UClassID getStaticClassID();
    217    virtual UClassID getDynamicClassID() const override;
    218 };
    219 
    220 IntegralPartSubstitution::~IntegralPartSubstitution() {}
    221 
    222 class FractionalPartSubstitution : public NFSubstitution {
    223    UBool byDigits;
    224    UBool useSpaces;
    225    enum { kMaxDecimalDigits = 8 };
    226 public:
    227    FractionalPartSubstitution(int32_t pos,
    228        const NFRuleSet* ruleSet,
    229        const UnicodeString& description,
    230        UErrorCode& status);
    231    virtual ~FractionalPartSubstitution();
    232 
    233    virtual bool operator==(const NFSubstitution& rhs) const override;
    234 
    235    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
    236    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {}
    237    virtual int64_t transformNumber(int64_t /*number*/) const override { return 0; }
    238    virtual double transformNumber(double number) const override { return number - uprv_floor(number); }
    239 
    240    virtual UBool doParse(const UnicodeString& text,
    241        ParsePosition& parsePosition,
    242        double baseValue,
    243        double upperBound,
    244        UBool lenientParse,
    245        uint32_t nonNumericalExecutedRuleMask,
    246        int32_t recursionCount,
    247        Formattable& result) const override;
    248 
    249    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; }
    250    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return 0.0; }
    251    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
    252 
    253 public:
    254    static UClassID getStaticClassID();
    255    virtual UClassID getDynamicClassID() const override;
    256 };
    257 
    258 FractionalPartSubstitution::~FractionalPartSubstitution() {}
    259 
    260 class AbsoluteValueSubstitution : public NFSubstitution {
    261 public:
    262    AbsoluteValueSubstitution(int32_t _pos,
    263        const NFRuleSet* _ruleSet,
    264        const UnicodeString& description,
    265        UErrorCode& status)
    266        : NFSubstitution(_pos, _ruleSet, description, status) {}
    267    virtual ~AbsoluteValueSubstitution();
    268 
    269    virtual int64_t transformNumber(int64_t number) const override { return number >= 0 ? number : -number; }
    270    virtual double transformNumber(double number) const override { return uprv_fabs(number); }
    271    virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return -newRuleValue; }
    272    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; }
    273    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003e); } // '>'
    274 
    275 public:
    276    static UClassID getStaticClassID();
    277    virtual UClassID getDynamicClassID() const override;
    278 };
    279 
    280 AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {}
    281 
    282 class NumeratorSubstitution : public NFSubstitution {
    283    double denominator;
    284    int64_t ldenominator;
    285    UBool withZeros;
    286 public:
    287    static inline UnicodeString fixdesc(const UnicodeString& desc) {
    288        if (desc.endsWith(LTLT, 2)) {
    289            UnicodeString result(desc, 0, desc.length()-1);
    290            return result;
    291        }
    292        return desc;
    293    }
    294    NumeratorSubstitution(int32_t _pos,
    295        double _denominator,
    296        NFRuleSet* _ruleSet,
    297        const UnicodeString& description,
    298        UErrorCode& status)
    299        : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator) 
    300    {
    301        ldenominator = util64_fromDouble(denominator);
    302        withZeros = description.endsWith(LTLT, 2);
    303    }
    304    virtual ~NumeratorSubstitution();
    305 
    306    virtual bool operator==(const NFSubstitution& rhs) const override;
    307 
    308    virtual int64_t transformNumber(int64_t number) const override { return number * ldenominator; }
    309    virtual double transformNumber(double number) const override { return uprv_round(number * denominator); }
    310 
    311    virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {}
    312    virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override;
    313    virtual UBool doParse(const UnicodeString& text, 
    314        ParsePosition& parsePosition,
    315        double baseValue,
    316        double upperBound,
    317        UBool /*lenientParse*/,
    318        uint32_t nonNumericalExecutedRuleMask,
    319        int32_t recursionCount,
    320        Formattable& result) const override;
    321 
    322    virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue / oldRuleValue; }
    323    virtual double calcUpperBound(double /*oldUpperBound*/) const override { return denominator; }
    324    virtual char16_t tokenChar() const override { return static_cast<char16_t>(0x003c); } // '<'
    325 private:
    326    static const char16_t LTLT[2];
    327 
    328 public:
    329    static UClassID getStaticClassID();
    330    virtual UClassID getDynamicClassID() const override;
    331 };
    332 
    333 NumeratorSubstitution::~NumeratorSubstitution() {}
    334 
    335 NFSubstitution*
    336 NFSubstitution::makeSubstitution(int32_t pos,
    337                                 const NFRule* rule,
    338                                 const NFRule* predecessor,
    339                                 const NFRuleSet* ruleSet,
    340                                 const RuleBasedNumberFormat* formatter,
    341                                 const UnicodeString& description,
    342                                 UErrorCode& status)
    343 {
    344    if (U_FAILURE(status)) return nullptr;
    345    // if the description is empty, return a NullSubstitution
    346    if (description.length() == 0) {
    347        return nullptr;
    348    }
    349 
    350    switch (description.charAt(0)) {
    351        // if the description begins with '<'...
    352    case gLessThan:
    353        // throw an exception if the rule is a negative number
    354        // rule
    355        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
    356            // throw new IllegalArgumentException("<< not allowed in negative-number rule");
    357            status = U_PARSE_ERROR;
    358            return nullptr;
    359        }
    360 
    361        // if the rule is a fraction rule, return an
    362        // IntegralPartSubstitution
    363        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
    364            || rule->getBaseValue() == NFRule::kProperFractionRule
    365            || rule->getBaseValue() == NFRule::kDefaultRule) {
    366            return new IntegralPartSubstitution(pos, ruleSet, description, status);
    367        }
    368 
    369        // if the rule set containing the rule is a fraction
    370        // rule set, return a NumeratorSubstitution
    371        else if (ruleSet->isFractionRuleSet()) {
    372            return new NumeratorSubstitution(pos, static_cast<double>(rule->getBaseValue()),
    373                formatter->getDefaultRuleSet(), description, status);
    374        }
    375 
    376        // otherwise, return a MultiplierSubstitution
    377        else {
    378            return new MultiplierSubstitution(pos, rule, ruleSet,
    379                description, status);
    380        }
    381 
    382        // if the description begins with '>'...
    383    case gGreaterThan:
    384        // if the rule is a negative-number rule, return
    385        // an AbsoluteValueSubstitution
    386        if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
    387            return new AbsoluteValueSubstitution(pos, ruleSet, description, status);
    388        }
    389 
    390        // if the rule is a fraction rule, return a
    391        // FractionalPartSubstitution
    392        else if (rule->getBaseValue() == NFRule::kImproperFractionRule
    393            || rule->getBaseValue() == NFRule::kProperFractionRule
    394            || rule->getBaseValue() == NFRule::kDefaultRule) {
    395            return new FractionalPartSubstitution(pos, ruleSet, description, status);
    396        }
    397 
    398        // if the rule set owning the rule is a fraction rule set,
    399        // throw an exception
    400        else if (ruleSet->isFractionRuleSet()) {
    401            // throw new IllegalArgumentException(">> not allowed in fraction rule set");
    402            status = U_PARSE_ERROR;
    403            return nullptr;
    404        }
    405 
    406        // otherwise, return a ModulusSubstitution
    407        else {
    408            return new ModulusSubstitution(pos, rule, predecessor,
    409                ruleSet, description, status);
    410        }
    411 
    412        // if the description begins with '=', always return a
    413        // SameValueSubstitution
    414    case gEquals:
    415        return new SameValueSubstitution(pos, ruleSet, description, status);
    416 
    417        // and if it's anything else, throw an exception
    418    default:
    419        // throw new IllegalArgumentException("Illegal substitution character");
    420        status = U_PARSE_ERROR;
    421    }
    422    return nullptr;
    423 }
    424 
    425 NFSubstitution::NFSubstitution(int32_t _pos,
    426                               const NFRuleSet* _ruleSet,
    427                               const UnicodeString& description,
    428                               UErrorCode& status)
    429                               : pos(_pos), ruleSet(nullptr), numberFormat(nullptr)
    430 {
    431    if (U_FAILURE(status)) return;
    432    // the description should begin and end with the same character.
    433    // If it doesn't that's a syntax error.  Otherwise,
    434    // makeSubstitution() was the only thing that needed to know
    435    // about these characters, so strip them off
    436    UnicodeString workingDescription(description);
    437    if (description.length() >= 2
    438        && description.charAt(0) == description.charAt(description.length() - 1))
    439    {
    440        workingDescription.remove(description.length() - 1, 1);
    441        workingDescription.remove(0, 1);
    442    }
    443    else if (description.length() != 0) {
    444        // throw new IllegalArgumentException("Illegal substitution syntax");
    445        status = U_PARSE_ERROR;
    446        return;
    447    }
    448 
    449    if (workingDescription.length() == 0) {
    450        // if the description was just two paired token characters
    451        // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
    452        // format its result
    453        this->ruleSet = _ruleSet;
    454    }
    455    else if (workingDescription.charAt(0) == gPercent) {
    456        // if the description contains a rule set name, that's the rule
    457        // set we use to format the result: get a reference to the
    458        // names rule set
    459        this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status);
    460    }
    461    else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
    462        // if the description begins with 0 or #, treat it as a
    463        // DecimalFormat pattern, and initialize a DecimalFormat with
    464        // that pattern (then set it to use the DecimalFormatSymbols
    465        // belonging to our formatter)
    466        const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols();
    467        if (!sym) {
    468            status = U_MISSING_RESOURCE_ERROR;
    469            return;
    470        }
    471        DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status);
    472        /* test for nullptr */
    473        if (!tempNumberFormat) {
    474            status = U_MEMORY_ALLOCATION_ERROR;
    475            return;
    476        }
    477        if (U_FAILURE(status)) {
    478            delete tempNumberFormat;
    479            return;
    480        }
    481        this->numberFormat = tempNumberFormat;
    482    }
    483    else if (workingDescription.charAt(0) == gGreaterThan) {
    484        // if the description is ">>>", this substitution bypasses the
    485        // usual rule-search process and always uses the rule that precedes
    486        // it in its own rule set's rule list (this is used for place-value
    487        // notations: formats where you want to see a particular part of
    488        // a number even when it's 0)
    489 
    490        // this causes problems when >>> is used in a frationalPartSubstitution
    491        // this->ruleSet = nullptr;
    492        this->ruleSet = _ruleSet;
    493        this->numberFormat = nullptr;
    494    }
    495    else {
    496        // and of the description is none of these things, it's a syntax error
    497 
    498        // throw new IllegalArgumentException("Illegal substitution syntax");
    499        status = U_PARSE_ERROR;
    500    }
    501 }
    502 
    503 NFSubstitution::~NFSubstitution()
    504 {
    505    delete numberFormat;
    506    numberFormat = nullptr;
    507 }
    508 
    509 /**
    510 * Set's the substitution's divisor.  Used by NFRule.setBaseValue().
    511 * A no-op for all substitutions except multiplier and modulus
    512 * substitutions.
    513 * @param radix The radix of the divisor
    514 * @param exponent The exponent of the divisor
    515 */
    516 void
    517 NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) {
    518  // a no-op for all substitutions except multiplier and modulus substitutions
    519 }
    520 
    521 void
    522 NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) {
    523    if (numberFormat != nullptr) {
    524        numberFormat->setDecimalFormatSymbols(newSymbols);
    525    }
    526 }
    527 
    528 //-----------------------------------------------------------------------
    529 // boilerplate
    530 //-----------------------------------------------------------------------
    531 
    532 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution)
    533 
    534 /**
    535 * Compares two substitutions for equality
    536 * @param The substitution to compare this one to
    537 * @return true if the two substitutions are functionally equivalent
    538 */
    539 bool
    540 NFSubstitution::operator==(const NFSubstitution& rhs) const
    541 {
    542  // compare class and all of the fields all substitutions have
    543  // in common
    544  // this should be called by subclasses before their own equality tests
    545  return typeid(*this) == typeid(rhs)
    546  && pos == rhs.pos
    547  && (ruleSet == nullptr) == (rhs.ruleSet == nullptr)
    548  // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead?
    549  && (numberFormat == nullptr
    550      ? (rhs.numberFormat == nullptr)
    551      : (*numberFormat == *rhs.numberFormat));
    552 }
    553 
    554 /**
    555 * Returns a textual description of the substitution
    556 * @return A textual description of the substitution.  This might
    557 * not be identical to the description it was created from, but
    558 * it'll produce the same result.
    559 */
    560 void
    561 NFSubstitution::toString(UnicodeString& text) const
    562 {
    563  // use tokenChar() to get the character at the beginning and
    564  // end of the substitutin token.  In between them will go
    565  // either the name of the rule set it uses, or the pattern of
    566  // the DecimalFormat it uses
    567  text.remove();
    568  text.append(tokenChar());
    569 
    570  UnicodeString temp;
    571  if (ruleSet != nullptr) {
    572    ruleSet->getName(temp);
    573  } else if (numberFormat != nullptr) {
    574    numberFormat->toPattern(temp);
    575  }
    576  text.append(temp);
    577  text.append(tokenChar());
    578 }
    579 
    580 //-----------------------------------------------------------------------
    581 // formatting
    582 //-----------------------------------------------------------------------
    583 
    584 /**
    585 * Performs a mathematical operation on the number, formats it using
    586 * either ruleSet or decimalFormat, and inserts the result into
    587 * toInsertInto.
    588 * @param number The number being formatted.
    589 * @param toInsertInto The string we insert the result into
    590 * @param pos The position in toInsertInto where the owning rule's
    591 * rule text begins (this value is added to this substitution's
    592 * position to determine exactly where to insert the new text)
    593 */
    594 void
    595 NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
    596 {
    597    if (U_FAILURE(status)) return;
    598    if (ruleSet != nullptr) {
    599        // Perform a transformation on the number that is dependent
    600        // on the type of substitution this is, then just call its
    601        // rule set's format() method to format the result
    602        ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status);
    603    } else if (numberFormat != nullptr) {
    604        if (number <= MAX_INT64_IN_DOUBLE) {
    605            // or perform the transformation on the number,
    606            // then use that formatter's format() method
    607            // to format the result
    608            UnicodeString temp;
    609            numberFormat->format(transformNumber(static_cast<double>(number)), temp, status);
    610            toInsertInto.insert(_pos + this->pos, temp);
    611        } 
    612        else { 
    613            // We have gone beyond double precision. Something has to give. 
    614            // We're favoring accuracy of the large number over potential rules 
    615            // that round like a CompactDecimalFormat, which is not a common use case. 
    616            // 
    617            // Perform a transformation on the number that is dependent 
    618            // on the type of substitution this is, then just call its 
    619            // rule set's format() method to format the result 
    620            int64_t numberToFormat = transformNumber(number); 
    621            UnicodeString temp;
    622            numberFormat->format(numberToFormat, temp, status);
    623            toInsertInto.insert(_pos + this->pos, temp);
    624        } 
    625    }
    626 }
    627 
    628 /**
    629 * Performs a mathematical operation on the number, formats it using
    630 * either ruleSet or decimalFormat, and inserts the result into
    631 * toInsertInto.
    632 * @param number The number being formatted.
    633 * @param toInsertInto The string we insert the result into
    634 * @param pos The position in toInsertInto where the owning rule's
    635 * rule text begins (this value is added to this substitution's
    636 * position to determine exactly where to insert the new text)
    637 */
    638 void
    639 NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const {
    640    if (U_FAILURE(status)) return;
    641    // perform a transformation on the number being formatted that
    642    // is dependent on the type of substitution this is
    643    double numberToFormat = transformNumber(number);
    644 
    645    if (uprv_isInfinite(numberToFormat)) {
    646        // This is probably a minus rule. Combine it with an infinite rule.
    647        const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity());
    648        infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
    649        return;
    650    }
    651 
    652    // if the result is an integer, from here on out we work in integer
    653    // space (saving time and memory and preserving accuracy)
    654    if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != nullptr) {
    655        ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status);
    656 
    657        // if the result isn't an integer, then call either our rule set's
    658        // format() method or our DecimalFormat's format() method to
    659        // format the result
    660    } else {
    661        if (ruleSet != nullptr) {
    662            ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
    663        } else if (numberFormat != nullptr) {
    664            UnicodeString temp;
    665            numberFormat->format(numberToFormat, temp);
    666            toInsertInto.insert(_pos + this->pos, temp);
    667        }
    668    }
    669 }
    670 
    671 
    672    //-----------------------------------------------------------------------
    673    // parsing
    674    //-----------------------------------------------------------------------
    675 
    676 #ifdef RBNF_DEBUG
    677 #include <stdio.h>
    678 #endif
    679 
    680 /**
    681 * Parses a string using the rule set or DecimalFormat belonging
    682 * to this substitution.  If there's a match, a mathematical
    683 * operation (the inverse of the one used in formatting) is
    684 * performed on the result of the parse and the value passed in
    685 * and returned as the result.  The parse position is updated to
    686 * point to the first unmatched character in the string.
    687 * @param text The string to parse
    688 * @param parsePosition On entry, ignored, but assumed to be 0.
    689 * On exit, this is updated to point to the first unmatched
    690 * character (or 0 if the substitution didn't match)
    691 * @param baseValue A partial parse result that should be
    692 * combined with the result of this parse
    693 * @param upperBound When searching the rule set for a rule
    694 * matching the string passed in, only rules with base values
    695 * lower than this are considered
    696 * @param lenientParse If true and matching against rules fails,
    697 * the substitution will also try matching the text against
    698 * numerals using a default-costructed NumberFormat.  If false,
    699 * no extra work is done.  (This value is false whenever the
    700 * formatter isn't in lenient-parse mode, but is also false
    701 * under some conditions even when the formatter _is_ in
    702 * lenient-parse mode.)
    703 * @return If there's a match, this is the result of composing
    704 * baseValue with whatever was returned from matching the
    705 * characters.  This will be either a Long or a Double.  If there's
    706 * no match this is new Long(0) (not null), and parsePosition
    707 * is left unchanged.
    708 */
    709 UBool
    710 NFSubstitution::doParse(const UnicodeString& text,
    711                        ParsePosition& parsePosition,
    712                        double baseValue,
    713                        double upperBound,
    714                        UBool lenientParse,
    715                        uint32_t nonNumericalExecutedRuleMask,
    716                        int32_t recursionCount,
    717                        Formattable& result) const
    718 {
    719 #ifdef RBNF_DEBUG
    720    fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound);
    721 #endif
    722    // figure out the highest base value a rule can have and match
    723    // the text being parsed (this varies according to the type of
    724    // substitutions: multiplier, modulus, and numerator substitutions
    725    // restrict the search to rules with base values lower than their
    726    // own; same-value substitutions leave the upper bound wherever
    727    // it was, and the others allow any rule to match
    728    upperBound = calcUpperBound(upperBound);
    729 
    730    // use our rule set to parse the text.  If that fails and
    731    // lenient parsing is enabled (this is always false if the
    732    // formatter's lenient-parsing mode is off, but it may also
    733    // be false even when the formatter's lenient-parse mode is
    734    // on), then also try parsing the text using a default-
    735    // constructed NumberFormat
    736    if (ruleSet != nullptr) {
    737        ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
    738        if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
    739            UErrorCode status = U_ZERO_ERROR;
    740            NumberFormat* fmt = NumberFormat::createInstance(status);
    741            if (U_SUCCESS(status)) {
    742                fmt->parse(text, result, parsePosition);
    743            }
    744            delete fmt;
    745        }
    746 
    747        // ...or use our DecimalFormat to parse the text
    748    } else if (numberFormat != nullptr) {
    749        numberFormat->parse(text, result, parsePosition);
    750    }
    751 
    752    // if the parse was successful, we've already advanced the caller's
    753    // parse position (this is the one function that doesn't have one
    754    // of its own).  Derive a parse result and return it as a Long,
    755    // if possible, or a Double
    756    if (parsePosition.getIndex() != 0) {
    757        UErrorCode status = U_ZERO_ERROR;
    758        double tempResult = result.getDouble(status);
    759 
    760        // composeRuleValue() produces a full parse result from
    761        // the partial parse result passed to this function from
    762        // the caller (this is either the owning rule's base value
    763        // or the partial result obtained from composing the
    764        // owning rule's base value with its other substitution's
    765        // parse result) and the partial parse result obtained by
    766        // matching the substitution (which will be the same value
    767        // the caller would get by parsing just this part of the
    768        // text with RuleBasedNumberFormat.parse() ).  How the two
    769        // values are used to derive the full parse result depends
    770        // on the types of substitutions: For a regular rule, the
    771        // ultimate result is its multiplier substitution's result
    772        // times the rule's divisor (or the rule's base value) plus
    773        // the modulus substitution's result (which will actually
    774        // supersede part of the rule's base value).  For a negative-
    775        // number rule, the result is the negative of its substitution's
    776        // result.  For a fraction rule, it's the sum of its two
    777        // substitution results.  For a rule in a fraction rule set,
    778        // it's the numerator substitution's result divided by
    779        // the rule's base value.  Results from same-value substitutions
    780        // propagate back upard, and null substitutions don't affect
    781        // the result.
    782        tempResult = composeRuleValue(tempResult, baseValue);
    783        result.setDouble(tempResult);
    784        return true;
    785        // if the parse was UNsuccessful, return 0
    786    } else {
    787        result.setLong(0);
    788        return false;
    789    }
    790 }
    791 
    792    /**
    793     * Returns true if this is a modulus substitution.  (We didn't do this
    794     * with instanceof partially because it causes source files to
    795     * proliferate and partially because we have to port this to C++.)
    796     * @return true if this object is an instance of ModulusSubstitution
    797     */
    798 UBool
    799 NFSubstitution::isModulusSubstitution() const {
    800    return false;
    801 }
    802 
    803 //===================================================================
    804 // SameValueSubstitution
    805 //===================================================================
    806 
    807 /**
    808 * A substitution that passes the value passed to it through unchanged.
    809 * Represented by == in rule descriptions.
    810 */
    811 SameValueSubstitution::SameValueSubstitution(int32_t _pos,
    812                        const NFRuleSet* _ruleSet,
    813                        const UnicodeString& description,
    814                        UErrorCode& status)
    815 : NFSubstitution(_pos, _ruleSet, description, status)
    816 {
    817    if (0 == description.compare(gEqualsEquals, 2)) {
    818        // throw new IllegalArgumentException("== is not a legal token");
    819        status = U_PARSE_ERROR;
    820    }
    821 }
    822 
    823 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution)
    824 
    825 //===================================================================
    826 // MultiplierSubstitution
    827 //===================================================================
    828 
    829 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution)
    830 
    831 bool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const
    832 {
    833    return NFSubstitution::operator==(rhs) &&
    834        divisor == ((const MultiplierSubstitution*)&rhs)->divisor;
    835 }
    836 
    837 
    838 //===================================================================
    839 // ModulusSubstitution
    840 //===================================================================
    841 
    842 /**
    843 * A substitution that divides the number being formatted by the its rule's
    844 * divisor and formats the remainder.  Represented by "&gt;&gt;" in a
    845 * regular rule.
    846 */
    847 ModulusSubstitution::ModulusSubstitution(int32_t _pos,
    848                                         const NFRule* rule,
    849                                         const NFRule* predecessor,
    850                                         const NFRuleSet* _ruleSet,
    851                                         const UnicodeString& description,
    852                                         UErrorCode& status)
    853 : NFSubstitution(_pos, _ruleSet, description, status)
    854 , divisor(rule->getDivisor())
    855 , ruleToUse(nullptr)
    856 {
    857  if (U_FAILURE(status)) return;
    858  // the owning rule's divisor controls the behavior of this
    859  // substitution: rather than keeping a backpointer to the rule,
    860  // we keep a copy of the divisor
    861 
    862  if (divisor == 0) {
    863      status = U_PARSE_ERROR;
    864  }
    865 
    866  if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
    867    // the >>> token doesn't alter how this substitution calculates the
    868    // values it uses for formatting and parsing, but it changes
    869    // what's done with that value after it's obtained: >>> short-
    870    // circuits the rule-search process and goes straight to the
    871    // specified rule to format the substitution value
    872    ruleToUse = predecessor;
    873  }
    874 }
    875 
    876 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution)
    877 
    878 bool ModulusSubstitution::operator==(const NFSubstitution& rhs) const
    879 {
    880  return NFSubstitution::operator==(rhs) &&
    881  divisor == ((const ModulusSubstitution*)&rhs)->divisor &&
    882  ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse;
    883 }
    884 
    885 //-----------------------------------------------------------------------
    886 // formatting
    887 //-----------------------------------------------------------------------
    888 
    889 
    890 /**
    891 * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
    892 * the substitution.  Otherwise, just use the superclass function.
    893 * @param number The number being formatted
    894 * @toInsertInto The string to insert the result of this substitution
    895 * into
    896 * @param pos The position of the rule text in toInsertInto
    897 */
    898 void
    899 ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
    900 {
    901    if (U_FAILURE(status)) return;
    902    // if this isn't a >>> substitution, just use the inherited version
    903    // of this function (which uses either a rule set or a DecimalFormat
    904    // to format its substitution value)
    905    if (ruleToUse == nullptr) {
    906        NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
    907 
    908        // a >>> substitution goes straight to a particular rule to
    909        // format the substitution value
    910    } else {
    911        int64_t numberToFormat = transformNumber(number);
    912        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
    913    }
    914 }
    915 
    916 /**
    917 * If this is a &gt;&gt;&gt; substitution, use ruleToUse to fill in
    918 * the substitution.  Otherwise, just use the superclass function.
    919 * @param number The number being formatted
    920 * @toInsertInto The string to insert the result of this substitution
    921 * into
    922 * @param pos The position of the rule text in toInsertInto
    923 */
    924 void
    925 ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const
    926 {
    927    if (U_FAILURE(status)) return;
    928    // if this isn't a >>> substitution, just use the inherited version
    929    // of this function (which uses either a rule set or a DecimalFormat
    930    // to format its substitution value)
    931    if (ruleToUse == nullptr) {
    932        NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
    933 
    934        // a >>> substitution goes straight to a particular rule to
    935        // format the substitution value
    936    } else {
    937        double numberToFormat = transformNumber(number);
    938 
    939        ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status);
    940    }
    941 }
    942 
    943 //-----------------------------------------------------------------------
    944 // parsing
    945 //-----------------------------------------------------------------------
    946 
    947 /**
    948 * If this is a &gt;&gt;&gt; substitution, match only against ruleToUse.
    949 * Otherwise, use the superclass function.
    950 * @param text The string to parse
    951 * @param parsePosition Ignored on entry, updated on exit to point to
    952 * the first unmatched character.
    953 * @param baseValue The partial parse result prior to calling this
    954 * routine.
    955 */
    956 UBool
    957 ModulusSubstitution::doParse(const UnicodeString& text,
    958                             ParsePosition& parsePosition,
    959                             double baseValue,
    960                             double upperBound,
    961                             UBool lenientParse,
    962                             uint32_t nonNumericalExecutedRuleMask,
    963                             int32_t recursionCount,
    964                             Formattable& result) const
    965 {
    966    // if this isn't a >>> substitution, we can just use the
    967    // inherited parse() routine to do the parsing
    968    if (ruleToUse == nullptr) {
    969        return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, recursionCount, result);
    970 
    971        // but if it IS a >>> substitution, we have to do it here: we
    972        // use the specific rule's doParse() method, and then we have to
    973        // do some of the other work of NFRuleSet.parse()
    974    } else {
    975        ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, recursionCount, result);
    976 
    977        if (parsePosition.getIndex() != 0) {
    978            UErrorCode status = U_ZERO_ERROR;
    979            double tempResult = result.getDouble(status);
    980            tempResult = composeRuleValue(tempResult, baseValue);
    981            result.setDouble(tempResult);
    982        }
    983 
    984        return true;
    985    }
    986 }
    987 /**
    988 * Returns a textual description of the substitution
    989 * @return A textual description of the substitution.  This might
    990 * not be identical to the description it was created from, but
    991 * it'll produce the same result.
    992 */
    993 void
    994 ModulusSubstitution::toString(UnicodeString& text) const
    995 {
    996  // use tokenChar() to get the character at the beginning and
    997  // end of the substitutin token.  In between them will go
    998  // either the name of the rule set it uses, or the pattern of
    999  // the DecimalFormat it uses
   1000 
   1001  if ( ruleToUse != nullptr ) { // Must have been a >>> substitution.
   1002      text.remove();
   1003      text.append(tokenChar());
   1004      text.append(tokenChar());
   1005      text.append(tokenChar());
   1006  } else { // Otherwise just use the super-class function.
   1007   NFSubstitution::toString(text);
   1008  }
   1009 }
   1010 //===================================================================
   1011 // IntegralPartSubstitution
   1012 //===================================================================
   1013 
   1014 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution)
   1015 
   1016 
   1017 //===================================================================
   1018 // FractionalPartSubstitution
   1019 //===================================================================
   1020 
   1021 
   1022    /**
   1023     * Constructs a FractionalPartSubstitution.  This object keeps a flag
   1024     * telling whether it should format by digits or not.  In addition,
   1025     * it marks the rule set it calls (if any) as a fraction rule set.
   1026     */
   1027 FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
   1028                             const NFRuleSet* _ruleSet,
   1029                             const UnicodeString& description,
   1030                             UErrorCode& status)
   1031 : NFSubstitution(_pos, _ruleSet, description, status)
   1032 , byDigits(false)
   1033 , useSpaces(true)
   1034 
   1035 {
   1036    if (U_FAILURE(status)) return;
   1037    // akk, ruleSet can change in superclass constructor
   1038    if (0 == description.compare(gGreaterGreaterThan, 2) ||
   1039        0 == description.compare(gGreaterGreaterGreaterThan, 3) ||
   1040        _ruleSet == getRuleSet()) {
   1041        byDigits = true;
   1042        if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) {
   1043            useSpaces = false;
   1044        }
   1045    } else {
   1046        // cast away const
   1047        NFRuleSet* rs = const_cast<NFRuleSet*>(getRuleSet());
   1048        if (rs != nullptr) {
   1049            rs->makeIntoFractionRuleSet();
   1050        } else {
   1051            status = U_PARSE_ERROR;
   1052        }
   1053    }
   1054 }
   1055 
   1056 //-----------------------------------------------------------------------
   1057 // formatting
   1058 //-----------------------------------------------------------------------
   1059 
   1060 /**
   1061 * If in "by digits" mode, fills in the substitution one decimal digit
   1062 * at a time using the rule set containing this substitution.
   1063 * Otherwise, uses the superclass function.
   1064 * @param number The number being formatted
   1065 * @param toInsertInto The string to insert the result of formatting
   1066 * the substitution into
   1067 * @param pos The position of the owning rule's rule text in
   1068 * toInsertInto
   1069 */
   1070 void
   1071 FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto,
   1072                                           int32_t _pos, int32_t recursionCount, UErrorCode& status) const
   1073 {
   1074  if (U_FAILURE(status)) return;
   1075  // if we're not in "byDigits" mode, just use the inherited
   1076  // doSubstitution() routine
   1077  if (!byDigits) {
   1078    NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status);
   1079 
   1080    // if we're in "byDigits" mode, transform the value into an integer
   1081    // by moving the decimal point eight places to the right and
   1082    // pulling digits off the right one at a time, formatting each digit
   1083    // as an integer using this substitution's owning rule set
   1084    // (this is slower, but more accurate, than doing it from the
   1085    // other end)
   1086  } else {
   1087    //          int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits));
   1088    //          // this flag keeps us from formatting trailing zeros.  It starts
   1089    //          // out false because we're pulling from the right, and switches
   1090    //          // to true the first time we encounter a non-zero digit
   1091    //          UBool doZeros = false;
   1092    //          for (int32_t i = 0; i < kMaxDecimalDigits; i++) {
   1093    //              int64_t digit = numberToFormat % 10;
   1094    //              if (digit != 0 || doZeros) {
   1095    //                  if (doZeros && useSpaces) {
   1096    //                      toInsertInto.insert(_pos + getPos(), gSpace);
   1097    //                  }
   1098    //                  doZeros = true;
   1099    //                  getRuleSet()->format(digit, toInsertInto, _pos + getPos());
   1100    //              }
   1101    //              numberToFormat /= 10;
   1102    //          }
   1103 
   1104    DecimalQuantity dl;
   1105    dl.setToDouble(number);
   1106    dl.roundToMagnitude(-20, UNUM_ROUND_HALFEVEN, status);     // round to 20 fraction digits.
   1107    
   1108    UBool pad = false;
   1109    for (int32_t didx = dl.getLowerDisplayMagnitude(); didx<0; didx++) {
   1110      // Loop iterates over fraction digits, starting with the LSD.
   1111      //   include both real digits from the number, and zeros
   1112      //   to the left of the MSD but to the right of the decimal point.
   1113      if (pad && useSpaces) {
   1114        toInsertInto.insert(_pos + getPos(), gSpace);
   1115      } else {
   1116        pad = true;
   1117      }
   1118      int64_t digit = dl.getDigit(didx);
   1119      getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status);
   1120    }
   1121 
   1122    if (!pad) {
   1123      // hack around lack of precision in digitlist. if we would end up with
   1124      // "foo point" make sure we add a " zero" to the end.
   1125      getRuleSet()->format(static_cast<int64_t>(0), toInsertInto, _pos + getPos(), recursionCount, status);
   1126    }
   1127  }
   1128 }
   1129 
   1130 //-----------------------------------------------------------------------
   1131 // parsing
   1132 //-----------------------------------------------------------------------
   1133 
   1134 /**
   1135 * If in "by digits" mode, parses the string as if it were a string
   1136 * of individual digits; otherwise, uses the superclass function.
   1137 * @param text The string to parse
   1138 * @param parsePosition Ignored on entry, but updated on exit to point
   1139 * to the first unmatched character
   1140 * @param baseValue The partial parse result prior to entering this
   1141 * function
   1142 * @param upperBound Only consider rules with base values lower than
   1143 * this when filling in the substitution
   1144 * @param lenientParse If true, try matching the text as numerals if
   1145 * matching as words doesn't work
   1146 * @return If the match was successful, the current partial parse
   1147 * result; otherwise new Long(0).  The result is either a Long or
   1148 * a Double.
   1149 */
   1150 
   1151 UBool
   1152 FractionalPartSubstitution::doParse(const UnicodeString& text,
   1153                ParsePosition& parsePosition,
   1154                double baseValue,
   1155                double /*upperBound*/,
   1156                UBool lenientParse,
   1157                uint32_t nonNumericalExecutedRuleMask,
   1158                int32_t recursionCount,
   1159                Formattable& resVal) const
   1160 {
   1161    // if we're not in byDigits mode, we can just use the inherited
   1162    // doParse()
   1163    if (!byDigits) {
   1164        return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, recursionCount, resVal);
   1165 
   1166        // if we ARE in byDigits mode, parse the text one digit at a time
   1167        // using this substitution's owning rule set (we do this by setting
   1168        // upperBound to 10 when calling doParse() ) until we reach
   1169        // nonmatching text
   1170    } else {
   1171        UnicodeString workText(text);
   1172        ParsePosition workPos(1);
   1173        double result = 0;
   1174        int32_t digit;
   1175 //          double p10 = 0.1;
   1176 
   1177        DecimalQuantity dl;
   1178        int32_t totalDigits = 0;
   1179        NumberFormat* fmt = nullptr;
   1180        while (workText.length() > 0 && workPos.getIndex() != 0) {
   1181            workPos.setIndex(0);
   1182            Formattable temp;
   1183            getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, recursionCount, temp);
   1184            UErrorCode status = U_ZERO_ERROR;
   1185            digit = temp.getLong(status);
   1186 //            digit = temp.getType() == Formattable::kLong ?
   1187 //               temp.getLong() :
   1188 //            (int32_t)temp.getDouble();
   1189 
   1190            if (lenientParse && workPos.getIndex() == 0) {
   1191                if (!fmt) {
   1192                    status = U_ZERO_ERROR;
   1193                    fmt = NumberFormat::createInstance(status);
   1194                    if (U_FAILURE(status)) {
   1195                        delete fmt;
   1196                        fmt = nullptr;
   1197                    }
   1198                }
   1199                if (fmt) {
   1200                    fmt->parse(workText, temp, workPos);
   1201                    digit = temp.getLong(status);
   1202                }
   1203            }
   1204 
   1205            if (workPos.getIndex() != 0) {
   1206                dl.appendDigit(static_cast<int8_t>(digit), 0, true);
   1207                totalDigits++;
   1208 //                  result += digit * p10;
   1209 //                  p10 /= 10;
   1210                parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
   1211                workText.removeBetween(0, workPos.getIndex());
   1212                while (workText.length() > 0 && workText.charAt(0) == gSpace) {
   1213                    workText.removeBetween(0, 1);
   1214                    parsePosition.setIndex(parsePosition.getIndex() + 1);
   1215                }
   1216            }
   1217        }
   1218        delete fmt;
   1219 
   1220        dl.adjustMagnitude(-totalDigits);
   1221        result = dl.toDouble();
   1222        result = composeRuleValue(result, baseValue);
   1223        resVal.setDouble(result);
   1224        return true;
   1225    }
   1226 }
   1227 
   1228 bool
   1229 FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const
   1230 {
   1231  return NFSubstitution::operator==(rhs) &&
   1232  ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits;
   1233 }
   1234 
   1235 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution)
   1236 
   1237 
   1238 //===================================================================
   1239 // AbsoluteValueSubstitution
   1240 //===================================================================
   1241 
   1242 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution)
   1243 
   1244 //===================================================================
   1245 // NumeratorSubstitution
   1246 //===================================================================
   1247 
   1248 void
   1249 NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const {
   1250    if (U_FAILURE(status)) return;
   1251    // perform a transformation on the number being formatted that
   1252    // is dependent on the type of substitution this is
   1253 
   1254    double numberToFormat = transformNumber(number);
   1255    int64_t longNF = util64_fromDouble(numberToFormat);
   1256 
   1257    const NFRuleSet* aruleSet = getRuleSet();
   1258    if (withZeros && aruleSet != nullptr) {
   1259        // if there are leading zeros in the decimal expansion then emit them
   1260        int64_t nf =longNF;
   1261        int32_t len = toInsertInto.length();
   1262        while ((nf *= 10) < denominator) {
   1263            toInsertInto.insert(apos + getPos(), gSpace);
   1264            aruleSet->format(static_cast<int64_t>(0), toInsertInto, apos + getPos(), recursionCount, status);
   1265        }
   1266        apos += toInsertInto.length() - len;
   1267    }
   1268 
   1269    // if the result is an integer, from here on out we work in integer
   1270    // space (saving time and memory and preserving accuracy)
   1271    if (numberToFormat == longNF && aruleSet != nullptr) {
   1272        aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status);
   1273 
   1274        // if the result isn't an integer, then call either our rule set's
   1275        // format() method or our DecimalFormat's format() method to
   1276        // format the result
   1277    } else {
   1278        if (aruleSet != nullptr) {
   1279            aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status);
   1280        } else {
   1281            UnicodeString temp;
   1282            getNumberFormat()->format(numberToFormat, temp, status);
   1283            toInsertInto.insert(apos + getPos(), temp);
   1284        }
   1285    }
   1286 }
   1287 
   1288 UBool 
   1289 NumeratorSubstitution::doParse(const UnicodeString& text, 
   1290                               ParsePosition& parsePosition,
   1291                               double baseValue,
   1292                               double upperBound,
   1293                               UBool /*lenientParse*/,
   1294                               uint32_t nonNumericalExecutedRuleMask,
   1295                               int32_t recursionCount,
   1296                               Formattable& result) const
   1297 {
   1298    // we don't have to do anything special to do the parsing here,
   1299    // but we have to turn lenient parsing off-- if we leave it on,
   1300    // it SERIOUSLY messes up the algorithm
   1301 
   1302    // if withZeros is true, we need to count the zeros
   1303    // and use that to adjust the parse result
   1304    UErrorCode status = U_ZERO_ERROR;
   1305    int32_t zeroCount = 0;
   1306    UnicodeString workText(text);
   1307 
   1308    if (withZeros) {
   1309        ParsePosition workPos(1);
   1310        Formattable temp;
   1311 
   1312        while (workText.length() > 0 && workPos.getIndex() != 0) {
   1313            workPos.setIndex(0);
   1314            getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, recursionCount, temp); // parse zero or nothing at all
   1315            if (workPos.getIndex() == 0) {
   1316                // we failed, either there were no more zeros, or the number was formatted with digits
   1317                // either way, we're done
   1318                break;
   1319            }
   1320 
   1321            ++zeroCount;
   1322            parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
   1323            workText.remove(0, workPos.getIndex());
   1324            while (workText.length() > 0 && workText.charAt(0) == gSpace) {
   1325                workText.remove(0, 1);
   1326                parsePosition.setIndex(parsePosition.getIndex() + 1);
   1327            }
   1328        }
   1329 
   1330        workText = text;
   1331        workText.remove(0, parsePosition.getIndex());
   1332        parsePosition.setIndex(0);
   1333    }
   1334 
   1335    // we've parsed off the zeros, now let's parse the rest from our current position
   1336    NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, recursionCount, result);
   1337 
   1338    if (withZeros) {
   1339        // any base value will do in this case.  is there a way to
   1340        // force this to not bother trying all the base values?
   1341 
   1342        // compute the 'effective' base and prescale the value down
   1343        int64_t n = result.getLong(status); // force conversion!
   1344        int64_t d = 1;
   1345        while (d <= n) {
   1346            d *= 10;
   1347        }
   1348        // now add the zeros
   1349        while (zeroCount > 0) {
   1350            d *= 10;
   1351            --zeroCount;
   1352        }
   1353        // d is now our true denominator
   1354        result.setDouble(static_cast<double>(n) / static_cast<double>(d));
   1355    }
   1356 
   1357    return true;
   1358 }
   1359 
   1360 bool
   1361 NumeratorSubstitution::operator==(const NFSubstitution& rhs) const
   1362 {
   1363    return NFSubstitution::operator==(rhs) &&
   1364        denominator == ((const NumeratorSubstitution*)&rhs)->denominator;
   1365 }
   1366 
   1367 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution)
   1368 
   1369 const char16_t NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c };
   1370        
   1371 U_NAMESPACE_END
   1372 
   1373 /* U_HAVE_RBNF */
   1374 #endif