1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 1996-2015, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.dev.test.format;
10
11import java.math.BigInteger;
12import java.text.ParseException;
13import java.util.Locale;
14import java.util.Random;
15
16import org.junit.Test;
17import org.junit.runner.RunWith;
18import org.junit.runners.JUnit4;
19
20import com.ibm.icu.dev.test.TestFmwk;
21import com.ibm.icu.math.BigDecimal;
22import com.ibm.icu.text.DecimalFormat;
23import com.ibm.icu.text.DecimalFormatSymbols;
24import com.ibm.icu.text.DisplayContext;
25import com.ibm.icu.text.NumberFormat;
26import com.ibm.icu.text.RuleBasedNumberFormat;
27import com.ibm.icu.util.ULocale;
28
29/**
30 * This does not test lenient parse mode, since testing the default implementation
31 * introduces a dependency on collation.  See RbnfLenientScannerTest.
32 */
33@RunWith(JUnit4.class)
34public class RbnfTest extends TestFmwk {
35    static String fracRules =
36        "%main:\n" +
37        // this rule formats the number if it's 1 or more.  It formats
38        // the integral part using a DecimalFormat ("#,##0" puts
39        // thousands separators in the right places) and the fractional
40        // part using %%frac.  If there is no fractional part, it
41        // just shows the integral part.
42        "    x.0: <#,##0<[ >%%frac>];\n" +
43        // this rule formats the number if it's between 0 and 1.  It
44        // shows only the fractional part (0.5 shows up as "1/2," not
45        // "0 1/2")
46        "    0.x: >%%frac>;\n" +
47        // the fraction rule set.  This works the same way as the one in the
48        // preceding example: We multiply the fractional part of the number
49        // being formatted by each rule's base value and use the rule that
50        // produces the result closest to 0 (or the first rule that produces 0).
51        // Since we only provide rules for the numbers from 2 to 10, we know
52        // we'll get a fraction with a denominator between 2 and 10.
53        // "<0<" causes the numerator of the fraction to be formatted
54        // using numerals
55        "%%frac:\n" +
56        "    2: 1/2;\n" +
57        "    3: <0</3;\n" +
58        "    4: <0</4;\n" +
59        "    5: <0</5;\n" +
60        "    6: <0</6;\n" +
61        "    7: <0</7;\n" +
62        "    8: <0</8;\n" +
63        "    9: <0</9;\n" +
64        "   10: <0</10;\n";
65
66    @Test
67    public void TestCoverage() {
68        String durationInSecondsRules =
69                // main rule set for formatting with words
70                "%with-words:\n"
71                        // take care of singular and plural forms of "second"
72                        + "    0 seconds; 1 second; =0= seconds;\n"
73                        // use %%min to format values greater than 60 seconds
74                        + "    60/60: <%%min<[, >>];\n"
75                        // use %%hr to format values greater than 3,600 seconds
76                        // (the ">>>" below causes us to see the number of minutes
77                        // when when there are zero minutes)
78                        + "    3600/60: <%%hr<[, >>>];\n"
79                        // this rule set takes care of the singular and plural forms
80                        // of "minute"
81                        + "%%min:\n"
82                        + "    0 minutes; 1 minute; =0= minutes;\n"
83                        // this rule set takes care of the singular and plural forms
84                        // of "hour"
85                        + "%%hr:\n"
86                        + "    0 hours; 1 hour; =0= hours;\n"
87
88                        // main rule set for formatting in numerals
89                        + "%in-numerals:\n"
90                        // values below 60 seconds are shown with "sec."
91                        + "    =0= sec.;\n"
92                        // higher values are shown with colons: %%min-sec is used for
93                        // values below 3,600 seconds...
94                        + "    60: =%%min-sec=;\n"
95                        // ...and %%hr-min-sec is used for values of 3,600 seconds
96                        // and above
97                        + "    3600: =%%hr-min-sec=;\n"
98                        // this rule causes values of less than 10 minutes to show without
99                        // a leading zero
100                        + "%%min-sec:\n"
101                        + "    0: :=00=;\n"
102                        + "    60/60: <0<>>;\n"
103                        // this rule set is used for values of 3,600 or more.  Minutes are always
104                        // shown, and always shown with two digits
105                        + "%%hr-min-sec:\n"
106                        + "    0: :=00=;\n"
107                        + "    60/60: <00<>>;\n"
108                        + "    3600/60: <#,##0<:>>>;\n"
109                        // the lenient-parse rules allow several different characters to be used
110                        // as delimiters between hours, minutes, and seconds
111                        + "%%lenient-parse:\n"
112                        + "    & : = . = ' ' = -;\n";
113
114        // extra calls to boost coverage numbers
115        RuleBasedNumberFormat fmt0 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
116        RuleBasedNumberFormat fmt1 = (RuleBasedNumberFormat)fmt0.clone();
117        RuleBasedNumberFormat fmt2 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
118        if (!fmt0.equals(fmt0)) {
119            errln("self equality fails");
120        }
121        if (!fmt0.equals(fmt1)) {
122            errln("clone equality fails");
123        }
124        if (!fmt0.equals(fmt2)) {
125            errln("duplicate equality fails");
126        }
127        String str = fmt0.toString();
128        logln(str);
129
130        RuleBasedNumberFormat fmt3 =  new RuleBasedNumberFormat(durationInSecondsRules);
131
132        if (fmt0.equals(fmt3)) {
133            errln("nonequal fails");
134        }
135        if (!fmt3.equals(fmt3)) {
136            errln("self equal 2 fails");
137        }
138        str = fmt3.toString();
139        logln(str);
140
141        String[] names = fmt3.getRuleSetNames();
142
143        try {
144            fmt3.setDefaultRuleSet(null);
145            fmt3.setDefaultRuleSet("%%foo");
146            errln("sdrf %%foo didn't fail");
147        }
148        catch (Exception e) {
149            logln("Got the expected exception");
150        }
151
152        try {
153            fmt3.setDefaultRuleSet("%bogus");
154            errln("sdrf %bogus didn't fail");
155        }
156        catch (Exception e) {
157            logln("Got the expected exception");
158        }
159
160        try {
161            str = fmt3.format(2.3, names[0]);
162            logln(str);
163            str = fmt3.format(2.3, "%%foo");
164            errln("format double %%foo didn't fail");
165        }
166        catch (Exception e) {
167            logln("Got the expected exception");
168        }
169
170        try {
171            str = fmt3.format(123L, names[0]);
172            logln(str);
173            str = fmt3.format(123L, "%%foo");
174            errln("format double %%foo didn't fail");
175        }
176        catch (Exception e) {
177            logln("Got the expected exception");
178        }
179
180        RuleBasedNumberFormat fmt4 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
181        RuleBasedNumberFormat fmt5 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
182        str = fmt4.toString();
183        logln(str);
184        if (!fmt4.equals(fmt5)) {
185            errln("duplicate 2 equality failed");
186        }
187        str = fmt4.format(123L);
188        logln(str);
189        try {
190            Number num = fmt4.parse(str);
191            logln(num.toString());
192        }
193        catch (Exception e) {
194            errln("parse caught exception");
195        }
196
197        str = fmt4.format(.000123);
198        logln(str);
199        try {
200            Number num = fmt4.parse(str);
201            logln(num.toString());
202        }
203        catch (Exception e) {
204            errln("parse caught exception");
205        }
206
207        str = fmt4.format(456.000123);
208        logln(str);
209        try {
210            Number num = fmt4.parse(str);
211            logln(num.toString());
212        }
213        catch (Exception e) {
214            errln("parse caught exception");
215        }
216    }
217
218    @Test
219    public void TestUndefinedSpellout() {
220        Locale greek = new Locale("el", "", "");
221        RuleBasedNumberFormat[] formatters = {
222                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.SPELLOUT),
223                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.ORDINAL),
224                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.DURATION),
225        };
226
227        String[] data = {
228                "0",
229                "1",
230                "15",
231                "20",
232                "23",
233                "73",
234                "88",
235                "100",
236                "106",
237                "127",
238                "200",
239                "579",
240                "1,000",
241                "2,000",
242                "3,004",
243                "4,567",
244                "15,943",
245                "105,000",
246                "2,345,678",
247                "-36",
248                "-36.91215",
249                "234.56789"
250        };
251
252        NumberFormat decFormat = NumberFormat.getInstance(Locale.US);
253        for (int j = 0; j < formatters.length; ++j) {
254            com.ibm.icu.text.NumberFormat formatter = formatters[j];
255            logln("formatter[" + j + "]");
256            for (int i = 0; i < data.length; ++i) {
257                try {
258                    String result = formatter.format(decFormat.parse(data[i]));
259                    logln("[" + i + "] " + data[i] + " ==> " + result);
260                }
261                catch (Exception e) {
262                    errln("formatter[" + j + "], data[" + i + "] " + data[i] + " threw exception " + e.getMessage());
263                }
264            }
265        }
266    }
267
268    /**
269     * Perform a simple spot check on the English spellout rules
270     */
271    @Test
272    public void TestEnglishSpellout() {
273        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US,
274                RuleBasedNumberFormat.SPELLOUT);
275        String[][] testData = {
276                { "1", "one" },
277                { "15", "fifteen" },
278                { "20", "twenty" },
279                { "23", "twenty-three" },
280                { "73", "seventy-three" },
281                { "88", "eighty-eight" },
282                { "100", "one hundred" },
283                { "106", "one hundred six" },
284                { "127", "one hundred twenty-seven" },
285                { "200", "two hundred" },
286                { "579", "five hundred seventy-nine" },
287                { "1,000", "one thousand" },
288                { "2,000", "two thousand" },
289                { "3,004", "three thousand four" },
290                { "4,567", "four thousand five hundred sixty-seven" },
291                { "15,943", "fifteen thousand nine hundred forty-three" },
292                { "2,345,678", "two million three hundred forty-five "
293                        + "thousand six hundred seventy-eight" },
294                { "-36", "minus thirty-six" },
295                { "234.567", "two hundred thirty-four point five six seven" }
296        };
297
298        doTest(formatter, testData, true);
299    }
300
301    /**
302     * Perform a simple spot check on the English ordinal-abbreviation rules
303     */
304    @Test
305    public void TestOrdinalAbbreviations() {
306        RuleBasedNumberFormat formatter= new RuleBasedNumberFormat(Locale.US,
307                RuleBasedNumberFormat.ORDINAL);
308        String[][] testData = {
309                { "1", "1st" },
310                { "2", "2nd" },
311                { "3", "3rd" },
312                { "4", "4th" },
313                { "7", "7th" },
314                { "10", "10th" },
315                { "11", "11th" },
316                { "13", "13th" },
317                { "20", "20th" },
318                { "21", "21st" },
319                { "22", "22nd" },
320                { "23", "23rd" },
321                { "24", "24th" },
322                { "33", "33rd" },
323                { "102", "102nd" },
324                { "312", "312th" },
325                { "12,345", "12,345th" }
326        };
327
328        doTest(formatter, testData, false);
329    }
330
331    /**
332     * Perform a simple spot check on the duration-formatting rules
333     */
334    @Test
335    public void TestDurations() {
336        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US,
337                RuleBasedNumberFormat.DURATION);
338        String[][] testData = {
339                { "3,600", "1:00:00" },             //move me and I fail
340                { "0", "0 sec." },
341                { "1", "1 sec." },
342                { "24", "24 sec." },
343                { "60", "1:00" },
344                { "73", "1:13" },
345                { "145", "2:25" },
346                { "666", "11:06" },
347                //            { "3,600", "1:00:00" },
348                { "3,740", "1:02:20" },
349                { "10,293", "2:51:33" }
350        };
351
352        doTest(formatter, testData, true);
353    }
354
355    /**
356     * Perform a simple spot check on the Spanish spellout rules
357     */
358    @Test
359    public void TestSpanishSpellout() {
360        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("es", "es",
361                ""), RuleBasedNumberFormat.SPELLOUT);
362        String[][] testData = {
363                { "1", "uno" },
364                { "6", "seis" },
365                { "16", "diecis\u00e9is" },
366                { "20", "veinte" },
367                { "24", "veinticuatro" },
368                { "26", "veintis\u00e9is" },
369                { "73", "setenta y tres" },
370                { "88", "ochenta y ocho" },
371                { "100", "cien" },
372                { "106", "ciento seis" },
373                { "127", "ciento veintisiete" },
374                { "200", "doscientos" },
375                { "579", "quinientos setenta y nueve" },
376                { "1,000", "mil" },
377                { "2,000", "dos mil" },
378                { "3,004", "tres mil cuatro" },
379                { "4,567", "cuatro mil quinientos sesenta y siete" },
380                { "15,943", "quince mil novecientos cuarenta y tres" },
381                { "2,345,678", "dos millones trescientos cuarenta y cinco mil "
382                        + "seiscientos setenta y ocho"},
383                { "-36", "menos treinta y seis" },
384                { "234.567", "doscientos treinta y cuatro coma cinco seis siete" }
385        };
386
387        doTest(formatter, testData, true);
388    }
389
390    /**
391     * Perform a simple spot check on the French spellout rules
392     */
393    @Test
394    public void TestFrenchSpellout() {
395        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.FRANCE,
396                RuleBasedNumberFormat.SPELLOUT);
397        String[][] testData = {
398                { "1", "un" },
399                { "15", "quinze" },
400                { "20", "vingt" },
401                { "21", "vingt-et-un" },
402                { "23", "vingt-trois" },
403                { "62", "soixante-deux" },
404                { "70", "soixante-dix" },
405                { "71", "soixante-et-onze" },
406                { "73", "soixante-treize" },
407                { "80", "quatre-vingts" },
408                { "88", "quatre-vingt-huit" },
409                { "100", "cent" },
410                { "106", "cent six" },
411                { "127", "cent vingt-sept" },
412                { "200", "deux cents" },
413                { "579", "cinq cent soixante-dix-neuf" },
414                { "1,000", "mille" },
415                { "1,123", "mille cent vingt-trois" },
416                { "1,594", "mille cinq cent quatre-vingt-quatorze" },
417                { "2,000", "deux mille" },
418                { "3,004", "trois mille quatre" },
419                { "4,567", "quatre mille cinq cent soixante-sept" },
420                { "15,943", "quinze mille neuf cent quarante-trois" },
421                { "2,345,678", "deux millions trois cent quarante-cinq mille "
422                        + "six cent soixante-dix-huit" },
423                { "-36", "moins trente-six" },
424                { "234.567", "deux cent trente-quatre virgule cinq six sept" }
425        };
426
427        doTest(formatter, testData, true);
428    }
429
430    /**
431     * Perform a simple spot check on the Swiss French spellout rules
432     */
433    @Test
434    public void TestSwissFrenchSpellout() {
435        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("fr", "CH"),
436                RuleBasedNumberFormat.SPELLOUT);
437        String[][] testData = {
438                { "1", "un" },
439                { "15", "quinze" },
440                { "20", "vingt" },
441                { "21", "vingt-et-un" },
442                { "23", "vingt-trois" },
443                { "62", "soixante-deux" },
444                { "70", "septante" },
445                { "71", "septante-et-un" },
446                { "73", "septante-trois" },
447                { "80", "huitante" },
448                { "88", "huitante-huit" },
449                { "100", "cent" },
450                { "106", "cent six" },
451                { "127", "cent vingt-sept" },
452                { "200", "deux cents" },
453                { "579", "cinq cent septante-neuf" },
454                { "1,000", "mille" },
455                { "1,123", "mille cent vingt-trois" },
456                { "1,594", "mille cinq cent nonante-quatre" },
457                { "2,000", "deux mille" },
458                { "3,004", "trois mille quatre" },
459                { "4,567", "quatre mille cinq cent soixante-sept" },
460                { "15,943", "quinze mille neuf cent quarante-trois" },
461                { "2,345,678", "deux millions trois cent quarante-cinq mille "
462                        + "six cent septante-huit" },
463                { "-36", "moins trente-six" },
464                { "234.567", "deux cent trente-quatre virgule cinq six sept" }
465        };
466
467        doTest(formatter, testData, true);
468    }
469
470    /**
471     * Perform a simple spot check on the Italian spellout rules
472     */
473    @Test
474    public void TestItalianSpellout() {
475        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.ITALIAN,
476                RuleBasedNumberFormat.SPELLOUT);
477        String[][] testData = {
478                { "1", "uno" },
479                { "15", "quindici" },
480                { "20", "venti" },
481                { "23", "venti\u00ADtr\u00E9" },
482                { "73", "settanta\u00ADtr\u00E9" },
483                { "88", "ottant\u00ADotto" },
484                { "100", "cento" },
485                { "106", "cento\u00ADsei" },
486                { "108", "cent\u00ADotto" },
487                { "127", "cento\u00ADventi\u00ADsette" },
488                { "181", "cent\u00ADottant\u00ADuno" },
489                { "200", "due\u00ADcento" },
490                { "579", "cinque\u00ADcento\u00ADsettanta\u00ADnove" },
491                { "1,000", "mille" },
492                { "2,000", "due\u00ADmila" },
493                { "3,004", "tre\u00ADmila\u00ADquattro" },
494                { "4,567", "quattro\u00ADmila\u00ADcinque\u00ADcento\u00ADsessanta\u00ADsette" },
495                { "15,943", "quindici\u00ADmila\u00ADnove\u00ADcento\u00ADquaranta\u00ADtr\u00E9" },
496                { "-36", "meno trenta\u00ADsei" },
497                { "234.567", "due\u00ADcento\u00ADtrenta\u00ADquattro virgola cinque sei sette" }
498        };
499
500        doTest(formatter, testData, true);
501    }
502
503    /**
504     * Perform a simple spot check on the German spellout rules
505     */
506    @Test
507    public void TestGermanSpellout() {
508        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.GERMANY,
509                RuleBasedNumberFormat.SPELLOUT);
510        String[][] testData = {
511                { "1", "eins" },
512                { "15", "f\u00fcnfzehn" },
513                { "20", "zwanzig" },
514                { "23", "drei\u00ADund\u00ADzwanzig" },
515                { "73", "drei\u00ADund\u00ADsiebzig" },
516                { "88", "acht\u00ADund\u00ADachtzig" },
517                { "100", "ein\u00ADhundert" },
518                { "106", "ein\u00ADhundert\u00ADsechs" },
519                { "127", "ein\u00ADhundert\u00ADsieben\u00ADund\u00ADzwanzig" },
520                { "200", "zwei\u00ADhundert" },
521                { "579", "f\u00fcnf\u00ADhundert\u00ADneun\u00ADund\u00ADsiebzig" },
522                { "1,000", "ein\u00ADtausend" },
523                { "2,000", "zwei\u00ADtausend" },
524                { "3,004", "drei\u00ADtausend\u00ADvier" },
525                { "4,567", "vier\u00ADtausend\u00ADf\u00fcnf\u00ADhundert\u00ADsieben\u00ADund\u00ADsechzig" },
526                { "15,943", "f\u00fcnfzehn\u00ADtausend\u00ADneun\u00ADhundert\u00ADdrei\u00ADund\u00ADvierzig" },
527                { "2,345,678", "zwei Millionen drei\u00ADhundert\u00ADf\u00fcnf\u00ADund\u00ADvierzig\u00ADtausend\u00AD"
528                        + "sechs\u00ADhundert\u00ADacht\u00ADund\u00ADsiebzig" }
529        };
530
531        doTest(formatter, testData, true);
532    }
533
534    /**
535     * Perform a simple spot check on the Thai spellout rules
536     */
537    @Test
538    public void TestThaiSpellout() {
539        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("th", "TH"),
540                RuleBasedNumberFormat.SPELLOUT);
541        String[][] testData = {
542                { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
543                { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
544                { "10", "\u0e2a\u0e34\u0e1a" },
545                { "11", "\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
546                { "21", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
547                { "101", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e23\u0e49\u0e2d\u0e22\u200b\u0e2b\u0e19\u0e36\u0e48\u0e07" },
548                { "1.234", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e2d\u0e07\u0e2a\u0e32\u0e21\u0e2a\u0e35\u0e48" },
549                { "21.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
550                { "22.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e2d\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
551                { "23.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e32\u0e21\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
552                { "123.45", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e23\u0e49\u0e2d\u0e22\u200b\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e32\u0e21\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
553                { "12,345.678", "\u0E2B\u0E19\u0E36\u0E48\u0E07\u200b\u0E2B\u0E21\u0E37\u0E48\u0E19\u200b\u0E2A\u0E2D\u0E07\u200b\u0E1E\u0E31\u0E19\u200b\u0E2A\u0E32\u0E21\u200b\u0E23\u0E49\u0E2D\u0E22\u200b\u0E2A\u0E35\u0E48\u200b\u0E2A\u0E34\u0E1A\u200b\u0E2B\u0E49\u0E32\u200b\u0E08\u0E38\u0E14\u200b\u0E2B\u0E01\u0E40\u0E08\u0E47\u0E14\u0E41\u0E1B\u0E14" },
554        };
555
556        doTest(formatter, testData, true);
557    }
558
559    /**
560     * Perform a simple spot check on the ordinal spellout rules
561     */
562    @Test
563    public void TestPluralRules() {
564        String enRules = "%digits-ordinal:"
565                + "-x: −>>;"
566                + "0: =#,##0=$(ordinal,one{st}two{nd}few{rd}other{th})$;";
567        RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
568        String[][] enTestData = {
569                { "1", "1st" },
570                { "2", "2nd" },
571                { "3", "3rd" },
572                { "4", "4th" },
573                { "11", "11th" },
574                { "12", "12th" },
575                { "13", "13th" },
576                { "14", "14th" },
577                { "21", "21st" },
578                { "22", "22nd" },
579                { "23", "23rd" },
580                { "24", "24th" },
581        };
582
583        doTest(enFormatter, enTestData, true);
584
585        // This is trying to model the feminine form, but don't worry about the details too much.
586        // We're trying to test the plural rules.
587        String ruRules = "%spellout-numbering:"
588                + "-x: минус >>;"
589                + "x.x: [<< $(cardinal,one{целый}other{целых})$ ]>%%fractions-feminine>;"
590                + "0: ноль;"
591                + "1: один;"
592                + "2: два;"
593                + "3: три;"
594                + "4: четыре;"
595                + "5: пять;"
596                + "6: шесть;"
597                + "7: семь;"
598                + "8: восемь;"
599                + "9: девять;"
600                + "10: десять;"
601                + "11: одиннадцать;"
602                + "12: двенадцать;"
603                + "13: тринадцать;"
604                + "14: четырнадцать;"
605                + "15: пятнадцать;"
606                + "16: шестнадцать;"
607                + "17: семнадцать;"
608                + "18: восемнадцать;"
609                + "19: девятнадцать;"
610                + "20: двадцать[ >>];"
611                + "30: тридцать[ >>];"
612                + "40: сорок[ >>];"
613                + "50: пятьдесят[ >>];"
614                + "60: шестьдесят[ >>];"
615                + "70: семьдесят[ >>];"
616                + "80: восемьдесят[ >>];"
617                + "90: девяносто[ >>];"
618                + "100: сто[ >>];"
619                + "200: <<сти[ >>];"
620                + "300: <<ста[ >>];"
621                + "500: <<сот[ >>];"
622                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
623                + "1000000: << $(cardinal,one{миллион}few{миллионы}other{миллионов})$[ >>];"
624                + "%%fractions-feminine:"
625                + "10: <%spellout-numbering< $(cardinal,one{десятая}other{десятых})$;"
626                + "100: <%spellout-numbering< $(cardinal,one{сотая}other{сотых})$;";
627        RuleBasedNumberFormat ruFormatter = new RuleBasedNumberFormat(ruRules, new ULocale("ru"));
628        String[][] ruTestData = {
629                { "1", "один" },
630                { "100", "сто" },
631                { "125", "сто двадцать пять" },
632                { "399", "триста девяносто девять" },
633                { "1,000", "один тысяча" },
634                { "1,001", "один тысяча один" },
635                { "2,000", "два тысячи" },
636                { "2,001", "два тысячи один" },
637                { "2,002", "два тысячи два" },
638                { "3,333", "три тысячи триста тридцать три" },
639                { "5,000", "пять тысяч" },
640                { "11,000", "одиннадцать тысяч" },
641                { "21,000", "двадцать один тысяча" },
642                { "22,000", "двадцать два тысячи" },
643                { "25,001", "двадцать пять тысяч один" },
644                { "0.1", "один десятая" },
645                { "0.2", "два десятых" },
646                { "0.21", "двадцать один сотая" },
647                { "0.22", "двадцать два сотых" },
648                { "21.1", "двадцать один целый один десятая" },
649                { "22.2", "двадцать два целых два десятых" },
650        };
651
652        doTest(ruFormatter, ruTestData, true);
653
654        // Make sure there are no divide by 0 errors.
655        String result = new RuleBasedNumberFormat(ruRules, new ULocale("ru")).format(21000);
656        if (!"двадцать один тысяча".equals(result)) {
657            errln("Got " + result + " for 21000");
658        }
659    }
660
661    /**
662     * Perform a simple spot check on the parsing going into an infinite loop for alternate rules.
663     */
664    @Test
665    public void TestMultiplePluralRules() {
666        // This is trying to model the feminine form, but don't worry about the details too much.
667        // We're trying to test the plural rules where there are different prefixes.
668        String ruRules = "%spellout-cardinal-feminine-genitive:"
669                + "-x: минус >>;"
670                + "x.x: << запятая >>;"
671                + "0: ноля;"
672                + "1: одной;"
673                + "2: двух;"
674                + "3: трех;"
675                + "4: четырех;"
676                + "5: пяти;"
677                + "6: шести;"
678                + "7: семи;"
679                + "8: восьми;"
680                + "9: девяти;"
681                + "10: десяти;"
682                + "11: одиннадцати;"
683                + "12: двенадцати;"
684                + "13: тринадцати;"
685                + "14: четырнадцати;"
686                + "15: пятнадцати;"
687                + "16: шестнадцати;"
688                + "17: семнадцати;"
689                + "18: восемнадцати;"
690                + "19: девятнадцати;"
691                + "20: двадцати[ >>];"
692                + "30: тридцати[ >>];"
693                + "40: сорока[ >>];"
694                + "50: пятидесяти[ >>];"
695                + "60: шестидесяти[ >>];"
696                + "70: семидесяти[ >>];"
697                + "80: восемидесяти[ >>];"
698                + "90: девяноста[ >>];"
699                + "100: ста[ >>];"
700                + "200: <<сот[ >>];"
701                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
702                + "1000000: =#,##0=;"
703                + "%spellout-cardinal-feminine:"
704                + "-x: минус >>;"
705                + "x.x: << запятая >>;"
706                + "0: ноль;"
707                + "1: одна;"
708                + "2: две;"
709                + "3: три;"
710                + "4: четыре;"
711                + "5: пять;"
712                + "6: шесть;"
713                + "7: семь;"
714                + "8: восемь;"
715                + "9: девять;"
716                + "10: десять;"
717                + "11: одиннадцать;"
718                + "12: двенадцать;"
719                + "13: тринадцать;"
720                + "14: четырнадцать;"
721                + "15: пятнадцать;"
722                + "16: шестнадцать;"
723                + "17: семнадцать;"
724                + "18: восемнадцать;"
725                + "19: девятнадцать;"
726                + "20: двадцать[ >>];"
727                + "30: тридцать[ >>];"
728                + "40: сорок[ >>];"
729                + "50: пятьдесят[ >>];"
730                + "60: шестьдесят[ >>];"
731                + "70: семьдесят[ >>];"
732                + "80: восемьдесят[ >>];"
733                + "90: девяносто[ >>];"
734                + "100: сто[ >>];"
735                + "200: <<сти[ >>];"
736                + "300: <<ста[ >>];"
737                + "500: <<сот[ >>];"
738                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
739                + "1000000: =#,##0=;";
740        RuleBasedNumberFormat ruFormatter = new RuleBasedNumberFormat(ruRules, new ULocale("ru"));
741        try {
742            Number result;
743            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000))).doubleValue()) {
744                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
745            }
746            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000, "%spellout-cardinal-feminine-genitive"))).doubleValue()) {
747                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
748            }
749            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000, "%spellout-cardinal-feminine"))).doubleValue()) {
750                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
751            }
752        }
753        catch (ParseException e) {
754            errln(e.toString());
755        }
756    }
757
758    @Test
759    public void TestFractionalRuleSet() {
760        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(fracRules,
761                Locale.ENGLISH);
762
763        String[][] testData = {
764                { "0", "0" },
765                { "1", "1" },
766                { "10", "10" },
767                { ".1", "1/10" },
768                { ".11", "1/9" },
769                { ".125", "1/8" },
770                { ".1428", "1/7" },
771                { ".1667", "1/6" },
772                { ".2", "1/5" },
773                { ".25", "1/4" },
774                { ".333", "1/3" },
775                { ".5", "1/2" },
776                { "1.1", "1 1/10" },
777                { "2.11", "2 1/9" },
778                { "3.125", "3 1/8" },
779                { "4.1428", "4 1/7" },
780                { "5.1667", "5 1/6" },
781                { "6.2", "6 1/5" },
782                { "7.25", "7 1/4" },
783                { "8.333", "8 1/3" },
784                { "9.5", "9 1/2" },
785                { ".2222", "2/9" },
786                { ".4444", "4/9" },
787                { ".5555", "5/9" },
788                { "1.2856", "1 2/7" }
789        };
790        doTest(formatter, testData, false); // exact values aren't parsable from fractions
791    }
792
793    @Test
794    public void TestSwedishSpellout()
795    {
796        Locale locale = new Locale("sv", "", "");
797        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(locale,
798                RuleBasedNumberFormat.SPELLOUT);
799
800        String[][] testDataDefault = {
801                { "101", "ett\u00ADhundra\u00ADett" },
802                { "123", "ett\u00ADhundra\u00ADtjugo\u00ADtre" },
803                { "1,001", "et\u00ADtusen ett" },
804                { "1,100", "et\u00ADtusen ett\u00ADhundra" },
805                { "1,101", "et\u00ADtusen ett\u00ADhundra\u00ADett" },
806                { "1,234", "et\u00ADtusen tv\u00e5\u00ADhundra\u00ADtrettio\u00ADfyra" },
807                { "10,001", "tio\u00ADtusen ett" },
808                { "11,000", "elva\u00ADtusen" },
809                { "12,000", "tolv\u00ADtusen" },
810                { "20,000", "tjugo\u00ADtusen" },
811                { "21,000", "tjugo\u00ADet\u00ADtusen" },
812                { "21,001", "tjugo\u00ADet\u00ADtusen ett" },
813                { "200,000", "tv\u00e5\u00ADhundra\u00ADtusen" },
814                { "201,000", "tv\u00e5\u00ADhundra\u00ADet\u00ADtusen" },
815                { "200,200", "tv\u00e5\u00ADhundra\u00ADtusen tv\u00e5\u00ADhundra" },
816                { "2,002,000", "tv\u00e5 miljoner tv\u00e5\u00ADtusen" },
817                { "12,345,678", "tolv miljoner tre\u00ADhundra\u00ADfyrtio\u00ADfem\u00ADtusen sex\u00ADhundra\u00ADsjuttio\u00AD\u00e5tta" },
818                { "123,456.789", "ett\u00ADhundra\u00ADtjugo\u00ADtre\u00ADtusen fyra\u00ADhundra\u00ADfemtio\u00ADsex komma sju \u00e5tta nio" },
819                { "-12,345.678", "minus tolv\u00ADtusen tre\u00ADhundra\u00ADfyrtio\u00ADfem komma sex sju \u00e5tta" },
820        };
821
822        logln("testing default rules");
823        doTest(formatter, testDataDefault, true);
824
825        String[][] testDataNeutrum = {
826                { "101", "ett\u00adhundra\u00adett" },
827                { "1,001", "et\u00adtusen ett" },
828                { "1,101", "et\u00adtusen ett\u00adhundra\u00adett" },
829                { "10,001", "tio\u00adtusen ett" },
830                { "21,001", "tjugo\u00adet\u00adtusen ett" }
831        };
832
833        formatter.setDefaultRuleSet("%spellout-cardinal-neuter");
834        logln("testing neutrum rules");
835        doTest(formatter, testDataNeutrum, true);
836
837        String[][] testDataYear = {
838                { "101", "ett\u00adhundra\u00adett" },
839                { "900", "nio\u00adhundra" },
840                { "1,001", "et\u00adtusen ett" },
841                { "1,100", "elva\u00adhundra" },
842                { "1,101", "elva\u00adhundra\u00adett" },
843                { "1,234", "tolv\u00adhundra\u00adtrettio\u00adfyra" },
844                { "2,001", "tjugo\u00adhundra\u00adett" },
845                { "10,001", "tio\u00adtusen ett" }
846        };
847
848        formatter.setDefaultRuleSet("%spellout-numbering-year");
849        logln("testing year rules");
850        doTest(formatter, testDataYear, true);
851    }
852
853    @Test
854    public void TestBigNumbers() {
855        BigInteger bigI = new BigInteger("1234567890", 10);
856        StringBuffer buf = new StringBuffer();
857        RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
858        fmt.format(bigI, buf, null);
859        logln("big int: " + buf.toString());
860
861        buf.setLength(0);
862        java.math.BigDecimal bigD = new java.math.BigDecimal(bigI);
863        fmt.format(bigD, buf, null);
864        logln("big dec: " + buf.toString());
865    }
866
867    @Test
868    public void TestTrailingSemicolon() {
869        String thaiRules =
870            "%default:\n" +
871            "  -x: \u0e25\u0e1a>>;\n" +
872            "  x.x: <<\u0e08\u0e38\u0e14>>>;\n" +
873            "  \u0e28\u0e39\u0e19\u0e22\u0e4c; \u0e2b\u0e19\u0e36\u0e48\u0e07; \u0e2a\u0e2d\u0e07; \u0e2a\u0e32\u0e21;\n" +
874            "  \u0e2a\u0e35\u0e48; \u0e2b\u0e49\u0e32; \u0e2b\u0e01; \u0e40\u0e08\u0e47\u0e14; \u0e41\u0e1b\u0e14;\n" +
875            "  \u0e40\u0e01\u0e49\u0e32; \u0e2a\u0e34\u0e1a; \u0e2a\u0e34\u0e1a\u0e40\u0e2d\u0e47\u0e14;\n" +
876            "  \u0e2a\u0e34\u0e1a\u0e2a\u0e2d\u0e07; \u0e2a\u0e34\u0e1a\u0e2a\u0e32\u0e21;\n" +
877            "  \u0e2a\u0e34\u0e1a\u0e2a\u0e35\u0e48; \u0e2a\u0e34\u0e1a\u0e2b\u0e49\u0e32;\n" +
878            "  \u0e2a\u0e34\u0e1a\u0e2b\u0e01; \u0e2a\u0e34\u0e1a\u0e40\u0e08\u0e47\u0e14;\n" +
879            "  \u0e2a\u0e34\u0e1a\u0e41\u0e1b\u0e14; \u0e2a\u0e34\u0e1a\u0e40\u0e01\u0e49\u0e32;\n" +
880            "  20: \u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
881            "  30: \u0e2a\u0e32\u0e21\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
882            "  40: \u0e2a\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
883            "  50: \u0e2b\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
884            "  60: \u0e2b\u0e01\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
885            "  70: \u0e40\u0e08\u0e47\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
886            "  80: \u0e41\u0e1b\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
887            "  90: \u0e40\u0e01\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
888            "  100: <<\u0e23\u0e49\u0e2d\u0e22[>>];\n" +
889            "  1000: <<\u0e1e\u0e31\u0e19[>>];\n" +
890            "  10000: <<\u0e2b\u0e21\u0e37\u0e48\u0e19[>>];\n" +
891            "  100000: <<\u0e41\u0e2a\u0e19[>>];\n" +
892            "  1,000,000: <<\u0e25\u0e49\u0e32\u0e19[>>];\n" +
893            "  1,000,000,000: <<\u0e1e\u0e31\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
894            "  1,000,000,000,000: <<\u0e25\u0e49\u0e32\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
895            "  1,000,000,000,000,000: =#,##0=;\n" +
896            "%%alt-ones:\n" +
897            "  \u0e28\u0e39\u0e19\u0e22\u0e4c;\n" +
898            "  \u0e40\u0e2d\u0e47\u0e14;\n" +
899            "  =%default=;\n ; ;; ";
900
901        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(thaiRules, new Locale("th", "TH", ""));
902
903        String[][] testData = {
904                { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
905                { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
906                { "123.45", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e23\u0e49\u0e2d\u0e22\u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a\u0e2a\u0e32\u0e21\u0e08\u0e38\u0e14\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" }
907        };
908
909        doTest(formatter, testData, true);
910    }
911
912    @Test
913    public void TestSmallValues() {
914        String[][] testData = {
915                { "0.001", "zero point zero zero one" },
916                { "0.0001", "zero point zero zero zero one" },
917                { "0.00001", "zero point zero zero zero zero one" },
918                { "0.000001", "zero point zero zero zero zero zero one" },
919                { "0.0000001", "zero point zero zero zero zero zero zero one" },
920                { "0.00000001", "zero point zero zero zero zero zero zero zero one" },
921                { "0.000000001", "zero point zero zero zero zero zero zero zero zero one" },
922                { "0.0000000001", "zero point zero zero zero zero zero zero zero zero zero one" },
923                { "0.00000000001", "zero point zero zero zero zero zero zero zero zero zero zero one" },
924                { "0.000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero one" },
925                { "0.0000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero one" },
926                { "0.00000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
927                { "0.000000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
928                { "10,000,000.001", "ten million point zero zero one" },
929                { "10,000,000.0001", "ten million point zero zero zero one" },
930                { "10,000,000.00001", "ten million point zero zero zero zero one" },
931                { "10,000,000.000001", "ten million point zero zero zero zero zero one" },
932                { "10,000,000.0000001", "ten million point zero zero zero zero zero zero one" },
933                { "10,000,000.00000001", "ten million point zero zero zero zero zero zero zero one" },
934                { "10,000,000.000000002", "ten million point zero zero zero zero zero zero zero zero two" },
935                { "10,000,000", "ten million" },
936                { "1,234,567,890.0987654", "one billion two hundred thirty-four million five hundred sixty-seven thousand eight hundred ninety point zero nine eight seven six five four" },
937                { "123,456,789.9876543", "one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine point nine eight seven six five four three" },
938                { "12,345,678.87654321", "twelve million three hundred forty-five thousand six hundred seventy-eight point eight seven six five four three two one" },
939                { "1,234,567.7654321", "one million two hundred thirty-four thousand five hundred sixty-seven point seven six five four three two one" },
940                { "123,456.654321", "one hundred twenty-three thousand four hundred fifty-six point six five four three two one" },
941                { "12,345.54321", "twelve thousand three hundred forty-five point five four three two one" },
942                { "1,234.4321", "one thousand two hundred thirty-four point four three two one" },
943                { "123.321", "one hundred twenty-three point three two one" },
944                { "0.0000000011754944", "zero point zero zero zero zero zero zero zero zero one one seven five four nine four four" },
945                { "0.000001175494351", "zero point zero zero zero zero zero one one seven five four nine four three five one" },
946        };
947
948        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US, RuleBasedNumberFormat.SPELLOUT);
949        doTest(formatter, testData, true);
950    }
951
952    @Test
953    public void TestRuleSetDisplayName() {
954        /*
955         * Spellout rules for U.K. English.
956         * This was borrowed from the rule sets for TestRuleSetDisplayName()
957         */
958        final String ukEnglish =
959                "%simplified:\n"
960                        + "    -x: minus >>;\n"
961                        + "    x.x: << point >>;\n"
962                        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
963                        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
964                        + "        seventeen; eighteen; nineteen;\n"
965                        + "    20: twenty[->>];\n"
966                        + "    30: thirty[->>];\n"
967                        + "    40: forty[->>];\n"
968                        + "    50: fifty[->>];\n"
969                        + "    60: sixty[->>];\n"
970                        + "    70: seventy[->>];\n"
971                        + "    80: eighty[->>];\n"
972                        + "    90: ninety[->>];\n"
973                        + "    100: << hundred[ >>];\n"
974                        + "    1000: << thousand[ >>];\n"
975                        + "    1,000,000: << million[ >>];\n"
976                        + "    1,000,000,000,000: << billion[ >>];\n"
977                        + "    1,000,000,000,000,000: =#,##0=;\n"
978                        + "%alt-teens:\n"
979                        + "    =%simplified=;\n"
980                        + "    1000>: <%%alt-hundreds<[ >>];\n"
981                        + "    10,000: =%simplified=;\n"
982                        + "    1,000,000: << million[ >%simplified>];\n"
983                        + "    1,000,000,000,000: << billion[ >%simplified>];\n"
984                        + "    1,000,000,000,000,000: =#,##0=;\n"
985                        + "%%alt-hundreds:\n"
986                        + "    0: SHOULD NEVER GET HERE!;\n"
987                        + "    10: <%simplified< thousand;\n"
988                        + "    11: =%simplified= hundred>%%empty>;\n"
989                        + "%%empty:\n"
990                        + "    0:;"
991                        + "%ordinal:\n"
992                        + "    zeroth; first; second; third; fourth; fifth; sixth; seventh;\n"
993                        + "        eighth; ninth;\n"
994                        + "    tenth; eleventh; twelfth; thirteenth; fourteenth;\n"
995                        + "        fifteenth; sixteenth; seventeenth; eighteenth;\n"
996                        + "        nineteenth;\n"
997                        + "    twentieth; twenty->>;\n"
998                        + "    30: thirtieth; thirty->>;\n"
999                        + "    40: fortieth; forty->>;\n"
1000                        + "    50: fiftieth; fifty->>;\n"
1001                        + "    60: sixtieth; sixty->>;\n"
1002                        + "    70: seventieth; seventy->>;\n"
1003                        + "    80: eightieth; eighty->>;\n"
1004                        + "    90: ninetieth; ninety->>;\n"
1005                        + "    100: <%simplified< hundredth; <%simplified< hundred >>;\n"
1006                        + "    1000: <%simplified< thousandth; <%simplified< thousand >>;\n"
1007                        + "    1,000,000: <%simplified< millionth; <%simplified< million >>;\n"
1008                        + "    1,000,000,000,000: <%simplified< billionth;\n"
1009                        + "        <%simplified< billion >>;\n"
1010                        + "    1,000,000,000,000,000: =#,##0=;"
1011                        + "%default:\n"
1012                        + "    -x: minus >>;\n"
1013                        + "    x.x: << point >>;\n"
1014                        + "    =%simplified=;\n"
1015                        + "    100: << hundred[ >%%and>];\n"
1016                        + "    1000: << thousand[ >%%and>];\n"
1017                        + "    100,000>>: << thousand[>%%commas>];\n"
1018                        + "    1,000,000: << million[>%%commas>];\n"
1019                        + "    1,000,000,000,000: << billion[>%%commas>];\n"
1020                        + "    1,000,000,000,000,000: =#,##0=;\n"
1021                        + "%%and:\n"
1022                        + "    and =%default=;\n"
1023                        + "    100: =%default=;\n"
1024                        + "%%commas:\n"
1025                        + "    ' and =%default=;\n"
1026                        + "    100: , =%default=;\n"
1027                        + "    1000: , <%default< thousand, >%default>;\n"
1028                        + "    1,000,000: , =%default=;"
1029                        + "%%lenient-parse:\n"
1030                        + "    & ' ' , ',' ;\n";
1031        ULocale.setDefault(ULocale.US);
1032        String[][] localizations = new String[][] {
1033            /* public rule sets*/
1034                {"%simplified", "%default", "%ordinal"},
1035            /* display names in "en_US" locale*/
1036                {"en_US", "Simplified", "Default", "Ordinal"},
1037            /* display names in "zh_Hans" locale*/
1038                {"zh_Hans", "\u7B80\u5316", "\u7F3A\u7701",  "\u5E8F\u5217"},
1039            /* display names in a fake locale*/
1040                {"foo_Bar_BAZ", "Simplified", "Default", "Ordinal"}
1041        };
1042
1043        //Construct RuleBasedNumberFormat by rule sets and localizations list
1044        RuleBasedNumberFormat formatter
1045                = new RuleBasedNumberFormat(ukEnglish, localizations, ULocale.US);
1046        RuleBasedNumberFormat f2= new RuleBasedNumberFormat(ukEnglish, localizations);
1047        assertTrue("Check the two formatters' equality", formatter.equals(f2));
1048
1049        //get displayName by name
1050        String[] ruleSetNames = formatter.getRuleSetNames();
1051        for (int i=0; i<ruleSetNames.length; i++) {
1052            logln("Rule set name: " + ruleSetNames[i]);
1053            String RSName_defLoc = formatter.getRuleSetDisplayName(ruleSetNames[i]);
1054            assertEquals("Display name in default locale", localizations[1][i+1], RSName_defLoc);
1055            String RSName_loc = formatter.getRuleSetDisplayName(ruleSetNames[i], ULocale.CHINA);
1056            assertEquals("Display name in Chinese", localizations[2][i+1], RSName_loc);
1057        }
1058
1059        // getDefaultRuleSetName
1060        String defaultRS = formatter.getDefaultRuleSetName();
1061        //you know that the default rule set is %simplified according to rule sets string ukEnglish
1062        assertEquals("getDefaultRuleSetName", "%simplified", defaultRS);
1063
1064        //get locales of localizations
1065        ULocale[] locales = formatter.getRuleSetDisplayNameLocales();
1066        for (int i=0; i<locales.length; i++) {
1067            logln(locales[i].getName());
1068        }
1069
1070        //get displayNames
1071        String[] RSNames_defLoc = formatter.getRuleSetDisplayNames();
1072        for (int i=0; i<RSNames_defLoc.length; i++) {
1073            assertEquals("getRuleSetDisplayNames in default locale", localizations[1][i+1], RSNames_defLoc[i]);
1074        }
1075
1076        String[] RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.UK);
1077        for (int i=0; i<RSNames_loc.length; i++) {
1078            assertEquals("getRuleSetDisplayNames in English", localizations[1][i+1], RSNames_loc[i]);
1079        }
1080
1081        RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.CHINA);
1082        for (int i=0; i<RSNames_loc.length; i++) {
1083            assertEquals("getRuleSetDisplayNames in Chinese", localizations[2][i+1], RSNames_loc[i]);
1084        }
1085
1086        RSNames_loc = formatter.getRuleSetDisplayNames(new ULocale("foo_Bar_BAZ"));
1087        for (int i=0; i<RSNames_loc.length; i++) {
1088            assertEquals("getRuleSetDisplayNames in fake locale", localizations[3][i+1], RSNames_loc[i]);
1089        }
1090    }
1091
1092    @Test
1093    public void TestAllLocales() {
1094        StringBuilder errors = new StringBuilder();
1095        String[] names = {
1096                " (spellout) ",
1097                " (ordinal) "
1098                //" (duration) " // English only
1099        };
1100        double[] numbers = {45.678, 1, 2, 10, 11, 100, 110, 200, 1000, 1111, -1111};
1101        int count = numbers.length;
1102        Random r = (count <= numbers.length ? null : createRandom());
1103
1104        for (ULocale loc : NumberFormat.getAvailableULocales()) {
1105            for (int j = 0; j < names.length; ++j) {
1106                RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(loc, j+1);
1107                if (!loc.equals(fmt.getLocale(ULocale.ACTUAL_LOCALE))) {
1108                    // Skip the redundancy
1109                    break;
1110                }
1111
1112                for (int c = 0; c < count; c++) {
1113                    double n;
1114                    if (c < numbers.length) {
1115                        n = numbers[c];
1116                    } else {
1117                        n = (r.nextInt(10000) - 3000) / 16d;
1118                    }
1119
1120                    String s = fmt.format(n);
1121                    if (isVerbose()) {
1122                        logln(loc.getName() + names[j] + "success format: " + n + " -> " + s);
1123                    }
1124
1125                    try {
1126                        // RBNF parse is extremely slow when lenient option is enabled.
1127                        // non-lenient parse
1128                        fmt.setLenientParseMode(false);
1129                        Number num = fmt.parse(s);
1130                        if (isVerbose()) {
1131                            logln(loc.getName() + names[j] + "success parse: " + s + " -> " + num);
1132                        }
1133                        if (j != 0) {
1134                            // TODO: Fix the ordinal rules.
1135                            continue;
1136                        }
1137                        if (n != num.doubleValue()) {
1138                            errors.append("\n" + loc + names[j] + "got " + num + " expected " + n);
1139                        }
1140                    } catch (ParseException pe) {
1141                        String msg = loc.getName() + names[j] + "ERROR:" + pe.getMessage();
1142                        logln(msg);
1143                        errors.append("\n" + msg);
1144                    }
1145                }
1146            }
1147        }
1148        if (errors.length() > 0) {
1149            errln(errors.toString());
1150        }
1151    }
1152
1153    void doTest(RuleBasedNumberFormat formatter, String[][] testData,
1154                boolean testParsing) {
1155        //        NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
1156        NumberFormat decFmt = new DecimalFormat("#,###.################");
1157        try {
1158            for (int i = 0; i < testData.length; i++) {
1159                String number = testData[i][0];
1160                String expectedWords = testData[i][1];
1161                if (isVerbose()) {
1162                    logln("test[" + i + "] number: " + number + " target: " + expectedWords);
1163                }
1164                Number num = decFmt.parse(number);
1165                String actualWords = formatter.format(num);
1166
1167                if (!actualWords.equals(expectedWords)) {
1168                    errln("Spot check format failed: for " + number + ", expected\n    "
1169                            + expectedWords + ", but got\n    " +
1170                            actualWords);
1171                }
1172                else if (testParsing) {
1173                    String actualNumber = decFmt.format(formatter
1174                            .parse(actualWords));
1175
1176                    if (!actualNumber.equals(number)) {
1177                        errln("Spot check parse failed: for " + actualWords +
1178                                ", expected " + number + ", but got " +
1179                                actualNumber);
1180                    }
1181                }
1182            }
1183        }
1184        catch (Throwable e) {
1185            e.printStackTrace();
1186            errln("Test failed with exception: " + e.toString());
1187        }
1188    }
1189
1190    /* Tests the method
1191     *      public boolean equals(Object that)
1192     */
1193    @Test
1194    public void TestEquals(){
1195        // Tests when "if (!(that instanceof RuleBasedNumberFormat))" is true
1196        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1197        if (rbnf.equals("dummy") ||
1198                rbnf.equals(new Character('a')) ||
1199                rbnf.equals(new Object()) ||
1200                rbnf.equals(-1) ||
1201                rbnf.equals(0) ||
1202                rbnf.equals(1) ||
1203                rbnf.equals(-1.0) ||
1204                rbnf.equals(0.0) ||
1205                rbnf.equals(1.0))
1206        {
1207            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1208                    "be false for an invalid object.");
1209        }
1210
1211        // Tests when
1212        // "if (!locale.equals(that2.locale) || lenientParse != that2.lenientParse)"
1213        // is true
1214        RuleBasedNumberFormat rbnf1 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1215        RuleBasedNumberFormat rbnf2 = new RuleBasedNumberFormat("dummy", new Locale("jp"));
1216        RuleBasedNumberFormat rbnf3 = new RuleBasedNumberFormat("dummy", new Locale("sp"));
1217        RuleBasedNumberFormat rbnf4 = new RuleBasedNumberFormat("dummy", new Locale("fr"));
1218
1219        if(rbnf1.equals(rbnf2) || rbnf1.equals(rbnf3) ||
1220                rbnf1.equals(rbnf4) || rbnf2.equals(rbnf3) ||
1221                rbnf2.equals(rbnf4) || rbnf3.equals(rbnf4)){
1222            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1223                    "be false for an invalid object.");
1224        }
1225
1226        if(!rbnf1.equals(rbnf1)){
1227            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1228                    "be false for an invalid object.");
1229        }
1230
1231        if(!rbnf2.equals(rbnf2)){
1232            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1233                    "be false for an invalid object.");
1234        }
1235
1236        if(!rbnf3.equals(rbnf3)){
1237            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1238                    "be false for an invalid object.");
1239        }
1240
1241        if(!rbnf4.equals(rbnf4)){
1242            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1243                    "be false for an invalid object.");
1244        }
1245
1246        RuleBasedNumberFormat rbnf5 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1247        RuleBasedNumberFormat rbnf6 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1248
1249        if(!rbnf5.equals(rbnf6)){
1250            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1251                    "be false for an invalid object.");
1252        }
1253        rbnf6.setLenientParseMode(true);
1254
1255        if(rbnf5.equals(rbnf6)){
1256            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1257                    "be false for an invalid object.");
1258        }
1259
1260        // Tests when "if (!ruleSets[i].equals(that2.ruleSets[i]))" is true
1261        RuleBasedNumberFormat rbnf7 = new RuleBasedNumberFormat("not_dummy", new Locale("en"));
1262        if(rbnf5.equals(rbnf7)){
1263            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1264                    "be false for an invalid object.");
1265        }
1266    }
1267
1268    /* Tests the method
1269     *      public ULocale[] getRuleSetDisplayNameLocales()
1270     */
1271    @Test
1272    public void TestGetRuleDisplayNameLocales(){
1273        // Tests when "if (ruleSetDisplayNames != null" is false
1274        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1275        rbnf.getRuleSetDisplayNameLocales();
1276        if(rbnf.getRuleSetDisplayNameLocales() != null){
1277            errln("RuleBasedNumberFormat.getRuleDisplayNameLocales() was suppose to " +
1278                    "return null.");
1279        }
1280    }
1281
1282    /* Tests the method
1283     *      private String[] getNameListForLocale(ULocale loc)
1284     *      public String[] getRuleSetDisplayNames(ULocale loc)
1285     */
1286    @Test
1287    public void TestGetNameListForLocale(){
1288        // Tests when "if (names != null)" is false and
1289        //  "if (loc != null && ruleSetDisplayNames != null)" is false
1290        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1291        rbnf.getRuleSetDisplayNames(null);
1292        try{
1293            rbnf.getRuleSetDisplayNames(null);
1294        } catch(Exception e){
1295            errln("RuleBasedNumberFormat.getRuleSetDisplayNames(ULocale loc) " +
1296                    "was not suppose to have an exception.");
1297        }
1298    }
1299
1300    /* Tests the method
1301     *      public String getRuleSetDisplayName(String ruleSetName, ULocale loc)
1302     */
1303    @Test
1304    public void TestGetRulesSetDisplayName(){
1305        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1306        //rbnf.getRuleSetDisplayName("dummy", new ULocale("en_US"));
1307
1308        // Tests when "if (names != null) " is true
1309
1310        // Tests when the method throws an exception
1311        try{
1312            rbnf.getRuleSetDisplayName("", new ULocale("en_US"));
1313            errln("RuleBasedNumberFormat.getRuleSetDisplayName(String ruleSetName, ULocale loc) " +
1314                    "was suppose to have an exception.");
1315        } catch(Exception ignored){}
1316
1317        try{
1318            rbnf.getRuleSetDisplayName("dummy", new ULocale("en_US"));
1319            errln("RuleBasedNumberFormat.getRuleSetDisplayName(String ruleSetName, ULocale loc) " +
1320                    "was suppose to have an exception.");
1321        } catch(Exception ignored){}
1322    }
1323
1324    /* Test the method
1325     *      public void process(StringBuffer buf, NFRuleSet ruleSet)
1326     */
1327    @Test
1328    public void TestChineseProcess(){
1329        String ruleWithChinese =
1330            "%simplified:\n"
1331            + "    -x: minus >>;\n"
1332            + "    x.x: << point >>;\n"
1333            + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
1334            + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
1335            + "        seventeen; eighteen; nineteen;\n"
1336            + "    20: twenty[->>];\n"
1337            + "    30: thirty[->>];\n"
1338            + "    40: forty[->>];\n"
1339            + "    50: fifty[->>];\n"
1340            + "    60: sixty[->>];\n"
1341            + "    70: seventy[->>];\n"
1342            + "    80: eighty[->>];\n"
1343            + "    90: ninety[->>];\n"
1344            + "    100: << hundred[ >>];\n"
1345            + "    1000: << thousand[ >>];\n"
1346            + "    1,000,000: << million[ >>];\n"
1347            + "    1,000,000,000,000: << billion[ >>];\n"
1348            + "    1,000,000,000,000,000: =#,##0=;\n"
1349            + "%alt-teens:\n"
1350            + "    =%simplified=;\n"
1351            + "    1000>: <%%alt-hundreds<[ >>];\n"
1352            + "    10,000: =%simplified=;\n"
1353            + "    1,000,000: << million[ >%simplified>];\n"
1354            + "    1,000,000,000,000: << billion[ >%simplified>];\n"
1355            + "    1,000,000,000,000,000: =#,##0=;\n"
1356            + "%%alt-hundreds:\n"
1357            + "    0: SHOULD NEVER GET HERE!;\n"
1358            + "    10: <%simplified< thousand;\n"
1359            + "    11: =%simplified= hundred>%%empty>;\n"
1360            + "%%empty:\n"
1361            + "    0:;"
1362            + "%accounting:\n"
1363            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1364            + "        \u842c; \u842c;\n"
1365            + "    \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1366            + "        \u842c; \u842c; \u842c; \u842c;\n"
1367            + "        \u842c;\n"
1368            + "    twentieth; \u96f6|>>;\n"
1369            + "    30: \u96f6; \u96f6|>>;\n"
1370            + "    40: \u96f6; \u96f6|>>;\n"
1371            + "    50: \u96f6; \u96f6|>>;\n"
1372            + "    60: \u96f6; \u96f6|>>;\n"
1373            + "    70: \u96f6; \u96f6|>>;\n"
1374            + "    80: \u96f6; \u96f6|>>;\n"
1375            + "    90: \u96f6; \u96f6|>>;\n"
1376            + "    100: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1377            + "    1000: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1378            + "    1,000,000: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1379            + "    1,000,000,000,000: <%simplified< \u96f6;\n"
1380            + "        <%simplified< \u96f6 >>;\n"
1381            + "    1,000,000,000,000,000: =#,##0=;"
1382            + "%default:\n"
1383            + "    -x: minus >>;\n"
1384            + "    x.x: << point >>;\n"
1385            + "    =%simplified=;\n"
1386            + "    100: << hundred[ >%%and>];\n"
1387            + "    1000: << thousand[ >%%and>];\n"
1388            + "    100,000>>: << thousand[>%%commas>];\n"
1389            + "    1,000,000: << million[>%%commas>];\n"
1390            + "    1,000,000,000,000: << billion[>%%commas>];\n"
1391            + "    1,000,000,000,000,000: =#,##0=;\n"
1392            + "%%and:\n"
1393            + "    and =%default=;\n"
1394            + "    100: =%default=;\n"
1395            + "%%commas:\n"
1396            + "    ' and =%default=;\n"
1397            + "    100: , =%default=;\n"
1398            + "    1000: , <%default< thousand, >%default>;\n"
1399            + "    1,000,000: , =%default=;"
1400            + "%traditional:\n"
1401            + "    -x: \u3007| >>;\n"
1402            + "    x.x: << \u9ede >>;\n"
1403            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1404            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1405            + "        \u842c; \u842c; \u842c;\n"
1406            + "    20: \u842c[->>];\n"
1407            + "    30: \u842c[->>];\n"
1408            + "    40: \u842c[->>];\n"
1409            + "    50: \u842c[->>];\n"
1410            + "    60: \u842c[->>];\n"
1411            + "    70: \u842c[->>];\n"
1412            + "    80: \u842c[->>];\n"
1413            + "    90: \u842c[->>];\n"
1414            + "    100: << \u842c[ >>];\n"
1415            + "    1000: << \u842c[ >>];\n"
1416            + "    1,000,000: << \u842c[ >>];\n"
1417            + "    1,000,000,000,000: << \u842c[ >>];\n"
1418            + "    1,000,000,000,000,000: =#,##0=;\n"
1419            + "%time:\n"
1420            + "    =0= sec.;\n"
1421            + "    60: =%%min-sec=;\n"
1422            + "    3600: =%%hr-min-sec=;\n"
1423            + "%%min-sec:\n"
1424            + "    0: *=00=;\n"
1425            + "    60/60: <0<>>;\n"
1426            + "%%hr-min-sec:\n"
1427            + "    0: *=00=;\n"
1428            + "    60/60: <00<>>;\n"
1429            + "    3600/60: <#,##0<:>>>;\n"
1430            + "%%post-process:com.ibm.icu.text.RBNFChinesePostProcessor\n";
1431
1432        RuleBasedNumberFormat rbnf =  new RuleBasedNumberFormat(ruleWithChinese, ULocale.CHINESE);
1433        String[] ruleNames = rbnf.getRuleSetNames();
1434        try{
1435            // Test with "null" rules
1436            rbnf.format(0.0, null);
1437            errln("This was suppose to return an exception for a null format");
1438        } catch(Exception e){}
1439        for(int i=0; i<ruleNames.length; i++){
1440            try{
1441                rbnf.format(-123450.6789,ruleNames[i]);
1442            } catch(Exception e){
1443                errln("RBNFChinesePostProcessor was not suppose to return an exception " +
1444                        "when being formatted with parameters 0.0 and " + ruleNames[i]);
1445            }
1446        }
1447    }
1448
1449    @Test
1450    public void TestSetDecimalFormatSymbols() {
1451        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(Locale.ENGLISH, RuleBasedNumberFormat.ORDINAL);
1452
1453        DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.ENGLISH);
1454
1455        double number = 1001;
1456
1457        String[] expected = { "1,001st", "1&001st" };
1458
1459        String result = rbnf.format(number);
1460        if (!result.equals(expected[0])) {
1461            errln("Format Error - Got: " + result + " Expected: " + expected[0]);
1462        }
1463
1464        /* Set new symbol for testing */
1465        dfs.setGroupingSeparator('&');
1466        rbnf.setDecimalFormatSymbols(dfs);
1467
1468        result = rbnf.format(number);
1469        if (!result.equals(expected[1])) {
1470            errln("Format Error - Got: " + result + " Expected: " + expected[1]);
1471        }
1472    }
1473
1474    @Test
1475    public void TestContext() {
1476        class TextContextItem {
1477            public String locale;
1478            public int format;
1479            public DisplayContext context;
1480            public double value;
1481            public String expectedResult;
1482            // Simple constructor
1483            public TextContextItem(String loc, int fmt, DisplayContext ctxt, double val, String expRes) {
1484                locale = loc;
1485                format = fmt;
1486                context = ctxt;
1487                value = val;
1488                expectedResult = expRes;
1489            }
1490        }
1491        final TextContextItem[] items = {
1492                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1493                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "Ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1494                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1495                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE,            123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1496                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    123.45, "one hundred twenty-three point four five" ),
1497                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "One hundred twenty-three point four five" ),
1498                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       123.45, "One hundred twenty-three point four five" ),
1499                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE,            123.45, "One hundred twenty-three point four five" ),
1500        };
1501        for (TextContextItem item: items) {
1502            ULocale locale = new ULocale(item.locale);
1503            RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(locale, item.format);
1504            rbnf.setContext(item.context);
1505            String result = rbnf.format(item.value, rbnf.getDefaultRuleSetName());
1506            if (!result.equals(item.expectedResult)) {
1507                errln("Error for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
1508            }
1509            RuleBasedNumberFormat rbnfClone = (RuleBasedNumberFormat)rbnf.clone();
1510            if (!rbnfClone.equals(rbnf)) {
1511                errln("Error for locale " + item.locale + ", context " + item.context + ", rbnf.clone() != rbnf");
1512            } else {
1513                result = rbnfClone.format(item.value, rbnfClone.getDefaultRuleSetName());
1514                if (!result.equals(item.expectedResult)) {
1515                    errln("Error with clone for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
1516                }
1517            }
1518        }
1519    }
1520
1521    @Test
1522    public void TestInfinityNaN() {
1523        String enRules = "%default:"
1524                + "-x: minus >>;"
1525                + "Inf: infinite;"
1526                + "NaN: not a number;"
1527                + "0: =#,##0=;";
1528        RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
1529        String[][] enTestData = {
1530                {"1", "1"},
1531                {"\u221E", "infinite"},
1532                {"-\u221E", "minus infinite"},
1533                {"NaN", "not a number"},
1534
1535        };
1536
1537        doTest(enFormatter, enTestData, true);
1538
1539        // Test the default behavior when the rules are undefined.
1540        enRules = "%default:"
1541                + "-x: ->>;"
1542                + "0: =#,##0=;";
1543        enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
1544        String[][] enDefaultTestData = {
1545                {"1", "1"},
1546                {"\u221E", "∞"},
1547                {"-\u221E", "-∞"},
1548                {"NaN", "NaN"},
1549
1550        };
1551
1552        doTest(enFormatter, enDefaultTestData, true);
1553    }
1554
1555    @Test
1556    public void TestVariableDecimalPoint() {
1557        String enRules = "%spellout-numbering:"
1558                + "-x: minus >>;"
1559                + "x.x: << point >>;"
1560                + "x,x: << comma >>;"
1561                + "0.x: xpoint >>;"
1562                + "0,x: xcomma >>;"
1563                + "0: zero;"
1564                + "1: one;"
1565                + "2: two;"
1566                + "3: three;"
1567                + "4: four;"
1568                + "5: five;"
1569                + "6: six;"
1570                + "7: seven;"
1571                + "8: eight;"
1572                + "9: nine;";
1573        RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
1574        String[][] enTestPointData = {
1575                {"1.1", "one point one"},
1576                {"1.23", "one point two three"},
1577                {"0.4", "xpoint four"},
1578        };
1579        doTest(enFormatter, enTestPointData, true);
1580        DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(ULocale.ENGLISH);
1581        decimalFormatSymbols.setDecimalSeparator(',');
1582        enFormatter.setDecimalFormatSymbols(decimalFormatSymbols);
1583        String[][] enTestCommaData = {
1584                {"1.1", "one comma one"},
1585                {"1.23", "one comma two three"},
1586                {"0.4", "xcomma four"},
1587        };
1588        doTest(enFormatter, enTestCommaData, true);
1589    }
1590
1591    @Test
1592    public void TestRounding() {
1593        RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(ULocale.ENGLISH, RuleBasedNumberFormat.SPELLOUT);
1594        String[][] enTestFullData = {
1595                {"0", "zero"},
1596                {"0.4", "zero point four"},
1597                {"0.49", "zero point four nine"},
1598                {"0.5", "zero point five"},
1599                {"0.51", "zero point five one"},
1600                {"0.99", "zero point nine nine"},
1601                {"1", "one"},
1602                {"1.01", "one point zero one"},
1603                {"1.49", "one point four nine"},
1604                {"1.5", "one point five"},
1605                {"1.51", "one point five one"},
1606                {"450359962737049.6", "four hundred fifty trillion three hundred fifty-nine billion nine hundred sixty-two million seven hundred thirty-seven thousand forty-nine point six"}, // 2^52 / 10
1607                {"450359962737049.7", "four hundred fifty trillion three hundred fifty-nine billion nine hundred sixty-two million seven hundred thirty-seven thousand forty-nine point seven"}, // 2^52 + 1 / 10
1608        };
1609        doTest(enFormatter, enTestFullData, false);
1610
1611        enFormatter.setMaximumFractionDigits(0);
1612        enFormatter.setRoundingMode(BigDecimal.ROUND_HALF_EVEN);
1613        String[][] enTestIntegerData = {
1614                {"0", "zero"},
1615                {"0.4", "zero"},
1616                {"0.49", "zero"},
1617                {"0.5", "zero"},
1618                {"0.51", "one"},
1619                {"0.99", "one"},
1620                {"1", "one"},
1621                {"1.01", "one"},
1622                {"1.49", "one"},
1623                {"1.5", "two"},
1624                {"1.51", "two"},
1625        };
1626        doTest(enFormatter, enTestIntegerData, false);
1627
1628        enFormatter.setMaximumFractionDigits(1);
1629        enFormatter.setRoundingMode(BigDecimal.ROUND_HALF_EVEN);
1630        String[][] enTestTwoDigitsData = {
1631                {"0", "zero"},
1632                {"0.04", "zero"},
1633                {"0.049", "zero"},
1634                {"0.05", "zero"},
1635                {"0.051", "zero point one"},
1636                {"0.099", "zero point one"},
1637                {"10.11", "ten point one"},
1638                {"10.149", "ten point one"},
1639                {"10.15", "ten point two"},
1640                {"10.151", "ten point two"},
1641        };
1642        doTest(enFormatter, enTestTwoDigitsData, false);
1643
1644        enFormatter.setMaximumFractionDigits(3);
1645        enFormatter.setRoundingMode(BigDecimal.ROUND_DOWN);
1646        String[][] enTestThreeDigitsDownData = {
1647                {"4.3", "four point three"}, // Not 4.299!
1648        };
1649        doTest(enFormatter, enTestThreeDigitsDownData, false);
1650    }
1651
1652    @Test
1653    public void testLargeNumbers() {
1654        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ULocale.US, RuleBasedNumberFormat.SPELLOUT);
1655
1656        String[][] enTestFullData = {
1657                {"-9007199254740991", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
1658                {"9007199254740991", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
1659                {"-9007199254740992", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
1660                {"9007199254740992", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
1661                {"9999999999999998", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-eight"},
1662                {"9999999999999999", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
1663                {"999999999999999999", "nine hundred ninety-nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
1664                {"1000000000000000000", "1,000,000,000,000,000,000"}, // The rules don't go to 1 quintillion yet
1665                {"-9223372036854775809", "-9,223,372,036,854,775,809"}, // We've gone beyond 64-bit precision
1666                {"-9223372036854775808", "-9,223,372,036,854,775,808"}, // We've gone beyond +64-bit precision
1667                {"-9223372036854775807", "minus 9,223,372,036,854,775,807"}, // Minimum 64-bit precision
1668                {"-9223372036854775806", "minus 9,223,372,036,854,775,806"}, // Minimum 64-bit precision + 1
1669                {"9223372036854774111", "9,223,372,036,854,774,111"}, // Below 64-bit precision
1670                {"9223372036854774999", "9,223,372,036,854,774,999"}, // Below 64-bit precision
1671                {"9223372036854775000", "9,223,372,036,854,775,000"}, // Below 64-bit precision
1672                {"9223372036854775806", "9,223,372,036,854,775,806"}, // Maximum 64-bit precision - 1
1673                {"9223372036854775807", "9,223,372,036,854,775,807"}, // Maximum 64-bit precision
1674                {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
1675        };
1676        doTest(rbnf, enTestFullData, false);
1677    }
1678
1679    @Test
1680    public void testCompactDecimalFormatStyle() {
1681        // This is not a common use case, but we're testing it anyway.
1682        final String numberPattern = "=###0.#####=;"
1683                + "1000: <###0.00< K;"
1684                + "1000000: <###0.00< M;"
1685                + "1000000000: <###0.00< B;"
1686                + "1000000000000: <###0.00< T;"
1687                + "1000000000000000: <###0.00< Q;";
1688        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(numberPattern, ULocale.US);
1689
1690        String[][] enTestFullData = {
1691                {"1000", "1.00 K"},
1692                {"1234", "1.23 K"},
1693                {"999994", "999.99 K"},
1694                {"999995", "1000.00 K"},
1695                {"1000000", "1.00 M"},
1696                {"1200000", "1.20 M"},
1697                {"1200000000", "1.20 B"},
1698                {"1200000000000", "1.20 T"},
1699                {"1200000000000000", "1.20 Q"},
1700                {"4503599627370495", "4.50 Q"},
1701                {"4503599627370496", "4.50 Q"},
1702                {"8990000000000000", "8.99 Q"},
1703                {"9008000000000000", "9.00 Q"}, // Number doesn't precisely fit into a double
1704                {"9456000000000000", "9.00 Q"},  // Number doesn't precisely fit into a double
1705                {"10000000000000000", "10.00 Q"},  // Number doesn't precisely fit into a double
1706                {"9223372036854775807", "9223.00 Q"}, // Maximum 64-bit precision
1707                {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
1708        };
1709        doTest(rbnf, enTestFullData, false);
1710    }
1711
1712    private void assertEquals(String expected, String result) {
1713        if (!expected.equals(result)) {
1714            errln("Expected: " + expected + " Got: " + result);
1715        }
1716    }
1717
1718    @Test
1719    public void testRoundingUnrealNumbers() {
1720        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ULocale.US, RuleBasedNumberFormat.SPELLOUT);
1721        rbnf.setRoundingMode(BigDecimal.ROUND_HALF_UP);
1722        rbnf.setMaximumFractionDigits(3);
1723        assertEquals("zero point one", rbnf.format(0.1));
1724        assertEquals("zero point zero zero one", rbnf.format(0.0005));
1725        assertEquals("infinity", rbnf.format(Double.POSITIVE_INFINITY));
1726        assertEquals("not a number", rbnf.format(Double.NaN));
1727    }
1728}
1729