]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/nfrs.cpp
ICU-461.18.tar.gz
[apple/icu.git] / icuSources / i18n / nfrs.cpp
CommitLineData
b75a7d8f
A
1/*
2******************************************************************************
46f4442e 3* Copyright (C) 1997-2008, International Business Machines
b75a7d8f
A
4* Corporation and others. All Rights Reserved.
5******************************************************************************
6* file name: nfrs.cpp
7* encoding: US-ASCII
8* tab size: 8 (not used)
9* indentation:4
10*
11* Modification history
12* Date Name Comments
13* 10/11/2001 Doug Ported from ICU4J
14*/
15
16#include "nfrs.h"
17
18#if U_HAVE_RBNF
19
20#include "unicode/uchar.h"
21#include "nfrule.h"
22#include "nfrlist.h"
23
24#ifdef RBNF_DEBUG
25#include "cmemory.h"
26#endif
27
374ca955 28#include "util.h"
b75a7d8f
A
29
30U_NAMESPACE_BEGIN
31
32#if 0
33// euclid's algorithm works with doubles
34// note, doubles only get us up to one quadrillion or so, which
35// isn't as much range as we get with longs. We probably still
36// want either 64-bit math, or BigInteger.
37
38static int64_t
39util_lcm(int64_t x, int64_t y)
40{
41 x.abs();
42 y.abs();
43
44 if (x == 0 || y == 0) {
45 return 0;
46 } else {
47 do {
48 if (x < y) {
49 int64_t t = x; x = y; y = t;
50 }
51 x -= y * (x/y);
52 } while (x != 0);
53
54 return y;
55 }
56}
57
58#else
59/**
60 * Calculates the least common multiple of x and y.
61 */
62static int64_t
63util_lcm(int64_t x, int64_t y)
64{
65 // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
66 // vol. 2, 1st ed., pp. 298-299
67 int64_t x1 = x;
68 int64_t y1 = y;
69
70 int p2 = 0;
71 while ((x1 & 1) == 0 && (y1 & 1) == 0) {
72 ++p2;
73 x1 >>= 1;
74 y1 >>= 1;
75 }
76
77 int64_t t;
78 if ((x1 & 1) == 1) {
79 t = -y1;
80 } else {
81 t = x1;
82 }
83
84 while (t != 0) {
85 while ((t & 1) == 0) {
86 t = t >> 1;
87 }
88 if (t > 0) {
89 x1 = t;
90 } else {
91 y1 = -t;
92 }
93 t = x1 - y1;
94 }
95
96 int64_t gcd = x1 << p2;
97
98 // x * y == gcd(x, y) * lcm(x, y)
99 return x / gcd * y;
100}
101#endif
102
103static const UChar gPercent = 0x0025;
104static const UChar gColon = 0x003a;
105static const UChar gSemicolon = 0x003b;
106static const UChar gLineFeed = 0x000a;
107
108static const UChar gFourSpaces[] =
109{
110 0x20, 0x20, 0x20, 0x20, 0
111}; /* " " */
112static const UChar gPercentPercent[] =
113{
114 0x25, 0x25, 0
115}; /* "%%" */
116
117NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& status)
118 : name()
119 , rules(0)
120 , negativeNumberRule(NULL)
121 , fIsFractionRuleSet(FALSE)
122 , fIsPublic(FALSE)
374ca955 123 , fRecursionCount(0)
b75a7d8f
A
124{
125 for (int i = 0; i < 3; ++i) {
126 fractionRules[i] = NULL;
127 }
128
129 if (U_FAILURE(status)) {
130 return;
131 }
132
133 UnicodeString& description = descriptions[index]; // !!! make sure index is valid
134
374ca955
A
135 if (description.length() == 0) {
136 // throw new IllegalArgumentException("Empty rule set description");
137 status = U_PARSE_ERROR;
73c04bcf 138 return;
374ca955
A
139 }
140
b75a7d8f
A
141 // if the description begins with a rule set name (the rule set
142 // name can be omitted in formatter descriptions that consist
143 // of only one rule set), copy it out into our "name" member
144 // and delete it from the description
145 if (description.charAt(0) == gPercent) {
146 int32_t pos = description.indexOf(gColon);
147 if (pos == -1) {
148 // throw new IllegalArgumentException("Rule set name doesn't end in colon");
149 status = U_PARSE_ERROR;
150 } else {
151 name.setTo(description, 0, pos);
152 while (pos < description.length() && uprv_isRuleWhiteSpace(description.charAt(++pos))) {
153 }
154 description.remove(0, pos);
155 }
156 } else {
374ca955 157 name.setTo(UNICODE_STRING_SIMPLE("%default"));
b75a7d8f
A
158 }
159
160 if (description.length() == 0) {
161 // throw new IllegalArgumentException("Empty rule set description");
162 status = U_PARSE_ERROR;
163 }
164
165 fIsPublic = name.indexOf(gPercentPercent) != 0;
166
167 // all of the other members of NFRuleSet are initialized
168 // by parseRules()
169}
170
171void
172NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* owner, UErrorCode& status)
173{
174 // start by creating a Vector whose elements are Strings containing
175 // the descriptions of the rules (one rule per element). The rules
176 // are separated by semicolons (there's no escape facility: ALL
177 // semicolons are rule delimiters)
178
179 if (U_FAILURE(status)) {
180 return;
181 }
182
183 // dlf - the original code kept a separate description array for no reason,
184 // so I got rid of it. The loop was too complex so I simplified it.
185
186 UnicodeString currentDescription;
187 int32_t oldP = 0;
188 while (oldP < description.length()) {
189 int32_t p = description.indexOf(gSemicolon, oldP);
190 if (p == -1) {
191 p = description.length();
192 }
193 currentDescription.setTo(description, oldP, p - oldP);
194 NFRule::makeRules(currentDescription, this, rules.last(), owner, rules, status);
195 oldP = p + 1;
196 }
197
198 // for rules that didn't specify a base value, their base values
199 // were initialized to 0. Make another pass through the list and
200 // set all those rules' base values. We also remove any special
201 // rules from the list and put them into their own member variables
202 int64_t defaultBaseValue = 0;
203
204 // (this isn't a for loop because we might be deleting items from
205 // the vector-- we want to make sure we only increment i when
206 // we _didn't_ delete aything from the vector)
207 uint32_t i = 0;
208 while (i < rules.size()) {
209 NFRule* rule = rules[i];
210
211 switch (rule->getType()) {
212 // if the rule's base value is 0, fill in a default
213 // base value (this will be 1 plus the preceding
214 // rule's base value for regular rule sets, and the
215 // same as the preceding rule's base value in fraction
216 // rule sets)
217 case NFRule::kNoBase:
374ca955 218 rule->setBaseValue(defaultBaseValue, status);
b75a7d8f
A
219 if (!isFractionRuleSet()) {
220 ++defaultBaseValue;
221 }
222 ++i;
223 break;
224
225 // if it's the negative-number rule, copy it into its own
226 // data member and delete it from the list
227 case NFRule::kNegativeNumberRule:
228 negativeNumberRule = rules.remove(i);
229 break;
230
231 // if it's the improper fraction rule, copy it into the
232 // correct element of fractionRules
233 case NFRule::kImproperFractionRule:
234 fractionRules[0] = rules.remove(i);
235 break;
236
237 // if it's the proper fraction rule, copy it into the
238 // correct element of fractionRules
239 case NFRule::kProperFractionRule:
240 fractionRules[1] = rules.remove(i);
241 break;
242
243 // if it's the master rule, copy it into the
244 // correct element of fractionRules
245 case NFRule::kMasterRule:
246 fractionRules[2] = rules.remove(i);
247 break;
248
249 // if it's a regular rule that already knows its base value,
250 // check to make sure the rules are in order, and update
251 // the default base value for the next rule
252 default:
253 if (rule->getBaseValue() < defaultBaseValue) {
254 // throw new IllegalArgumentException("Rules are not in order");
255 status = U_PARSE_ERROR;
256 return;
257 }
258 defaultBaseValue = rule->getBaseValue();
259 if (!isFractionRuleSet()) {
260 ++defaultBaseValue;
261 }
262 ++i;
263 break;
264 }
265 }
266}
267
268NFRuleSet::~NFRuleSet()
269{
270 delete negativeNumberRule;
271 delete fractionRules[0];
272 delete fractionRules[1];
273 delete fractionRules[2];
274}
275
374ca955 276static UBool
b75a7d8f
A
277util_equalRules(const NFRule* rule1, const NFRule* rule2)
278{
279 if (rule1) {
280 if (rule2) {
281 return *rule1 == *rule2;
282 }
283 } else if (!rule2) {
284 return TRUE;
285 }
286 return FALSE;
287}
288
289UBool
290NFRuleSet::operator==(const NFRuleSet& rhs) const
291{
292 if (rules.size() == rhs.rules.size() &&
293 fIsFractionRuleSet == rhs.fIsFractionRuleSet &&
294 name == rhs.name &&
295 util_equalRules(negativeNumberRule, rhs.negativeNumberRule) &&
296 util_equalRules(fractionRules[0], rhs.fractionRules[0]) &&
297 util_equalRules(fractionRules[1], rhs.fractionRules[1]) &&
298 util_equalRules(fractionRules[2], rhs.fractionRules[2])) {
299
300 for (uint32_t i = 0; i < rules.size(); ++i) {
301 if (*rules[i] != *rhs.rules[i]) {
302 return FALSE;
303 }
304 }
305 return TRUE;
306 }
307 return FALSE;
308}
309
374ca955
A
310#define RECURSION_LIMIT 50
311
b75a7d8f
A
312void
313NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos) const
314{
315 NFRule *rule = findNormalRule(number);
374ca955
A
316 if (rule) { // else error, but can't report it
317 NFRuleSet* ncThis = (NFRuleSet*)this;
318 if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) {
319 // stop recursion
320 ncThis->fRecursionCount = 0;
321 } else {
322 rule->doFormat(number, toAppendTo, pos);
323 ncThis->fRecursionCount--;
324 }
325 }
b75a7d8f
A
326}
327
328void
329NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos) const
330{
331 NFRule *rule = findDoubleRule(number);
374ca955
A
332 if (rule) { // else error, but can't report it
333 NFRuleSet* ncThis = (NFRuleSet*)this;
334 if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) {
335 // stop recursion
336 ncThis->fRecursionCount = 0;
337 } else {
338 rule->doFormat(number, toAppendTo, pos);
339 ncThis->fRecursionCount--;
340 }
341 }
b75a7d8f
A
342}
343
344NFRule*
345NFRuleSet::findDoubleRule(double number) const
346{
347 // if this is a fraction rule set, use findFractionRuleSetRule()
348 if (isFractionRuleSet()) {
349 return findFractionRuleSetRule(number);
350 }
351
352 // if the number is negative, return the negative number rule
353 // (if there isn't a negative-number rule, we pretend it's a
354 // positive number)
355 if (number < 0) {
356 if (negativeNumberRule) {
357 return negativeNumberRule;
358 } else {
359 number = -number;
360 }
361 }
362
363 // if the number isn't an integer, we use one of the fraction rules...
364 if (number != uprv_floor(number)) {
365 // if the number is between 0 and 1, return the proper
366 // fraction rule
367 if (number < 1 && fractionRules[1]) {
368 return fractionRules[1];
369 }
370 // otherwise, return the improper fraction rule
371 else if (fractionRules[0]) {
372 return fractionRules[0];
373 }
374 }
375
376 // if there's a master rule, use it to format the number
377 if (fractionRules[2]) {
378 return fractionRules[2];
379 }
380
729e4ab9
A
381 // always use the last rule for infinity. It is likely that rule
382 // will had a DecimalFormat that will do the right thing with infinity even
383 // if the rule's base value is strange, i.e. something larger than what
384 // util64_fromDouble produces below.
385 if (uprv_isInfinite(number) && (rules.size() > 0)) {
386 return rules[rules.size() - 1];
387 }
388
b75a7d8f
A
389 // and if we haven't yet returned a rule, use findNormalRule()
390 // to find the applicable rule
391 int64_t r = util64_fromDouble(number + 0.5);
392 return findNormalRule(r);
393}
394
395NFRule *
396NFRuleSet::findNormalRule(int64_t number) const
397{
398 // if this is a fraction rule set, use findFractionRuleSetRule()
399 // to find the rule (we should only go into this clause if the
400 // value is 0)
401 if (fIsFractionRuleSet) {
402 return findFractionRuleSetRule((double)number);
403 }
404
405 // if the number is negative, return the negative-number rule
406 // (if there isn't one, pretend the number is positive)
407 if (number < 0) {
408 if (negativeNumberRule) {
409 return negativeNumberRule;
410 } else {
411 number = -number;
412 }
413 }
414
415 // we have to repeat the preceding two checks, even though we
416 // do them in findRule(), because the version of format() that
417 // takes a long bypasses findRule() and goes straight to this
418 // function. This function does skip the fraction rules since
419 // we know the value is an integer (it also skips the master
420 // rule, since it's considered a fraction rule. Skipping the
421 // master rule in this function is also how we avoid infinite
422 // recursion)
423
424 // {dlf} unfortunately this fails if there are no rules except
425 // special rules. If there are no rules, use the master rule.
426
427 // binary-search the rule list for the applicable rule
428 // (a rule is used for all values from its base value to
429 // the next rule's base value)
430 int32_t hi = rules.size();
431 if (hi > 0) {
432 int32_t lo = 0;
433
434 while (lo < hi) {
435 int32_t mid = (lo + hi) / 2;
436 if (rules[mid]->getBaseValue() == number) {
437 return rules[mid];
438 }
439 else if (rules[mid]->getBaseValue() > number) {
440 hi = mid;
441 }
442 else {
443 lo = mid + 1;
444 }
445 }
374ca955
A
446 if (hi == 0) { // bad rule set, minimum base > 0
447 return NULL; // want to throw exception here
448 }
449
b75a7d8f
A
450 NFRule *result = rules[hi - 1];
451
452 // use shouldRollBack() to see whether we need to invoke the
453 // rollback rule (see shouldRollBack()'s documentation for
454 // an explanation of the rollback rule). If we do, roll back
455 // one rule and return that one instead of the one we'd normally
456 // return
457 if (result->shouldRollBack((double)number)) {
374ca955
A
458 if (hi == 1) { // bad rule set, no prior rule to rollback to from this base
459 return NULL;
460 }
b75a7d8f
A
461 result = rules[hi - 2];
462 }
463 return result;
464 }
465 // else use the master rule
466 return fractionRules[2];
467}
468
469/**
470 * If this rule is a fraction rule set, this function is used by
471 * findRule() to select the most appropriate rule for formatting
472 * the number. Basically, the base value of each rule in the rule
473 * set is treated as the denominator of a fraction. Whichever
474 * denominator can produce the fraction closest in value to the
475 * number passed in is the result. If there's a tie, the earlier
476 * one in the list wins. (If there are two rules in a row with the
477 * same base value, the first one is used when the numerator of the
478 * fraction would be 1, and the second rule is used the rest of the
479 * time.
480 * @param number The number being formatted (which will always be
481 * a number between 0 and 1)
482 * @return The rule to use to format this number
483 */
484NFRule*
485NFRuleSet::findFractionRuleSetRule(double number) const
486{
487 // the obvious way to do this (multiply the value being formatted
488 // by each rule's base value until you get an integral result)
489 // doesn't work because of rounding error. This method is more
490 // accurate
491
492 // find the least common multiple of the rules' base values
493 // and multiply this by the number being formatted. This is
494 // all the precision we need, and we can do all of the rest
495 // of the math using integer arithmetic
496 int64_t leastCommonMultiple = rules[0]->getBaseValue();
497 int64_t numerator;
498 {
499 for (uint32_t i = 1; i < rules.size(); ++i) {
500 leastCommonMultiple = util_lcm(leastCommonMultiple, rules[i]->getBaseValue());
501 }
502 numerator = util64_fromDouble(number * (double)leastCommonMultiple + 0.5);
503 }
504 // for each rule, do the following...
505 int64_t tempDifference;
506 int64_t difference = util64_fromDouble(uprv_maxMantissa());
507 int32_t winner = 0;
508 for (uint32_t i = 0; i < rules.size(); ++i) {
509 // "numerator" is the numerator of the fraction if the
510 // denominator is the LCD. The numerator if the rule's
511 // base value is the denominator is "numerator" times the
512 // base value divided bythe LCD. Here we check to see if
513 // that's an integer, and if not, how close it is to being
514 // an integer.
515 tempDifference = numerator * rules[i]->getBaseValue() % leastCommonMultiple;
516
517
518 // normalize the result of the above calculation: we want
519 // the numerator's distance from the CLOSEST multiple
520 // of the LCD
521 if (leastCommonMultiple - tempDifference < tempDifference) {
522 tempDifference = leastCommonMultiple - tempDifference;
523 }
524
525 // if this is as close as we've come, keep track of how close
526 // that is, and the line number of the rule that did it. If
527 // we've scored a direct hit, we don't have to look at any more
528 // rules
529 if (tempDifference < difference) {
530 difference = tempDifference;
531 winner = i;
532 if (difference == 0) {
533 break;
534 }
535 }
536 }
537
538 // if we have two successive rules that both have the winning base
539 // value, then the first one (the one we found above) is used if
540 // the numerator of the fraction is 1 and the second one is used if
541 // the numerator of the fraction is anything else (this lets us
542 // do things like "one third"/"two thirds" without haveing to define
543 // a whole bunch of extra rule sets)
544 if ((unsigned)(winner + 1) < rules.size() &&
545 rules[winner + 1]->getBaseValue() == rules[winner]->getBaseValue()) {
546 double n = ((double)rules[winner]->getBaseValue()) * number;
547 if (n < 0.5 || n >= 2) {
548 ++winner;
549 }
550 }
551
552 // finally, return the winning rule
553 return rules[winner];
554}
555
556/**
557 * Parses a string. Matches the string to be parsed against each
558 * of its rules (with a base value less than upperBound) and returns
559 * the value produced by the rule that matched the most charcters
560 * in the source string.
561 * @param text The string to parse
562 * @param parsePosition The initial position is ignored and assumed
563 * to be 0. On exit, this object has been updated to point to the
564 * first character position this rule set didn't consume.
565 * @param upperBound Limits the rules that can be allowed to match.
566 * Only rules whose base values are strictly less than upperBound
567 * are considered.
568 * @return The numerical result of parsing this string. This will
569 * be the matching rule's base value, composed appropriately with
570 * the results of matching any of its substitutions. The object
571 * will be an instance of Long if it's an integral value; otherwise,
572 * it will be an instance of Double. This function always returns
573 * a valid object: If nothing matched the input string at all,
574 * this function returns new Long(0), and the parse position is
575 * left unchanged.
576 */
577#ifdef RBNF_DEBUG
578#include <stdio.h>
579
580static void dumpUS(FILE* f, const UnicodeString& us) {
581 int len = us.length();
582 char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1];
46f4442e
A
583 if (buf != NULL) {
584 us.extract(0, len, buf);
585 buf[len] = 0;
586 fprintf(f, "%s", buf);
587 uprv_free(buf); //delete[] buf;
588 }
b75a7d8f
A
589}
590#endif
591
592UBool
729e4ab9 593NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result, UBool lenient) const
b75a7d8f
A
594{
595 // try matching each rule in the rule set against the text being
596 // parsed. Whichever one matches the most characters is the one
597 // that determines the value we return.
598
599 result.setLong(0);
600
601 // dump out if there's no text to parse
602 if (text.length() == 0) {
603 return 0;
604 }
605
606 ParsePosition highWaterMark;
607 ParsePosition workingPos = pos;
608
609#ifdef RBNF_DEBUG
610 fprintf(stderr, "<nfrs> %x '", this);
611 dumpUS(stderr, name);
612 fprintf(stderr, "' text '");
613 dumpUS(stderr, text);
614 fprintf(stderr, "'\n");
615 fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0);
616#endif
617
618 // start by trying the negative number rule (if there is one)
619 if (negativeNumberRule) {
620 Formattable tempResult;
621#ifdef RBNF_DEBUG
622 fprintf(stderr, " <nfrs before negative> %x ub: %g\n", negativeNumberRule, upperBound);
623#endif
624 UBool success = negativeNumberRule->doParse(text, workingPos, 0, upperBound, tempResult);
625#ifdef RBNF_DEBUG
626 fprintf(stderr, " <nfrs after negative> success: %d wpi: %d\n", success, workingPos.getIndex());
627#endif
628 if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
629 result = tempResult;
630 highWaterMark = workingPos;
631 }
632 workingPos = pos;
633 }
634#ifdef RBNF_DEBUG
635 fprintf(stderr, "<nfrs> continue fractional with text '");
636 dumpUS(stderr, text);
637 fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex());
638#endif
639 // then try each of the fraction rules
640 {
641 for (int i = 0; i < 3; i++) {
642 if (fractionRules[i]) {
643 Formattable tempResult;
729e4ab9 644 UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult, lenient || isDecimalFormatRuleParseable() );
b75a7d8f
A
645 if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
646 result = tempResult;
647 highWaterMark = workingPos;
648 }
649 workingPos = pos;
650 }
651 }
652 }
653#ifdef RBNF_DEBUG
654 fprintf(stderr, "<nfrs> continue other with text '");
655 dumpUS(stderr, text);
656 fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex());
657#endif
658
659 // finally, go through the regular rules one at a time. We start
660 // at the end of the list because we want to try matching the most
661 // sigificant rule first (this helps ensure that we parse
662 // "five thousand three hundred six" as
663 // "(five thousand) (three hundred) (six)" rather than
664 // "((five thousand three) hundred) (six)"). Skip rules whose
665 // base values are higher than the upper bound (again, this helps
666 // limit ambiguity by making sure the rules that match a rule's
667 // are less significant than the rule containing the substitutions)/
668 {
669 int64_t ub = util64_fromDouble(upperBound);
670#ifdef RBNF_DEBUG
671 {
672 char ubstr[64];
673 util64_toa(ub, ubstr, 64);
674 char ubstrhex[64];
675 util64_toa(ub, ubstrhex, 64, 16);
676 fprintf(stderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex);
677 }
678#endif
679 for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) {
680 if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) {
681 continue;
682 }
683 Formattable tempResult;
684 UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, tempResult);
685 if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
686 result = tempResult;
687 highWaterMark = workingPos;
688 }
689 workingPos = pos;
690 }
691 }
692#ifdef RBNF_DEBUG
693 fprintf(stderr, "<nfrs> exit\n");
694#endif
695 // finally, update the parse postion we were passed to point to the
696 // first character we didn't use, and return the result that
697 // corresponds to that string of characters
698 pos = highWaterMark;
699
700 return 1;
701}
702
703void
704NFRuleSet::appendRules(UnicodeString& result) const
705{
706 // the rule set name goes first...
707 result.append(name);
708 result.append(gColon);
709 result.append(gLineFeed);
710
711 // followed by the regular rules...
712 for (uint32_t i = 0; i < rules.size(); i++) {
713 result.append(gFourSpaces);
73c04bcf 714 rules[i]->_appendRuleText(result);
b75a7d8f
A
715 result.append(gLineFeed);
716 }
717
718 // followed by the special rules (if they exist)
719 if (negativeNumberRule) {
720 result.append(gFourSpaces);
73c04bcf 721 negativeNumberRule->_appendRuleText(result);
b75a7d8f
A
722 result.append(gLineFeed);
723 }
724
725 {
726 for (uint32_t i = 0; i < 3; ++i) {
727 if (fractionRules[i]) {
728 result.append(gFourSpaces);
73c04bcf 729 fractionRules[i]->_appendRuleText(result);
b75a7d8f
A
730 result.append(gLineFeed);
731 }
732 }
733 }
734}
735
736// utility functions
737
738int64_t util64_fromDouble(double d) {
739 int64_t result = 0;
740 if (!uprv_isNaN(d)) {
741 double mant = uprv_maxMantissa();
742 if (d < -mant) {
743 d = -mant;
744 } else if (d > mant) {
745 d = mant;
746 }
747 UBool neg = d < 0;
748 if (neg) {
749 d = -d;
750 }
751 result = (int64_t)uprv_floor(d);
752 if (neg) {
753 result = -result;
754 }
755 }
756 return result;
757}
758
759int64_t util64_pow(int32_t r, uint32_t e) {
760 if (r == 0) {
761 return 0;
762 } else if (e == 0) {
763 return 1;
764 } else {
765 int64_t n = r;
766 while (--e > 0) {
767 n *= r;
768 }
769 return n;
770 }
771}
772
773static const uint8_t asciiDigits[] = {
774 0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u,
775 0x38u, 0x39u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u,
776 0x67u, 0x68u, 0x69u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu,
777 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, 0x75u, 0x76u,
778 0x77u, 0x78u, 0x79u, 0x7au,
779};
780
781static const UChar kUMinus = (UChar)0x002d;
782
73c04bcf 783#ifdef RBNF_DEBUG
b75a7d8f
A
784static const char kMinus = '-';
785
786static const uint8_t digitInfo[] = {
787 0, 0, 0, 0, 0, 0, 0, 0,
788 0, 0, 0, 0, 0, 0, 0, 0,
789 0, 0, 0, 0, 0, 0, 0, 0,
790 0, 0, 0, 0, 0, 0, 0, 0,
791 0, 0, 0, 0, 0, 0, 0, 0,
792 0, 0, 0, 0, 0, 0, 0, 0,
793 0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u,
794 0x88u, 0x89u, 0, 0, 0, 0, 0, 0,
795 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u,
796 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u,
797 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u,
798 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0,
799 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u,
800 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u,
801 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u,
802 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0,
803};
804
b75a7d8f
A
805int64_t util64_atoi(const char* str, uint32_t radix)
806{
807 if (radix > 36) {
808 radix = 36;
809 } else if (radix < 2) {
810 radix = 2;
811 }
812 int64_t lradix = radix;
813
814 int neg = 0;
815 if (*str == kMinus) {
816 ++str;
817 neg = 1;
818 }
819 int64_t result = 0;
820 uint8_t b;
821 while ((b = digitInfo[*str++]) && ((b &= 0x7f) < radix)) {
822 result *= lradix;
823 result += (int32_t)b;
824 }
825 if (neg) {
826 result = -result;
827 }
828 return result;
829}
b75a7d8f
A
830
831int64_t util64_utoi(const UChar* str, uint32_t radix)
832{
833 if (radix > 36) {
834 radix = 36;
835 } else if (radix < 2) {
836 radix = 2;
837 }
838 int64_t lradix = radix;
839
840 int neg = 0;
841 if (*str == kUMinus) {
842 ++str;
843 neg = 1;
844 }
845 int64_t result = 0;
846 UChar c;
847 uint8_t b;
848 while (((c = *str++) < 0x0080) && (b = digitInfo[c]) && ((b &= 0x7f) < radix)) {
849 result *= lradix;
850 result += (int32_t)b;
851 }
852 if (neg) {
853 result = -result;
854 }
855 return result;
856}
857
b75a7d8f
A
858uint32_t util64_toa(int64_t w, char* buf, uint32_t len, uint32_t radix, UBool raw)
859{
860 if (radix > 36) {
861 radix = 36;
862 } else if (radix < 2) {
863 radix = 2;
864 }
865 int64_t base = radix;
866
867 char* p = buf;
868 if (len && (w < 0) && (radix == 10) && !raw) {
869 w = -w;
870 *p++ = kMinus;
871 --len;
872 } else if (len && (w == 0)) {
873 *p++ = (char)raw ? 0 : asciiDigits[0];
874 --len;
875 }
876
877 while (len && w != 0) {
878 int64_t n = w / base;
879 int64_t m = n * base;
880 int32_t d = (int32_t)(w-m);
881 *p++ = raw ? (char)d : asciiDigits[d];
882 w = n;
883 --len;
884 }
885 if (len) {
886 *p = 0; // null terminate if room for caller convenience
887 }
888
889 len = p - buf;
890 if (*buf == kMinus) {
891 ++buf;
892 }
893 while (--p > buf) {
894 char c = *p;
895 *p = *buf;
896 *buf = c;
897 ++buf;
898 }
899
900 return len;
901}
902#endif
903
904uint32_t util64_tou(int64_t w, UChar* buf, uint32_t len, uint32_t radix, UBool raw)
905{
906 if (radix > 36) {
907 radix = 36;
908 } else if (radix < 2) {
909 radix = 2;
910 }
911 int64_t base = radix;
912
913 UChar* p = buf;
914 if (len && (w < 0) && (radix == 10) && !raw) {
915 w = -w;
916 *p++ = kUMinus;
917 --len;
918 } else if (len && (w == 0)) {
919 *p++ = (UChar)raw ? 0 : asciiDigits[0];
920 --len;
921 }
922
923 while (len && (w != 0)) {
924 int64_t n = w / base;
925 int64_t m = n * base;
926 int32_t d = (int32_t)(w-m);
927 *p++ = (UChar)(raw ? d : asciiDigits[d]);
928 w = n;
929 --len;
930 }
931 if (len) {
932 *p = 0; // null terminate if room for caller convenience
933 }
934
935 len = (uint32_t)(p - buf);
936 if (*buf == kUMinus) {
937 ++buf;
938 }
939 while (--p > buf) {
940 UChar c = *p;
941 *p = *buf;
942 *buf = c;
943 ++buf;
944 }
945
946 return len;
947}
948
949
950U_NAMESPACE_END
951
952/* U_HAVE_RBNF */
953#endif
954