1/***********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1997-2011, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ***********************************************************************/
6
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "unicode/decimfmt.h"
12#include "tsnmfmt.h"
13#include "putilimp.h"
14#include "cstring.h"
15#include <float.h>
16#include <stdlib.h>
17
18IntlTestNumberFormat::~IntlTestNumberFormat() {}
19
20static const char * formattableTypeName(Formattable::Type t)
21{
22  switch(t) {
23  case Formattable::kDate: return "kDate";
24  case Formattable::kDouble: return "kDouble";
25  case Formattable::kLong: return "kLong";
26  case Formattable::kString: return "kString";
27  case Formattable::kArray: return "kArray";
28  case Formattable::kInt64: return "kInt64";
29  default: return "??unknown??";
30  }
31}
32
33/**
34 * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of
35 * NumberFormat.
36 */
37void IntlTestNumberFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
38{
39
40    if (exec) logln((UnicodeString)"TestSuite NumberFormat");
41    switch (index) {
42        case 0: name = "createInstance";
43            if (exec)
44            {
45                logln(name);
46                fStatus = U_ZERO_ERROR;
47                fFormat = NumberFormat::createInstance(fStatus);
48                testFormat(/*par*/);
49            }
50            break;
51
52        case 1: name = "DefaultLocale";
53            if (exec) testLocale(/*par, */Locale::getDefault(), name);
54            break;
55
56        case 2: name = "testAvailableLocales";
57            if (exec) {
58                logln(name);
59                testAvailableLocales(/*par*/);
60            }
61            break;
62
63        case 3: name = "monsterTest";
64            if (exec) {
65                logln(name);
66                monsterTest(/*par*/);
67            }
68            break;
69
70        default: name = ""; break;
71    }
72}
73
74void
75IntlTestNumberFormat::testLocale(/* char* par, */const Locale& locale, const UnicodeString& localeName)
76{
77    const char* name;
78
79    fLocale = locale;
80    name = "Number test";
81    logln((UnicodeString)name + " (" + localeName + ")");
82    fStatus = U_ZERO_ERROR;
83    fFormat = NumberFormat::createInstance(locale, fStatus);
84    testFormat(/* par */);
85
86    name = "Currency test";
87    logln((UnicodeString)name + " (" + localeName + ")");
88    fStatus = U_ZERO_ERROR;
89    fFormat = NumberFormat::createCurrencyInstance(locale, fStatus);
90    testFormat(/* par */);
91
92    name = "Percent test";
93    logln((UnicodeString)name + " (" + localeName + ")");
94    fStatus = U_ZERO_ERROR;
95    fFormat = NumberFormat::createPercentInstance(locale, fStatus);
96    testFormat(/* par */);
97
98    if (uprv_strcmp(locale.getName(), "en_US_POSIX") != 0) {
99        name = "Scientific test";
100        logln((UnicodeString)name + " (" + localeName + ")");
101        fStatus = U_ZERO_ERROR;
102        fFormat = NumberFormat::createScientificInstance(locale, fStatus);
103        testFormat(/* par */);
104    }
105}
106
107double IntlTestNumberFormat::randDouble()
108{
109    // Assume 8-bit (or larger) rand values.  Also assume
110    // that the system rand() function is very poor, which it always is.
111    // Call srand(currentTime) in intltest to make it truly random.
112    double d;
113    uint32_t i;
114    char* poke = (char*)&d;
115    do {
116        for (i=0; i < sizeof(double); ++i)
117        {
118            poke[i] = (char)(rand() & 0xFF);
119        }
120    } while (uprv_isNaN(d) || uprv_isInfinite(d)
121        || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d)));
122
123    return d;
124}
125
126/*
127 * Return a random uint32_t
128 **/
129uint32_t IntlTestNumberFormat::randLong()
130{
131    // Assume 8-bit (or larger) rand values.  Also assume
132    // that the system rand() function is very poor, which it always is.
133    // Call srand(currentTime) in intltest to make it truly random.
134    uint32_t d;
135    uint32_t i;
136    char* poke = (char*)&d;
137    for (i=0; i < sizeof(uint32_t); ++i)
138    {
139        poke[i] = (char)(rand() & 0xFF);
140    }
141    return d;
142}
143
144
145/* Make sure that we don't get something too large and multiply into infinity.
146   @param smallerThanMax the requested maximum value smaller than DBL_MAX */
147double IntlTestNumberFormat::getSafeDouble(double smallerThanMax) {
148    double it;
149    double high = (DBL_MAX/smallerThanMax)/10.0;
150    double low = -high;
151    do {
152        it = randDouble();
153    } while (low > it || it > high);
154    return it;
155}
156
157void
158IntlTestNumberFormat::testFormat(/* char* par */)
159{
160    if (U_FAILURE(fStatus))
161    {
162        dataerrln((UnicodeString)"**** FAIL: createXxxInstance failed. - " + u_errorName(fStatus));
163        if (fFormat != 0)
164            errln("**** FAIL: Non-null format returned by createXxxInstance upon failure.");
165        delete fFormat;
166        fFormat = 0;
167        return;
168    }
169
170    if (fFormat == 0)
171    {
172        errln((UnicodeString)"**** FAIL: Null format returned by createXxxInstance.");
173        return;
174    }
175
176    UnicodeString str;
177
178    // Assume it's a DecimalFormat and get some info
179    DecimalFormat *s = (DecimalFormat*)fFormat;
180    logln((UnicodeString)"  Pattern " + s->toPattern(str));
181
182#if U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400
183    tryIt(-2.02147304840132e-68);
184    tryIt(3.88057859588817e-68); // Test rounding when only some digits are shown because exponent is close to -maxfrac
185    tryIt(-2.64651110485945e+65); // Overflows to +INF when shown as a percent
186    tryIt(9.29526819488338e+64); // Ok -- used to fail?
187#else
188    tryIt(-2.02147304840132e-100);
189    tryIt(3.88057859588817e-096); // Test rounding when only some digits are shown because exponent is close to -maxfrac
190    tryIt(-2.64651110485945e+306); // Overflows to +INF when shown as a percent
191    tryIt(9.29526819488338e+250); // Ok -- used to fail?
192#endif
193
194    // These PASS now, with the sprintf/atof based format-parse.
195
196    // These fail due to round-off
197    // The least significant digit drops by one during each format-parse cycle.
198    // Both numbers DON'T have a round-off problem when multiplied by 100! (shown as %)
199#if U_PLATFORM == U_PF_OS390
200    tryIt(-9.18228054496402e+64);
201    tryIt(-9.69413034454191e+64);
202#else
203    tryIt(-9.18228054496402e+255);
204    tryIt(-9.69413034454191e+273);
205#endif
206
207#if U_PLATFORM != U_PF_OS390
208    tryIt(1.234e-200);
209    tryIt(-2.3e-168);
210
211    tryIt(uprv_getNaN());
212    tryIt(uprv_getInfinity());
213    tryIt(-uprv_getInfinity());
214#endif
215
216    tryIt((int32_t)251887531);
217    tryIt(5e-20 / 9);
218    tryIt(5e20 / 9);
219    tryIt(1.234e-50);
220    tryIt(9.99999999999996);
221    tryIt(9.999999999999996);
222
223	tryIt(5.06e-27);
224
225    tryIt((int32_t)INT32_MIN);
226    tryIt((int32_t)INT32_MAX);
227    tryIt((double)INT32_MIN);
228    tryIt((double)INT32_MAX);
229    tryIt((double)INT32_MIN - 1.0);
230    tryIt((double)INT32_MAX + 1.0);
231
232    tryIt(5.0 / 9.0 * 1e-20);
233    tryIt(4.0 / 9.0 * 1e-20);
234    tryIt(5.0 / 9.0 * 1e+20);
235    tryIt(4.0 / 9.0 * 1e+20);
236
237    tryIt(2147483647.);
238    tryIt((int32_t)0);
239    tryIt(0.0);
240    tryIt((int32_t)1);
241    tryIt((int32_t)10);
242    tryIt((int32_t)100);
243    tryIt((int32_t)-1);
244    tryIt((int32_t)-10);
245    tryIt((int32_t)-100);
246    tryIt((int32_t)-1913860352);
247
248    for (int32_t z=0; z<10; ++z)
249    {
250        double d = randFraction() * 2e10 - 1e10;
251        tryIt(d);
252    }
253
254    double it = getSafeDouble(100000.0);
255
256    tryIt(0.0);
257    tryIt(it);
258    tryIt((int32_t)0);
259    tryIt(uprv_floor(it));
260    tryIt((int32_t)randLong());
261
262    // try again
263    it = getSafeDouble(100.0);
264    tryIt(it);
265    tryIt(uprv_floor(it));
266    tryIt((int32_t)randLong());
267
268    // try again with very large numbers
269    it = getSafeDouble(100000000000.0);
270    tryIt(it);
271
272    // try again with very large numbers
273    // and without going outside of the int32_t range
274    it = randFraction() * INT32_MAX;
275    tryIt(it);
276    tryIt((int32_t)uprv_floor(it));
277
278    delete fFormat;
279}
280
281void
282IntlTestNumberFormat::tryIt(double aNumber)
283{
284    const int32_t DEPTH = 10;
285    Formattable number[DEPTH];
286    UnicodeString string[DEPTH];
287
288    int32_t numberMatch = 0;
289    int32_t stringMatch = 0;
290    UnicodeString errMsg;
291    int32_t i;
292    for (i=0; i<DEPTH; ++i)
293    {
294        errMsg.truncate(0); // if non-empty, we failed this iteration
295        UErrorCode status = U_ZERO_ERROR;
296        string[i] = "(n/a)"; // "format was never done" value
297        if (i == 0) {
298            number[i].setDouble(aNumber);
299        } else {
300            fFormat->parse(string[i-1], number[i], status);
301            if (U_FAILURE(status)) {
302                number[i].setDouble(1234.5); // "parse failed" value
303                errMsg = "**** FAIL: Parse of " + prettify(string[i-1]) + " failed.";
304                --i; // don't show empty last line: "1234.5 F> (n/a) P>"
305                break;
306            }
307        }
308        // Convert from long to double
309        if (number[i].getType() == Formattable::kLong)
310            number[i].setDouble(number[i].getLong());
311        else if (number[i].getType() == Formattable::kInt64)
312            number[i].setDouble((double)number[i].getInt64());
313        else if (number[i].getType() != Formattable::kDouble)
314        {
315            errMsg = ("**** FAIL: Parse of " + prettify(string[i-1])
316                + " returned non-numeric Formattable, type " + UnicodeString(formattableTypeName(number[i].getType()))
317                + ", Locale=" + UnicodeString(fLocale.getName())
318                + ", longValue=" + number[i].getLong());
319            break;
320        }
321        string[i].truncate(0);
322        fFormat->format(number[i].getDouble(), string[i]);
323        if (i > 0)
324        {
325            if (numberMatch == 0 && number[i] == number[i-1])
326                numberMatch = i;
327            else if (numberMatch > 0 && number[i] != number[i-1])
328            {
329                errMsg = ("**** FAIL: Numeric mismatch after match.");
330                break;
331            }
332            if (stringMatch == 0 && string[i] == string[i-1])
333                stringMatch = i;
334            else if (stringMatch > 0 && string[i] != string[i-1])
335            {
336                errMsg = ("**** FAIL: String mismatch after match.");
337                break;
338            }
339        }
340        if (numberMatch > 0 && stringMatch > 0)
341            break;
342    }
343    if (i == DEPTH)
344        --i;
345
346    if (stringMatch > 2 || numberMatch > 2)
347    {
348        errMsg = ("**** FAIL: No string and/or number match within 2 iterations.");
349    }
350
351    if (errMsg.length() != 0)
352    {
353        for (int32_t k=0; k<=i; ++k)
354        {
355            logln((UnicodeString)"" + k + ": " + number[k].getDouble() + " F> " +
356                  prettify(string[k]) + " P> ");
357        }
358        errln(errMsg);
359    }
360}
361
362void
363IntlTestNumberFormat::tryIt(int32_t aNumber)
364{
365    Formattable number(aNumber);
366    UnicodeString stringNum;
367    UErrorCode status = U_ZERO_ERROR;
368
369    fFormat->format(number, stringNum, status);
370    if (U_FAILURE(status))
371    {
372        errln("**** FAIL: Formatting " + aNumber);
373        return;
374    }
375    fFormat->parse(stringNum, number, status);
376    if (U_FAILURE(status))
377    {
378        errln("**** FAIL: Parse of " + prettify(stringNum) + " failed.");
379        return;
380    }
381    if (number.getType() != Formattable::kLong)
382    {
383        errln("**** FAIL: Parse of " + prettify(stringNum)
384            + " returned non-long Formattable, type " + UnicodeString(formattableTypeName(number.getType()))
385            + ", Locale=" + UnicodeString(fLocale.getName())
386            + ", doubleValue=" + number.getDouble()
387            + ", longValue=" + number.getLong()
388            + ", origValue=" + aNumber
389            );
390    }
391    if (number.getLong() != aNumber) {
392        errln("**** FAIL: Parse of " + prettify(stringNum) + " failed. Got:" + number.getLong()
393            + " Expected:" + aNumber);
394    }
395}
396
397void IntlTestNumberFormat::testAvailableLocales(/* char* par */)
398{
399    int32_t count = 0;
400    const Locale* locales = NumberFormat::getAvailableLocales(count);
401    logln((UnicodeString)"" + count + " available locales");
402    if (locales && count)
403    {
404        UnicodeString name;
405        UnicodeString all;
406        for (int32_t i=0; i<count; ++i)
407        {
408            if (i!=0)
409                all += ", ";
410            all += locales[i].getName();
411        }
412        logln(all);
413    }
414    else
415        dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer");
416}
417
418void IntlTestNumberFormat::monsterTest(/* char* par */)
419{
420    const char *SEP = "============================================================\n";
421    int32_t count;
422    const Locale* allLocales = NumberFormat::getAvailableLocales(count);
423    Locale* locales = (Locale*)allLocales;
424    Locale quickLocales[6];
425    if (allLocales && count)
426    {
427        if (quick && count > 6) {
428            logln("quick test: testing just 6 locales!");
429            count = 6;
430            locales = quickLocales;
431            locales[0] = allLocales[0];
432            locales[1] = allLocales[1];
433            locales[2] = allLocales[2];
434            // In a quick test, make sure we test locales that use
435            // currency prefix, currency suffix, and choice currency
436            // logic.  Otherwise bugs in these areas can slip through.
437            locales[3] = Locale("ar", "AE", "");
438            locales[4] = Locale("cs", "CZ", "");
439            locales[5] = Locale("en", "IN", "");
440        }
441        for (int32_t i=0; i<count; ++i)
442        {
443            UnicodeString name(locales[i].getName(), "");
444            logln(SEP);
445            testLocale(/* par, */locales[i], name);
446        }
447    }
448
449    logln(SEP);
450}
451
452#endif /* #if !UCONFIG_NO_FORMATTING */
453