// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *******************************************************************************
 * Copyright (C) 2001-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 */

/**
 * Port From:   ICU4C v1.8.1 : format : NumberFormatTest
 * Source File: $ICU4oot/source/test/intltest/numfmtst.cpp
 **/

package com.ibm.icu.dev.test.format;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.dev.test.format.IntlTestDecimalFormatAPIC.FieldContainer;
import com.ibm.icu.impl.ICUConfig;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.impl.data.ResourceReader;
import com.ibm.icu.impl.data.TokenIterator;
import com.ibm.icu.impl.number.PatternStringUtils;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
import com.ibm.icu.text.CompactDecimalFormat;
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.MeasureFormat;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.NumberFormat.NumberFormatFactory;
import com.ibm.icu.text.NumberFormat.SimpleNumberFormatFactory;
import com.ibm.icu.text.NumberingSystem;
import com.ibm.icu.text.RuleBasedNumberFormat;
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;

@RunWith(JUnit4.class)
public class NumberFormatTest extends TestFmwk {

    @Test
    public void TestRoundingScientific10542() {
        DecimalFormat format =
                new DecimalFormat("0.00E0");

        int[] roundingModes = {
              BigDecimal.ROUND_CEILING,
              BigDecimal.ROUND_DOWN,
              BigDecimal.ROUND_FLOOR,
              BigDecimal.ROUND_HALF_DOWN,
              BigDecimal.ROUND_HALF_EVEN,
              BigDecimal.ROUND_HALF_UP,
              BigDecimal.ROUND_UP};
        String[] descriptions = {
                "Round Ceiling",
                "Round Down",
                "Round Floor",
                "Round half down",
                "Round half even",
                "Round half up",
                "Round up"};

        double[] values = {-0.003006, -0.003005, -0.003004, 0.003014, 0.003015, 0.003016};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        String[][] expected = {
                {"-3.00E-3", "-3.00E-3", "-3.00E-3", "3.02E-3", "3.02E-3", "3.02E-3"},
                {"-3.00E-3", "-3.00E-3", "-3.00E-3", "3.01E-3", "3.01E-3", "3.01E-3"},
                {"-3.01E-3", "-3.01E-3", "-3.01E-3", "3.01E-3", "3.01E-3", "3.01E-3"},
                {"-3.01E-3", "-3.00E-3", "-3.00E-3", "3.01E-3", "3.01E-3", "3.02E-3"},
                {"-3.01E-3", "-3.00E-3", "-3.00E-3", "3.01E-3", "3.02E-3", "3.02E-3"},
                {"-3.01E-3", "-3.01E-3", "-3.00E-3", "3.01E-3", "3.02E-3", "3.02E-3"},
                {"-3.01E-3", "-3.01E-3", "-3.01E-3", "3.02E-3", "3.02E-3", "3.02E-3"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{-3006.0, -3005, -3004, 3014, 3015, 3016};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"-3.00E3", "-3.00E3", "-3.00E3", "3.02E3", "3.02E3", "3.02E3"},
                {"-3.00E3", "-3.00E3", "-3.00E3", "3.01E3", "3.01E3", "3.01E3"},
                {"-3.01E3", "-3.01E3", "-3.01E3", "3.01E3", "3.01E3", "3.01E3"},
                {"-3.01E3", "-3.00E3", "-3.00E3", "3.01E3", "3.01E3", "3.02E3"},
                {"-3.01E3", "-3.00E3", "-3.00E3", "3.01E3", "3.02E3", "3.02E3"},
                {"-3.01E3", "-3.01E3", "-3.00E3", "3.01E3", "3.02E3", "3.02E3"},
                {"-3.01E3", "-3.01E3", "-3.01E3", "3.02E3", "3.02E3", "3.02E3"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{0.0, -0.0};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"},
                {"0.00E0", "-0.00E0"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{1e25, 1e25 + 1e15, 1e25 - 1e15};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"1.00E25", "1.01E25", "1.00E25"},
                {"1.00E25", "1.00E25", "9.99E24"},
                {"1.00E25", "1.00E25", "9.99E24"},
                {"1.00E25", "1.00E25", "1.00E25"},
                {"1.00E25", "1.00E25", "1.00E25"},
                {"1.00E25", "1.00E25", "1.00E25"},
                {"1.00E25", "1.01E25", "1.00E25"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{-1e25, -1e25 + 1e15, -1e25 - 1e15};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"-1.00E25", "-9.99E24", "-1.00E25"},
                {"-1.00E25", "-9.99E24", "-1.00E25"},
                {"-1.00E25", "-1.00E25", "-1.01E25"},
                {"-1.00E25", "-1.00E25", "-1.00E25"},
                {"-1.00E25", "-1.00E25", "-1.00E25"},
                {"-1.00E25", "-1.00E25", "-1.00E25"},
                {"-1.00E25", "-1.00E25", "-1.01E25"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{1e-25, 1e-25 + 1e-35, 1e-25 - 1e-35};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"1.00E-25", "1.01E-25", "1.00E-25"},
                {"1.00E-25", "1.00E-25", "9.99E-26"},
                {"1.00E-25", "1.00E-25", "9.99E-26"},
                {"1.00E-25", "1.00E-25", "1.00E-25"},
                {"1.00E-25", "1.00E-25", "1.00E-25"},
                {"1.00E-25", "1.00E-25", "1.00E-25"},
                {"1.00E-25", "1.01E-25", "1.00E-25"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
        values = new double[]{-1e-25, -1e-25 + 1e-35, -1e-25 - 1e-35};
        // The order of these expected values correspond to the order of roundingModes and the order of values.
        expected = new String[][]{
                {"-1.00E-25", "-9.99E-26", "-1.00E-25"},
                {"-1.00E-25", "-9.99E-26", "-1.00E-25"},
                {"-1.00E-25", "-1.00E-25", "-1.01E-25"},
                {"-1.00E-25", "-1.00E-25", "-1.00E-25"},
                {"-1.00E-25", "-1.00E-25", "-1.00E-25"},
                {"-1.00E-25", "-1.00E-25", "-1.00E-25"},
                {"-1.00E-25", "-1.00E-25", "-1.01E-25"}};
        verifyRounding(format, values, expected, roundingModes, descriptions);
    }

    private void verifyRounding(DecimalFormat format, double[] values, String[][] expected, int[] roundingModes,
            String[] descriptions) {
        for (int i = 0; i < roundingModes.length; i++) {
            format.setRoundingMode(roundingModes[i]);
            for (int j = 0; j < values.length; j++) {
                assertEquals(descriptions[i]+" " +values[j], expected[i][j], format.format(values[j]));
            }
        }
    }

    @Test
    public void Test10419RoundingWith0FractionDigits() {
        Object[][] data = new Object[][]{
                {BigDecimal.ROUND_CEILING, 1.488, "2"},
                {BigDecimal.ROUND_DOWN, 1.588, "1"},
                {BigDecimal.ROUND_FLOOR, 1.588, "1"},
                {BigDecimal.ROUND_HALF_DOWN, 1.5, "1"},
                {BigDecimal.ROUND_HALF_EVEN, 2.5, "2"},
                {BigDecimal.ROUND_HALF_UP, 2.5, "3"},
                {BigDecimal.ROUND_UP, 1.5, "2"},
        };
        NumberFormat nff = NumberFormat.getNumberInstance(ULocale.ENGLISH);
        nff.setMaximumFractionDigits(0);
        for (Object[] item : data) {
          nff.setRoundingMode(((Integer) item[0]).intValue());
          assertEquals("Test10419", item[2], nff.format(item[1]));
        }
    }

    @Test
    public void TestParseNegativeWithFaLocale() {
        DecimalFormat parser = (DecimalFormat) NumberFormat.getInstance(new ULocale("fa"));
        try {
            double value = parser.parse("-0,5").doubleValue();
            assertEquals("Expect -0.5", -0.5, value);
        } catch (ParseException e) {
            TestFmwk.errln("Parsing -0.5 should have succeeded.");
        }
    }

    @Test
    public void TestParseNegativeWithAlternativeMinusSign() {
        DecimalFormat parser = (DecimalFormat) NumberFormat.getInstance(new ULocale("en"));
        try {
            double value = parser.parse("\u208B0.5").doubleValue();
            assertEquals("Expect -0.5", -0.5, value);
        } catch (ParseException e) {
            TestFmwk.errln("Parsing -0.5 should have succeeded.");
        }
    }

    // Test various patterns
    @Test
    public void TestPatterns() {

        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        final String pat[]    = { "#.#", "#.", ".#", "#" };
        int pat_length = pat.length;
        final String newpat[] = { "0.#", "0.", "#.0", "0" };
        final String num[]    = { "0",   "0.", ".0", "0" };
        for (int i=0; i<pat_length; ++i)
        {
            DecimalFormat fmt = new DecimalFormat(pat[i], sym);
            String newp = fmt.toPattern();
            if (!newp.equals(newpat[i]))
                errln("FAIL: Pattern " + pat[i] + " should transmute to " + newpat[i] +
                        "; " + newp + " seen instead");

            String s = ((NumberFormat)fmt).format(0);
            if (!s.equals(num[i]))
            {
                errln("FAIL: Pattern " + pat[i] + " should format zero as " + num[i] +
                        "; " + s + " seen instead");
                logln("Min integer digits = " + fmt.getMinimumIntegerDigits());
            }
            // BigInteger 0 - ticket#4731
            s = ((NumberFormat)fmt).format(BigInteger.ZERO);
            if (!s.equals(num[i]))
            {
                errln("FAIL: Pattern " + pat[i] + " should format BigInteger zero as " + num[i] +
                        "; " + s + " seen instead");
                logln("Min integer digits = " + fmt.getMinimumIntegerDigits());
            }
        }
    }

    @Test
    public void Test20186_SpacesAroundSemicolon() {
        DecimalFormat df = new DecimalFormat("0.00 ; -0.00");
        expect2(df, 1, "1.00 ");
        expect2(df, -1, " -1.00");

        df = new DecimalFormat("0.00;");
        expect2(df, 1, "1.00");
        expect2(df, -1, "-1.00");

        df = new DecimalFormat("0.00;0.00");
        expect2(df, 1, "1.00");
        expect(df, -1, "1.00");  // parses as 1, not -1

        df = new DecimalFormat(" 0.00 ; -0.00 ");
        expect2(df, 1, " 1.00 ");
        expect2(df, -1, " -1.00 ");
    }

    // Test exponential pattern
    @Test
    public void TestExponential() {

        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        final String pat[] = { "0.####E0", "00.000E00", "##0.######E000", "0.###E0;[0.###E0]" };
        int pat_length = pat.length;

        double val[] = { 0.01234, 123456789, 1.23e300, -3.141592653e-271 };
        int val_length = val.length;
        final String valFormat[] = {
                // 0.####E0
                "1.234E-2", "1.2346E8", "1.23E300", "-3.1416E-271",
                // 00.000E00
                "12.340E-03", "12.346E07", "12.300E299", "-31.416E-272",
                // ##0.######E000
                "12.34E-003", "123.4568E006", "1.23E300", "-314.1593E-273",
                // 0.###E0;[0.###E0]
                "1.234E-2", "1.235E8", "1.23E300", "[3.142E-271]" };
        /*double valParse[] =
            {
                0.01234, 123460000, 1.23E300, -3.1416E-271,
                0.01234, 123460000, 1.23E300, -3.1416E-271,
                0.01234, 123456800, 1.23E300, -3.141593E-271,
                0.01234, 123500000, 1.23E300, -3.142E-271,
            };*/ //The variable is never used

        int lval[] = { 0, -1, 1, 123456789 };
        int lval_length = lval.length;
        final String lvalFormat[] = {
                // 0.####E0
                "0E0", "-1E0", "1E0", "1.2346E8",
                // 00.000E00
                "00.000E00", "-10.000E-01", "10.000E-01", "12.346E07",
                // ##0.######E000
                "0E000", "-1E000", "1E000", "123.4568E006",
                // 0.###E0;[0.###E0]
                "0E0", "[1E0]", "1E0", "1.235E8" };
        int lvalParse[] =
            {
                0, -1, 1, 123460000,
                0, -1, 1, 123460000,
                0, -1, 1, 123456800,
                0, -1, 1, 123500000,
            };
        int ival = 0, ilval = 0;
        for (int p = 0; p < pat_length; ++p) {
            DecimalFormat fmt = new DecimalFormat(pat[p], sym);
            logln("Pattern \"" + pat[p] + "\" -toPattern-> \"" + fmt.toPattern() + "\"");
            int v;
            for (v = 0; v < val_length; ++v) {
                String s;
                s = ((NumberFormat) fmt).format(val[v]);
                logln(" " + val[v] + " -format-> " + s);
                if (!s.equals(valFormat[v + ival]))
                    errln("FAIL: Expected " + valFormat[v + ival]);

                ParsePosition pos = new ParsePosition(0);
                double a = fmt.parse(s, pos).doubleValue();
                if (pos.getIndex() == s.length()) {
                    logln("  -parse-> " + Double.toString(a));
                    // Use epsilon comparison as necessary
                } else
                    errln("FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a);
            }
            for (v = 0; v < lval_length; ++v) {
                String s;
                s = ((NumberFormat) fmt).format(lval[v]);
                logln(" " + lval[v] + "L -format-> " + s);
                if (!s.equals(lvalFormat[v + ilval]))
                    errln("ERROR: Expected " + lvalFormat[v + ilval] + " Got: " + s);

                ParsePosition pos = new ParsePosition(0);
                long a = 0;
                Number A = fmt.parse(s, pos);
                if (A != null) {
                    a = A.longValue();
                    if (pos.getIndex() == s.length()) {
                        logln("  -parse-> " + a);
                        if (a != lvalParse[v + ilval])
                            errln("FAIL: Expected " + lvalParse[v + ilval]);
                    } else
                        errln("FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + Long.toString(a));
                } else {
                    errln("Fail to parse the string: " + s);
                }
            }
            ival += val_length;
            ilval += lval_length;
        }
    }

    // Test the handling of quotes
    @Test
    public void TestQuotes() {

        StringBuffer pat;
        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        pat = new StringBuffer("a'fo''o'b#");
        DecimalFormat fmt = new DecimalFormat(pat.toString(), sym);
        String s = ((NumberFormat)fmt).format(123);
        logln("Pattern \"" + pat + "\"");
        logln(" Format 123 . " + s);
        if (!s.equals("afo'ob123"))
            errln("FAIL: Expected afo'ob123");

        s ="";
        pat = new StringBuffer("a''b#");
        fmt = new DecimalFormat(pat.toString(), sym);
        s = ((NumberFormat)fmt).format(123);
        logln("Pattern \"" + pat + "\"");
        logln(" Format 123 . " + s);
        if (!s.equals("a'b123"))
            errln("FAIL: Expected a'b123");
    }

    @Test
    public void TestParseCurrencyTrailingSymbol() {
        // see sun bug 4709840
        NumberFormat fmt = NumberFormat.getCurrencyInstance(Locale.GERMANY);
        float val = 12345.67f;
        String str = fmt.format(val);
        logln("val: " + val + " str: " + str);
        try {
            Number num = fmt.parse(str);
            logln("num: " + num);
        } catch (ParseException e) {
            errln("parse of '" + str + "' threw exception: " + e);
        }
    }

    /**
     * Test the handling of the currency symbol in patterns.
     **/
    @Test
    public void TestCurrencySign() {
        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        StringBuffer pat = new StringBuffer("");
        char currency = 0x00A4;
        // "\xA4#,##0.00;-\xA4#,##0.00"
        pat.append(currency).append("#,##0.00;-").append(currency).append("#,##0.00");
        DecimalFormat fmt = new DecimalFormat(pat.toString(), sym);
        String s = ((NumberFormat) fmt).format(1234.56);
        pat = new StringBuffer();
        logln("Pattern \"" + fmt.toPattern() + "\"");
        logln(" Format " + 1234.56 + " . " + s);
        assertEquals("symbol, pos", "$1,234.56", s);

        s = ((NumberFormat) fmt).format(-1234.56);
        logln(" Format " + Double.toString(-1234.56) + " . " + s);
        assertEquals("symbol, neg", "-$1,234.56", s);

        pat.setLength(0);
        // "\xA4\xA4 #,##0.00;\xA4\xA4 -#,##0.00"
        pat.append(currency).append(currency).append(" #,##0.00;").append(currency).append(currency).append(" -#,##0.00");
        fmt = new DecimalFormat(pat.toString(), sym);
        s = ((NumberFormat) fmt).format(1234.56);
        logln("Pattern \"" + fmt.toPattern() + "\"");
        logln(" Format " + Double.toString(1234.56) + " . " + s);
        assertEquals("name, pos", "USD 1,234.56", s);

        s = ((NumberFormat) fmt).format(-1234.56);
        logln(" Format " + Double.toString(-1234.56) + " . " + s);
        assertEquals("name, neg", "USD -1,234.56", s);
    }

    @Test
    public void TestSpaceParsing() {
        // the data are:
        // the string to be parsed, parsed position, parsed error index
        String[][] DATA = {
                {"$124", "4", "-1"},
                {"$124 $124", "4", "-1"},
                {"$124 ", "4", "-1"},
                {"$124  ", "4", "-1"},
                {"$ 124 ", "5", "-1"},
                {"$\u00A0124 ", "5", "-1"},
                {" $ 124 ", "6", "-1"},
                {"124$", "4", "-1"},
                {"124 $", "5", "-1"},
                {"$124\u200A", "4", "-1"},
                {"$\u200A124", "5", "-1"},
        };
        NumberFormat foo = NumberFormat.getCurrencyInstance();
        for (int i = 0; i < DATA.length; ++i) {
            ParsePosition parsePosition = new ParsePosition(0);
            String stringToBeParsed = DATA[i][0];
            int parsedPosition = Integer.parseInt(DATA[i][1]);
            int errorIndex = Integer.parseInt(DATA[i][2]);
            try {
                Number result = foo.parse(stringToBeParsed, parsePosition);
                if (parsePosition.getIndex() != parsedPosition ||
                        parsePosition.getErrorIndex() != errorIndex) {
                    errln("FAILED parse " + stringToBeParsed + "; parse position: " + parsePosition.getIndex() + "; error position: " + parsePosition.getErrorIndex());
                }
                if (parsePosition.getErrorIndex() == -1 &&
                        result.doubleValue() != 124) {
                    errln("FAILED parse " + stringToBeParsed + "; value " + result.doubleValue());
                }
            } catch (Exception e) {
                errln("FAILED " + e.toString());
            }
        }
    }

    @Test
    public void TestSpaceParsingStrict() {
        // All trailing grouping separators should be ignored in strict mode, not just the first.
        Object[][] cases = {
                {"123 ", 3, -1},
                {"123  ", 3, -1},
                {"123  ,", 3, -1},
                {"123,", 3, -1},
                {"123, ", 3, -1},
                {"123,,", 3, -1},
                {"123,, ", 3, -1},
                {"123,,456", 3, -1},
                {"123 ,", 3, -1},
                {"123, ", 3, -1},
                {"123, 456", 3, -1},
                {"123  456", 3, -1}
        };
        DecimalFormat df = new DecimalFormat("#,###");
        df.setParseStrict(true);
        for (Object[] cas : cases) {
            String input = (String) cas[0];
            int expectedIndex = (Integer) cas[1];
            int expectedErrorIndex = (Integer) cas[2];
            ParsePosition ppos = new ParsePosition(0);
            df.parse(input, ppos);
            assertEquals("Failed on index: '" + input + "'", expectedIndex, ppos.getIndex());
            assertEquals("Failed on error: '" + input + "'", expectedErrorIndex, ppos.getErrorIndex());
        }
    }

    @Test
    public void TestMultiCurrencySign() {
        String[][] DATA = {
                // the fields in the following test are:
                // locale,
                // currency pattern (with negative pattern),
                // currency number to be formatted,
                // currency format using currency symbol name, such as "$" for USD,
                // currency format using currency ISO name, such as "USD",
                // currency format using plural name, such as "US dollars".
                // for US locale
                {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1234.56", "$1,234.56", "USD 1,234.56", "US dollars 1,234.56"},
                {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD 1,234.56", "-US dollars 1,234.56"},
                {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1", "$1.00", "USD 1.00", "US dollars 1.00"},
                // for CHINA locale
                {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1234.56", "\u00A51,234.56", "CNY 1,234.56", "\u4EBA\u6C11\u5E01 1,234.56"},
                {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "-1234.56", "(\u00A51,234.56)", "(CNY 1,234.56)", "(\u4EBA\u6C11\u5E01 1,234.56)"},
                {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1", "\u00A51.00", "CNY 1.00", "\u4EBA\u6C11\u5E01 1.00"}
        };

        String doubleCurrencyStr = "\u00A4\u00A4";
        String tripleCurrencyStr = "\u00A4\u00A4\u00A4";

        for (int i=0; i<DATA.length; ++i) {
            String locale = DATA[i][0];
            String pat = DATA[i][1];
            Double numberToBeFormat = new Double(DATA[i][2]);
            DecimalFormatSymbols sym = new DecimalFormatSymbols(new ULocale(locale));
            for (int j=1; j<=3; ++j) {
                // j represents the number of currency sign in the pattern.
                if (j == 2) {
                    pat = pat.replaceAll("\u00A4", doubleCurrencyStr);
                } else if (j == 3) {
                    pat = pat.replaceAll("\u00A4\u00A4", tripleCurrencyStr);
                }
                DecimalFormat fmt = new DecimalFormat(pat, sym);
                String s = ((NumberFormat) fmt).format(numberToBeFormat);
                // DATA[i][3] is the currency format result using a
                // single currency sign.
                // DATA[i][4] is the currency format result using
                // double currency sign.
                // DATA[i][5] is the currency format result using
                // triple currency sign.
                // DATA[i][j+2] is the currency format result using
                // 'j' number of currency sign.
                String currencyFormatResult = DATA[i][2+j];
                if (!s.equals(currencyFormatResult)) {
                    errln("FAIL format: Expected " + currencyFormatResult + " but got " + s);
                }
                try {
                    // mix style parsing
                    for (int k=3; k<=4; ++k) {
                        // DATA[i][3] is the currency format result using a
                        // single currency sign.
                        // DATA[i][4] is the currency format result using
                        // double currency sign.
                        // DATA[i][5] is the currency format result using
                        // triple currency sign.
                        // ICU 59: long name parsing requires currency mode.
                        String oneCurrencyFormat = DATA[i][k];
                        if (fmt.parse(oneCurrencyFormat).doubleValue() !=
                                numberToBeFormat.doubleValue()) {
                            errln("FAILED parse " + oneCurrencyFormat);
                        }
                    }
                } catch (ParseException e) {
                    errln("FAILED, DecimalFormat parse currency: " + e.toString());
                }
            }
        }
    }

    @Test
    public void TestCurrencyFormatForMixParsing() {
        MeasureFormat curFmt = MeasureFormat.getCurrencyFormat(new ULocale("en_US"));
        String[] formats = {
                "$1,234.56",  // string to be parsed
                "USD1,234.56",
                "US dollars1,234.56",
                "1,234.56 US dollars"
        };
        try {
            for (int i = 0; i < formats.length; ++i) {
                String stringToBeParsed = formats[i];
                CurrencyAmount parsedVal = (CurrencyAmount)curFmt.parseObject(stringToBeParsed);
                Number val = parsedVal.getNumber();
                if (!val.equals(new BigDecimal("1234.56"))) {
                    errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the number. val=" + val);
                }
                if (!parsedVal.getCurrency().equals(Currency.getInstance("USD"))) {
                    errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the currency");
                }
            }
        } catch (ParseException e) {
            errln("parse FAILED: " + e.toString());
        }
    }


    /** Starting in ICU 62, strict mode is actually strict with currency formats. */
    @Test
    public void TestMismatchedCurrencyFormatFail() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.ENGLISH);
        assertEquals("Test assumes that currency sign is at the beginning",
                "\u00A4#,##0.00",
                df.toPattern());
        // Should round-trip on the correct currency format:
        expect2(df, 1.23, "\u00A41.23");
        df.setCurrency(Currency.getInstance("EUR"));
        expect2(df, 1.23, "\u20AC1.23");
        // Should parse with currency in the wrong place in lenient mode
        df.setParseStrict(false);
        expect(df, "1.23\u20AC", 1.23);
        expectParseCurrency(df, Currency.getInstance("EUR"), "1.23\u20AC");
        // Should NOT parse with currency in the wrong place in STRICT mode
        df.setParseStrict(true);
        {
            ParsePosition ppos = new ParsePosition(0);
            df.parse("1.23\u20AC", ppos);
            assertEquals("Should fail to parse", 0, ppos.getIndex());
        }
        {
            ParsePosition ppos = new ParsePosition(0);
            df.parseCurrency("1.23\u20AC", ppos);
            assertEquals("Should fail to parse currency", 0, ppos.getIndex());
        }
    }

    @Test
    public void TestDecimalFormatCurrencyParse() {
        // Locale.US
        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        StringBuffer pat = new StringBuffer("");
        char currency = 0x00A4;
        // "\xA4#,##0.00;-\xA4#,##0.00"
        pat.append(currency).append(currency).append(currency).append("#,##0.00;-").append(currency).append(currency).append(currency).append("#,##0.00");
        DecimalFormat fmt = new DecimalFormat(pat.toString(), sym);
        String[][] DATA = {
                // the data are:
                // string to be parsed, the parsed result (number)
                {"$1.00", "1"},
                {"USD1.00", "1"},
                {"1.00 US dollar", "1"},
                {"$1,234.56", "1234.56"},
                {"USD1,234.56", "1234.56"},
                {"1,234.56 US dollar", "1234.56"},
        };
        try {
            for (int i = 0; i < DATA.length; ++i) {
                String stringToBeParsed = DATA[i][0];
                double parsedResult = Double.parseDouble(DATA[i][1]);
                Number num = fmt.parse(stringToBeParsed);
                if (num.doubleValue() != parsedResult) {
                    errln("FAIL parse: Expected " + parsedResult);
                }
            }
        } catch (ParseException e) {
            errln("FAILED, DecimalFormat parse currency: " + e.toString());
        }
    }

    /**
     * Test localized currency patterns.
     */
    @Test
    public void TestCurrency() {
        String[] DATA = {
                "fr_CA", "1,50\u00a0$",
                "de_DE", "1,50\u00a0\u20AC",
                "de_DE@currency=DEM", "1,50\u00a0DM",
                "fr_FR", "1,50\u00a0\u20AC",
                "fr_FR@currency=FRF", "1,50\u00a0F",
        };

        for (int i=0; i<DATA.length; i+=2) {
            Locale locale = new Locale(DATA[i]);
            NumberFormat fmt = NumberFormat.getCurrencyInstance(locale);
            String s = fmt.format(1.50);
            if (s.equals(DATA[i+1])) {
                logln("Ok: 1.50 x " + locale + " => " + s);
            } else {
                logln("FAIL: 1.50 x " + locale + " => " + s +
                        ", expected " + DATA[i+1]);
            }
        }

        // format currency with CurrencyAmount
        for (int i=0; i<DATA.length; i+=2) {
            Locale locale = new Locale(DATA[i]);

            Currency curr = Currency.getInstance(locale);
            logln("\nName of the currency is: " + curr.getName(locale, Currency.LONG_NAME, new boolean[] {false}));
            CurrencyAmount cAmt = new CurrencyAmount(1.5, curr);
            logln("CurrencyAmount object's hashCode is: " + cAmt.hashCode()); //cover hashCode

            NumberFormat fmt = NumberFormat.getCurrencyInstance(locale);
            String sCurr = fmt.format(cAmt);
            if (sCurr.equals(DATA[i+1])) {
                logln("Ok: 1.50 x " + locale + " => " + sCurr);
            } else {
                errln("FAIL: 1.50 x " + locale + " => " + sCurr +
                        ", expected " + DATA[i+1]);
            }
        }

        //Cover MeasureFormat.getCurrencyFormat()
        ULocale save = ULocale.getDefault();
        ULocale.setDefault(ULocale.US);
        MeasureFormat curFmt = MeasureFormat.getCurrencyFormat();
        String strBuf = curFmt.format(new CurrencyAmount(new Float(1234.56), Currency.getInstance("USD")));

        try {
            CurrencyAmount parsedVal = (CurrencyAmount)curFmt.parseObject(strBuf);
            Number val = parsedVal.getNumber();
            if (!val.equals(new BigDecimal("1234.56"))) {
                errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the number. val=" + val);
            }
            if (!parsedVal.getCurrency().equals(Currency.getInstance("USD"))) {
                errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the currency");
            }
        }
        catch (ParseException e) {
            errln("FAIL: " + e.getMessage());
        }
        ULocale.setDefault(save);
    }

    @Test
    public void TestJavaCurrencyConversion() {
        java.util.Currency gbpJava = java.util.Currency.getInstance("GBP");
        Currency gbpIcu = Currency.getInstance("GBP");
        assertEquals("ICU should equal API value", gbpIcu, Currency.fromJavaCurrency(gbpJava));
        assertEquals("Java should equal API value", gbpJava, gbpIcu.toJavaCurrency());
        // Test CurrencyAmount constructors
        CurrencyAmount ca1 = new CurrencyAmount(123.45, gbpJava);
        CurrencyAmount ca2 = new CurrencyAmount(123.45, gbpIcu);
        assertEquals("CurrencyAmount from both Double constructors should be equal", ca1, ca2);
        // Coverage for the Number constructor
        ca1 = new CurrencyAmount(new BigDecimal("543.21"), gbpJava);
        ca2 = new CurrencyAmount(new BigDecimal("543.21"), gbpIcu);
        assertEquals("CurrencyAmount from both Number constructors should be equal", ca1, ca2);
    }

    @Test
    public void TestCurrencyIsoPluralFormat() {
        String[][] DATA = {
                // the data are:
                // locale,
                // currency amount to be formatted,
                // currency ISO code to be formatted,
                // format result using CURRENCYSTYLE,
                // format result using ISOCURRENCYSTYLE,
                // format result using PLURALCURRENCYSTYLE,
                {"en_US", "1", "USD", "$1.00", "USD 1.00", "1.00 US dollars"},
                {"en_US", "1234.56", "USD", "$1,234.56", "USD 1,234.56", "1,234.56 US dollars"},
                {"en_US", "-1234.56", "USD", "-$1,234.56", "-USD 1,234.56", "-1,234.56 US dollars"},
                {"zh_CN", "1", "USD", "US$1.00", "USD 1.00", "1.00 美元"},
                {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD 1,234.56", "1,234.56 美元"},
                {"zh_CN", "1", "CNY", "¥1.00", "CNY 1.00", "1.00 人民币"},
                {"zh_CN", "1234.56", "CNY", "¥1,234.56", "CNY 1,234.56", "1,234.56 人民币"},
                {"ru_RU", "1", "RUB", "1,00 \u20BD", "1,00 RUB", "1,00 российского рубля"},
                {"ru_RU", "2", "RUB", "2,00 \u20BD", "2,00 RUB", "2,00 российского рубля"},
                {"ru_RU", "5", "RUB", "5,00 \u20BD", "5,00 RUB", "5,00 российского рубля"},
                // test locale without currency information
                {"root", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"},
                {"root@numbers=latn", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"}, // ensure that the root locale is still used with modifiers
                {"root@numbers=arab", "-1.23", "USD", "\u061C-\u0661\u066B\u0662\u0663\u00A0US$", "\u061C-\u0661\u066B\u0662\u0663\u00A0USD", "\u061C-\u0661\u066B\u0662\u0663 USD"}, // ensure that the root locale is still used with modifiers
                {"es_AR", "1", "INR", "INR\u00A01,00", "INR\u00A01,00", "1,00 rupia india"},
                {"ar_EG", "1", "USD", "١٫٠٠\u00A0US$", "١٫٠٠\u00A0USD", "١٫٠٠ دولار أمريكي"},
        };

        for (int i=0; i<DATA.length; ++i) {
            for (int k = NumberFormat.CURRENCYSTYLE;
                    k <= NumberFormat.PLURALCURRENCYSTYLE;
                    ++k) {
                // k represents currency format style.
                if ( k != NumberFormat.CURRENCYSTYLE &&
                        k != NumberFormat.ISOCURRENCYSTYLE &&
                        k != NumberFormat.PLURALCURRENCYSTYLE ) {
                    continue;
                }
                String localeString = DATA[i][0];
                Double numberToBeFormat = new Double(DATA[i][1]);
                String currencyISOCode = DATA[i][2];
                ULocale locale = new ULocale(localeString);
                NumberFormat numFmt = NumberFormat.getInstance(locale, k);
                numFmt.setCurrency(Currency.getInstance(currencyISOCode));
                String strBuf = numFmt.format(numberToBeFormat);
                int resultDataIndex = k-1;
                if ( k == NumberFormat.CURRENCYSTYLE ) {
                    resultDataIndex = k+2;
                }
                // DATA[i][resultDataIndex] is the currency format result
                // using 'k' currency style.
                String formatResult = DATA[i][resultDataIndex];
                if (!strBuf.equals(formatResult)) {
                    errln("FAIL: localeID: " + localeString + ", expected(" + formatResult.length() + "): \"" + formatResult + "\", actual(" + strBuf.length() + "): \"" + strBuf + "\"");
                }
                // test parsing, and test parsing for all currency formats.
                for (int j = 3; j < 6; ++j) {
                    // DATA[i][3] is the currency format result using
                    // CURRENCYSTYLE formatter.
                    // DATA[i][4] is the currency format result using
                    // ISOCURRENCYSTYLE formatter.
                    // DATA[i][5] is the currency format result using
                    // PLURALCURRENCYSTYLE formatter.
                    String oneCurrencyFormatResult = DATA[i][j];
                    CurrencyAmount val = numFmt.parseCurrency(oneCurrencyFormatResult, null);
                    if (val.getNumber().doubleValue() != numberToBeFormat.doubleValue()) {
                        errln("FAIL: getCurrencyFormat of locale " + localeString + " failed roundtripping the number. val=" + val + "; expected: " + numberToBeFormat);
                    }
                }
            }
        }
    }


    @Test
    public void TestMiscCurrencyParsing() {
        String[][] DATA = {
                // each has: string to be parsed, parsed position, error position
                {"1.00 ", "4", "-1", "0", "4"},
                {"1.00 UAE dirha", "4", "-1", "0", "4"},
                {"1.00 us dollar", "14", "-1", "14", "-1"},
                {"1.00 US DOLLAR", "14", "-1", "14", "-1"},
                {"1.00 usd", "8", "-1", "8", "-1"},
                {"1.00 USD", "8", "-1", "8", "-1"},
        };
        ULocale locale = new ULocale("en_US");
        for (int i=0; i<DATA.length; ++i) {
            String stringToBeParsed = DATA[i][0];
            int parsedPosition = Integer.parseInt(DATA[i][1]);
            int errorIndex = Integer.parseInt(DATA[i][2]);
            int currParsedPosition = Integer.parseInt(DATA[i][3]);
            int currErrorIndex = Integer.parseInt(DATA[i][4]);
            NumberFormat numFmt = NumberFormat.getInstance(locale, NumberFormat.CURRENCYSTYLE);
            ParsePosition parsePosition = new ParsePosition(0);
            Number val = numFmt.parse(stringToBeParsed, parsePosition);
            if (parsePosition.getIndex() != parsedPosition ||
                    parsePosition.getErrorIndex() != errorIndex) {
                errln("FAIL: parse failed on case "+i+". expected position: " + parsedPosition +"; actual: " + parsePosition.getIndex());
                errln("FAIL: parse failed on case "+i+". expected error position: " + errorIndex + "; actual: " + parsePosition.getErrorIndex());
            }
            if (parsePosition.getErrorIndex() == -1 &&
                    val.doubleValue() != 1.00) {
                errln("FAIL: parse failed. expected 1.00, actual:" + val);
            }
            parsePosition = new ParsePosition(0);
            CurrencyAmount amt = numFmt.parseCurrency(stringToBeParsed, parsePosition);
            if (parsePosition.getIndex() != currParsedPosition ||
                    parsePosition.getErrorIndex() != currErrorIndex) {
                errln("FAIL: parseCurrency failed on case "+i+". expected error position: " + currErrorIndex + "; actual: " + parsePosition.getErrorIndex());
                errln("FAIL: parseCurrency failed on case "+i+". expected position: " + currParsedPosition +"; actual: " + parsePosition.getIndex());
            }
            if (parsePosition.getErrorIndex() == -1 &&
                    amt.getNumber().doubleValue() != 1.00) {
                errln("FAIL: parseCurrency failed. expected 1.00, actual:" + val);
            }
        }
    }

    @Test
    public void TestParseCurrency() {
        class ParseCurrencyItem {
            private final String localeString;
            private final String descrip;
            private final String currStr;
            private final int    doubExpectPos;
            private final int    doubExpectVal;
            private final int    curExpectPos;
            private final int    curExpectVal;
            private final String curExpectCurr;

            ParseCurrencyItem(String locStr, String desc, String curr, int doubExPos, int doubExVal, int curExPos, int curExVal, String curExCurr) {
                localeString  = locStr;
                descrip       = desc;
                currStr       = curr;
                doubExpectPos  = doubExPos;
                doubExpectVal  = doubExVal;
                curExpectPos  = curExPos;
                curExpectVal  = curExVal;
                curExpectCurr = curExCurr;
            }
            public String getLocaleString()  { return localeString; }
            public String getDescrip()       { return descrip; }
            public String getCurrStr()       { return currStr; }
            public int    getDoubExpectPos()  { return doubExpectPos; }
            public int    getDoubExpectVal()  { return doubExpectVal; }
            public int    getCurExpectPos()  { return curExpectPos; }
            public int    getCurExpectVal()  { return curExpectVal; }
            public String getCurExpectCurr() { return curExpectCurr; }
        }
        // Note: In cases where the number occurs before the currency sign, non-currency mode will parse the number
        // and stop when it reaches the currency symbol.
        final ParseCurrencyItem[] parseCurrencyItems = {
                new ParseCurrencyItem( "en_US", "dollars2", "$2.00",            5,  2,  5,  2,  "USD" ),
                new ParseCurrencyItem( "en_US", "dollars4", "$4",               2,  4,  2,  4,  "USD" ),
                new ParseCurrencyItem( "en_US", "dollars9", "9\u00A0$",         3,  9,  3,  9,  "USD" ),
                new ParseCurrencyItem( "en_US", "pounds3",  "\u00A33.00",       0,  0,  5,  3,  "GBP" ),
                new ParseCurrencyItem( "en_US", "pounds5",  "\u00A35",          0,  0,  2,  5,  "GBP" ),
                new ParseCurrencyItem( "en_US", "pounds7",  "7\u00A0\u00A3",    1,  7,  3,  7,  "GBP" ),
                new ParseCurrencyItem( "en_US", "euros8",   "\u20AC8",          0,  0,  2,  8,  "EUR" ),

                new ParseCurrencyItem( "en_GB", "pounds3",  "\u00A33.00",       5,  3,  5,  3,  "GBP" ),
                new ParseCurrencyItem( "en_GB", "pounds5",  "\u00A35",          2,  5,  2,  5,  "GBP" ),
                new ParseCurrencyItem( "en_GB", "pounds7",  "7\u00A0\u00A3",    3,  7,  3,  7,  "GBP" ),
                new ParseCurrencyItem( "en_GB", "euros4",   "4,00\u00A0\u20AC", 4,400,  6,400,  "EUR" ),
                new ParseCurrencyItem( "en_GB", "euros6",   "6\u00A0\u20AC",    1,  6,  3,  6,  "EUR" ),
                new ParseCurrencyItem( "en_GB", "euros8",   "\u20AC8",          0,  0,  2,  8,  "EUR" ),
                new ParseCurrencyItem( "en_GB", "dollars4", "US$4",             0,  0,  4,  4,  "USD" ),

                new ParseCurrencyItem( "fr_FR", "euros4",   "4,00\u00A0\u20AC", 6,  4,  6,  4,  "EUR" ),
                new ParseCurrencyItem( "fr_FR", "euros6",   "6\u00A0\u20AC",    3,  6,  3,  6,  "EUR" ),
                new ParseCurrencyItem( "fr_FR", "euros8",   "\u20AC8",          2,  8,  2,  8,  "EUR" ),
                new ParseCurrencyItem( "fr_FR", "dollars2", "$2.00",            0,  0,  0,  0,  ""    ),
                new ParseCurrencyItem( "fr_FR", "dollars4", "$4",               0,  0,  0,  0,  ""    ),
        };
        for (ParseCurrencyItem item: parseCurrencyItems) {
            String localeString = item.getLocaleString();
            ULocale uloc = new ULocale(localeString);
            NumberFormat fmt = null;
            try {
                fmt = NumberFormat.getCurrencyInstance(uloc);
            } catch (Exception e) {
                errln("NumberFormat.getCurrencyInstance fails for locale " + localeString);
                continue;
            }
            String currStr = item.getCurrStr();
            ParsePosition parsePos = new ParsePosition(0);

            Number numVal = fmt.parse(currStr, parsePos);
            if ( parsePos.getIndex() != item.getDoubExpectPos() || (numVal != null && numVal.intValue() != item.getDoubExpectVal()) ) {
                if (numVal != null) {
                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() +
                            ", expect pos/val " + item.getDoubExpectPos() + "/" + item.getDoubExpectVal() +
                            ", get " + parsePos.getIndex() + "/" + numVal.intValue() );
                } else {
                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() +
                            ", expect pos/val " + item.getDoubExpectPos() + "/" + item.getDoubExpectVal() +
                            ", get " + parsePos.getIndex() + "/(NULL)" );
                }
            }

            parsePos.setIndex(0);
            int curExpectPos = item.getCurExpectPos();
            CurrencyAmount currAmt = fmt.parseCurrency(currStr, parsePos);
            if ( parsePos.getIndex() != curExpectPos || (currAmt != null && (currAmt.getNumber().intValue() != item.getCurExpectVal() ||
                    currAmt.getCurrency().getCurrencyCode().compareTo(item.getCurExpectCurr()) != 0)) ) {
                if (currAmt != null) {
                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() +
                            ", expect pos/val/curr " + curExpectPos + "/" + item.getCurExpectVal() + "/" + item.getCurExpectCurr() +
                            ", get " + parsePos.getIndex() + "/" + currAmt.getNumber().intValue() + "/" + currAmt.getCurrency().getCurrencyCode() );
                } else {
                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() +
                            ", expect pos/val/curr " + curExpectPos + "/" + item.getCurExpectVal() + "/" + item.getCurExpectCurr() +
                            ", get " + parsePos.getIndex() + "/(NULL)" );
                }
            }
        }
    }

    @Test
    public void TestParseCurrencyWithWhitespace() {
        DecimalFormat df = new DecimalFormat("#,##0.00 ¤¤");
        ParsePosition ppos = new ParsePosition(0);
        df.parseCurrency("1.00 us denmark", ppos);
        assertEquals("Expected to fail on 'us denmark' string", 4, ppos.getErrorIndex());
    }

    @Test
    public void TestParseCurrPatternWithDecStyle() {
        String currpat = "¤#,##0.00";
        String parsetxt = "x0y$";
        DecimalFormat decfmt = (DecimalFormat)NumberFormat.getInstance(new ULocale("en_US"), NumberFormat.NUMBERSTYLE);
        decfmt.applyPattern(currpat);
        ParsePosition ppos = new ParsePosition(0);
        Number value = decfmt.parse(parsetxt, ppos);
        if (ppos.getIndex() != 0) {
            errln("DecimalFormat.parse expected to fail but got ppos " + ppos.getIndex() + ", value " + value);
        }
    }

    /**
     * Test the Currency object handling, new as of ICU 2.2.
     */
    @Test
    public void TestCurrencyObject() {
        NumberFormat fmt =
                NumberFormat.getCurrencyInstance(Locale.US);

        expectCurrency(fmt, null, 1234.56, "$1,234.56");

        expectCurrency(fmt, Currency.getInstance(Locale.FRANCE),
                1234.56, "\u20AC1,234.56"); // Euro

        expectCurrency(fmt, Currency.getInstance(Locale.JAPAN),
                1234.56, "\u00A51,235"); // Yen

        expectCurrency(fmt, Currency.getInstance(new Locale("fr", "CH", "")),
                1234.56, "CHF 1,234.56"); // no more 0.05 rounding here, see cldrbug 5548

        expectCurrency(fmt, Currency.getInstance(Locale.US),
                1234.56, "$1,234.56");

        fmt = NumberFormat.getCurrencyInstance(Locale.FRANCE);

        expectCurrency(fmt, null, 1234.56, "1\u202F234,56 \u20AC");

        expectCurrency(fmt, Currency.getInstance(Locale.JAPAN),
                1234.56, "1\u202F235 JPY"); // Yen

        expectCurrency(fmt, Currency.getInstance(new Locale("fr", "CH", "")),
                1234.56, "1\u202F234,56 CHF"); // no more rounding here, see cldrbug 5548

        expectCurrency(fmt, Currency.getInstance(Locale.US),
                1234.56, "1\u202F234,56 $US");

        expectCurrency(fmt, Currency.getInstance(Locale.FRANCE),
                1234.56, "1\u202F234,56 \u20AC"); // Euro
    }

    @Test
    public void TestCompatibleCurrencies() {
        NumberFormat fmt =
                NumberFormat.getCurrencyInstance(Locale.US);
        expectParseCurrency(fmt, Currency.getInstance(Locale.JAPAN), "\u00A51,235"); // Yen half-width
        expectParseCurrency(fmt, Currency.getInstance(Locale.JAPAN), "\uFFE51,235"); // Yen full-wdith
    }

    @Test
    public void TestCurrencyPatterns() {
        int i;
        Random rnd = new Random(2017);
        Locale[] locs = NumberFormat.getAvailableLocales();
        for (i=0; i<locs.length; ++i) {
            if (rnd.nextDouble() < 0.9) {
                // Check a random subset for speed:
                // Otherwise, this test takes a large fraction of the entire time.
                continue;
            }
            NumberFormat nf = NumberFormat.getCurrencyInstance(locs[i]);
            // Make sure currency formats do not have a variable number
            // of fraction digits
            int min = nf.getMinimumFractionDigits();
            int max = nf.getMaximumFractionDigits();
            if (min != max) {
                String a = nf.format(1.0);
                String b = nf.format(1.125);
                errln("FAIL: " + locs[i] +
                        " min fraction digits != max fraction digits; "+
                        "x 1.0 => " + a +
                        "; x 1.125 => " + b);
            }

            // Make sure EURO currency formats have exactly 2 fraction digits
            if (nf instanceof DecimalFormat) {
                Currency curr = ((DecimalFormat) nf).getCurrency();
                if (curr != null && "EUR".equals(curr.getCurrencyCode())) {
                    if (min != 2 || max != 2) {
                        String a = nf.format(1.0);
                        errln("FAIL: " + locs[i] +
                                " is a EURO format but it does not have 2 fraction digits; "+
                                "x 1.0 => " +
                                a);
                    }
                }
            }
        }
    }

    /**
     * Do rudimentary testing of parsing.
     */
    @Test
    public void TestParse() {
        String arg = "0.0";
        DecimalFormat format = new DecimalFormat("00");
        double aNumber = 0l;
        try {
            aNumber = format.parse(arg).doubleValue();
        } catch (ParseException e) {
            System.out.println(e);
        }
        logln("parse(" + arg + ") = " + aNumber);
    }

    /**
     * Test proper rounding by the format method.
     */
    @Test
    public void TestRounding487() {

        NumberFormat nf = NumberFormat.getInstance();
        roundingTest(nf, 0.00159999, 4, "0.0016");
        roundingTest(nf, 0.00995, 4, "0.01");

        roundingTest(nf, 12.3995, 3, "12.4");

        roundingTest(nf, 12.4999, 0, "12");
        roundingTest(nf, - 19.5, 0, "-20");

    }

    /**
     * Test the functioning of the secondary grouping value.
     */
    @Test
    public void TestSecondaryGrouping() {

        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        DecimalFormat f = new DecimalFormat("#,##,###", US);

        expect(f, 123456789L, "12,34,56,789");
        expectPat(f, "#,##,##0");
        f.applyPattern("#,###");

        f.setSecondaryGroupingSize(4);
        expect(f, 123456789L, "12,3456,789");
        expectPat(f, "#,####,##0");
        NumberFormat g = NumberFormat.getInstance(new Locale("hi", "IN"));

        String out = "";
        long l = 1876543210L;
        out = g.format(l);

        // expect "1,87,65,43,210", but with Hindi digits
        //         01234567890123
        boolean ok = true;
        if (out.length() != 14) {
            ok = false;
        } else {
            for (int i = 0; i < out.length(); ++i) {
                boolean expectGroup = false;
                switch (i) {
                case 1 :
                case 4 :
                case 7 :
                case 10 :
                    expectGroup = true;
                    break;
                }
                // Later -- fix this to get the actual grouping
                // character from the resource bundle.
                boolean isGroup = (out.charAt(i) == 0x002C);
                if (isGroup != expectGroup) {
                    ok = false;
                    break;
                }
            }
        }
        if (!ok) {
            errln("FAIL  Expected "+ l + " x hi_IN . \"1,87,65,43,210\" (with Hindi digits), got \""
                    + out + "\"");
        } else {
            logln("Ok    " + l + " x hi_IN . \"" + out + "\"");
        }
    }

    /*
     * Internal test utility.
     */
    private void roundingTest(NumberFormat nf, double x, int maxFractionDigits, final String expected) {
        nf.setMaximumFractionDigits(maxFractionDigits);
        String out = nf.format(x);
        logln(x + " formats with " + maxFractionDigits + " fractional digits to " + out);
        if (!out.equals(expected))
            errln("FAIL: Expected " + expected);
    }

    /**
     * Upgrade to alphaWorks
     */
    @Test
    public void TestExponent() {
        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt1 = new DecimalFormat("0.###E0", US);
        DecimalFormat fmt2 = new DecimalFormat("0.###E+0", US);
        int n = 1234;
        expect2(fmt1, n, "1.234E3");
        expect2(fmt2, n, "1.234E+3");
        expect(fmt1, "1.234E+3", n); // Either format should parse "E+3"

    }

    /**
     * Upgrade to alphaWorks
     */
    @Test
    public void TestScientific() {

        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);

        // Test pattern round-trip
        final String PAT[] = { "#E0", "0.####E0", "00.000E00", "##0.####E000", "0.###E0;[0.###E0]" };
        int PAT_length = PAT.length;
        int DIGITS[] = {
                // min int, max int, min frac, max frac
                0, 1, 0, 0, // "#E0"
                1, 1, 0, 4, // "0.####E0"
                2, 2, 3, 3, // "00.000E00"
                1, 3, 0, 4, // "##0.####E000"
                1, 1, 0, 3, // "0.###E0;[0.###E0]"
        };
        for (int i = 0; i < PAT_length; ++i) {
            String pat = PAT[i];
            DecimalFormat df = new DecimalFormat(pat, US);
            String pat2 = df.toPattern();
            if (pat.equals(pat2)) {
                logln("Ok   Pattern rt \"" + pat + "\" . \"" + pat2 + "\"");
            } else {
                errln("FAIL Pattern rt \"" + pat + "\" . \"" + pat2 + "\"");
            }
            // Make sure digit counts match what we expect
            if (i == 0) continue; // outputs to 1,1,0,0 since at least one min digit is required.
            if (df.getMinimumIntegerDigits() != DIGITS[4 * i]
                    || df.getMaximumIntegerDigits() != DIGITS[4 * i + 1]
                            || df.getMinimumFractionDigits() != DIGITS[4 * i + 2]
                                    || df.getMaximumFractionDigits() != DIGITS[4 * i + 3]) {
                errln("FAIL \""+ pat+ "\" min/max int; min/max frac = "
                        + df.getMinimumIntegerDigits() + "/"
                        + df.getMaximumIntegerDigits() + ";"
                        + df.getMinimumFractionDigits() + "/"
                        + df.getMaximumFractionDigits() + ", expect "
                        + DIGITS[4 * i] + "/"
                        + DIGITS[4 * i + 1] + ";"
                        + DIGITS[4 * i + 2] + "/"
                        + DIGITS[4 * i + 3]);
            }
        }

        expect2(new DecimalFormat("#E0", US), 12345.0, "1.2345E4");
        expect(new DecimalFormat("0E0", US), 12345.0, "1E4");

        // pattern of NumberFormat.getScientificInstance(Locale.US) = "0.######E0" not "#E0"
        // so result = 1.234568E4 not 1.2345678901E4
        //when the pattern problem is finalized, delete comment mark'//'
        //of the following code
        expect2(NumberFormat.getScientificInstance(Locale.US), 12345.678901, "1.2345678901E4");
        logln("Testing NumberFormat.getScientificInstance(ULocale) ...");
        expect2(NumberFormat.getScientificInstance(ULocale.US), 12345.678901, "1.2345678901E4");

        expect(new DecimalFormat("##0.###E0", US), 12345.0, "12.34E3");
        expect(new DecimalFormat("##0.###E0", US), 12345.00001, "12.35E3");
        expect2(new DecimalFormat("##0.####E0", US), 12345, "12.345E3");

        // pattern of NumberFormat.getScientificInstance(Locale.US) = "0.######E0" not "#E0"
        // so result = 1.234568E4 not 1.2345678901E4
        expect2(NumberFormat.getScientificInstance(Locale.FRANCE), 12345.678901, "1,2345678901E4");
        logln("Testing NumberFormat.getScientificInstance(ULocale) ...");
        expect2(NumberFormat.getScientificInstance(ULocale.FRANCE), 12345.678901, "1,2345678901E4");

        expect(new DecimalFormat("##0.####E0", US), 789.12345e-9, "789.12E-9");
        expect2(new DecimalFormat("##0.####E0", US), 780.e-9, "780E-9");
        expect(new DecimalFormat(".###E0", US), 45678.0, ".457E5");
        expect2(new DecimalFormat(".###E0", US), 0, ".0E0");
        /*
        expect(new DecimalFormat[] { new DecimalFormat("#E0", US),
                                     new DecimalFormat("##E0", US),
                                     new DecimalFormat("####E0", US),
                                     new DecimalFormat("0E0", US),
                                     new DecimalFormat("00E0", US),
                                     new DecimalFormat("000E0", US),
                                   },
               new Long(45678000),
               new String[] { "4.5678E7",
                              "45.678E6",
                              "4567.8E4",
                              "5E7",
                              "46E6",
                              "457E5",
                            }
               );
        !
        ! Unroll this test into individual tests below...
        !
         */
        expect2(new DecimalFormat("#E0", US), 45678000, "4.5678E7");
        expect2(new DecimalFormat("##E0", US), 45678000, "45.678E6");
        expect2(new DecimalFormat("####E0", US), 45678000, "4567.8E4");
        expect(new DecimalFormat("0E0", US), 45678000, "5E7");
        expect(new DecimalFormat("00E0", US), 45678000, "46E6");
        expect(new DecimalFormat("000E0", US), 45678000, "457E5");
        /*
        expect(new DecimalFormat("###E0", US, status),
               new Object[] { new Double(0.0000123), "12.3E-6",
                              new Double(0.000123), "123E-6",
                              new Double(0.00123), "1.23E-3",
                              new Double(0.0123), "12.3E-3",
                              new Double(0.123), "123E-3",
                              new Double(1.23), "1.23E0",
                              new Double(12.3), "12.3E0",
                              new Double(123), "123E0",
                              new Double(1230), "1.23E3",
                             });
        !
        ! Unroll this test into individual tests below...
        !
         */
        expect2(new DecimalFormat("###E0", US), 0.0000123, "12.3E-6");
        expect2(new DecimalFormat("###E0", US), 0.000123, "123E-6");
        expect2(new DecimalFormat("###E0", US), 0.00123, "1.23E-3");
        expect2(new DecimalFormat("###E0", US), 0.0123, "12.3E-3");
        expect2(new DecimalFormat("###E0", US), 0.123, "123E-3");
        expect2(new DecimalFormat("###E0", US), 1.23, "1.23E0");
        expect2(new DecimalFormat("###E0", US), 12.3, "12.3E0");
        expect2(new DecimalFormat("###E0", US), 123.0, "123E0");
        expect2(new DecimalFormat("###E0", US), 1230.0, "1.23E3");
        /*
        expect(new DecimalFormat("0.#E+00", US, status),
               new Object[] { new Double(0.00012), "1.2E-04",
                              new Long(12000),     "1.2E+04",
                             });
        !
        ! Unroll this test into individual tests below...
        !
         */
        expect2(new DecimalFormat("0.#E+00", US), 0.00012, "1.2E-04");
        expect2(new DecimalFormat("0.#E+00", US), 12000, "1.2E+04");
    }

    /**
     * Upgrade to alphaWorks
     */
    @Test
    public void TestPad() {

        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        expect2(new DecimalFormat("*^##.##", US), 0, "^^^^0");
        expect2(new DecimalFormat("*^##.##", US), -1.3, "^-1.3");
        expect2(
                new DecimalFormat("##0.0####E0*_ 'g-m/s^2'", US),
                0,
                "0.0E0______ g-m/s^2");
        expect(
                new DecimalFormat("##0.0####E0*_ 'g-m/s^2'", US),
                1.0 / 3,
                "333.333E-3_ g-m/s^2");
        expect2(new DecimalFormat("##0.0####*_ 'g-m/s^2'", US), 0, "0.0______ g-m/s^2");
        expect(
                new DecimalFormat("##0.0####*_ 'g-m/s^2'", US),
                1.0 / 3,
                "0.33333__ g-m/s^2");

        // Test padding before a sign
        final String formatStr = "*x#,###,###,##0.0#;*x(###,###,##0.0#)";
        expect2(new DecimalFormat(formatStr, US), -10, "xxxxxxxxxx(10.0)");
        expect2(new DecimalFormat(formatStr, US), -1000, "xxxxxxx(1,000.0)");
        expect2(new DecimalFormat(formatStr, US), -1000000, "xxx(1,000,000.0)");
        expect2(new DecimalFormat(formatStr, US), -100.37, "xxxxxxxx(100.37)");
        expect2(new DecimalFormat(formatStr, US), -10456.37, "xxxxx(10,456.37)");
        expect2(new DecimalFormat(formatStr, US), -1120456.37, "xx(1,120,456.37)");
        expect2(new DecimalFormat(formatStr, US), -112045600.37, "(112,045,600.37)");
        expect2(new DecimalFormat(formatStr, US), -1252045600.37, "(1,252,045,600.37)");

        expect2(new DecimalFormat(formatStr, US), 10, "xxxxxxxxxxxx10.0");
        expect2(new DecimalFormat(formatStr, US), 1000, "xxxxxxxxx1,000.0");
        expect2(new DecimalFormat(formatStr, US), 1000000, "xxxxx1,000,000.0");
        expect2(new DecimalFormat(formatStr, US), 100.37, "xxxxxxxxxx100.37");
        expect2(new DecimalFormat(formatStr, US), 10456.37, "xxxxxxx10,456.37");
        expect2(new DecimalFormat(formatStr, US), 1120456.37, "xxxx1,120,456.37");
        expect2(new DecimalFormat(formatStr, US), 112045600.37, "xx112,045,600.37");
        expect2(new DecimalFormat(formatStr, US), 10252045600.37, "10,252,045,600.37");

        // Test padding between a sign and a number
        final String formatStr2 = "#,###,###,##0.0#*x;(###,###,##0.0#*x)";
        expect2(new DecimalFormat(formatStr2, US), -10, "(10.0xxxxxxxxxx)");
        expect2(new DecimalFormat(formatStr2, US), -1000, "(1,000.0xxxxxxx)");
        expect2(new DecimalFormat(formatStr2, US), -1000000, "(1,000,000.0xxx)");
        expect2(new DecimalFormat(formatStr2, US), -100.37, "(100.37xxxxxxxx)");
        expect2(new DecimalFormat(formatStr2, US), -10456.37, "(10,456.37xxxxx)");
        expect2(new DecimalFormat(formatStr2, US), -1120456.37, "(1,120,456.37xx)");
        expect2(new DecimalFormat(formatStr2, US), -112045600.37, "(112,045,600.37)");
        expect2(new DecimalFormat(formatStr2, US), -1252045600.37, "(1,252,045,600.37)");

        expect2(new DecimalFormat(formatStr2, US), 10, "10.0xxxxxxxxxxxx");
        expect2(new DecimalFormat(formatStr2, US), 1000, "1,000.0xxxxxxxxx");
        expect2(new DecimalFormat(formatStr2, US), 1000000, "1,000,000.0xxxxx");
        expect2(new DecimalFormat(formatStr2, US), 100.37, "100.37xxxxxxxxxx");
        expect2(new DecimalFormat(formatStr2, US), 10456.37, "10,456.37xxxxxxx");
        expect2(new DecimalFormat(formatStr2, US), 1120456.37, "1,120,456.37xxxx");
        expect2(new DecimalFormat(formatStr2, US), 112045600.37, "112,045,600.37xx");
        expect2(new DecimalFormat(formatStr2, US), 10252045600.37, "10,252,045,600.37");

        //testing the setPadCharacter(UnicodeString) and getPadCharacterString()
        DecimalFormat fmt = new DecimalFormat("#", US);
        char padString = 'P';
        fmt.setPadCharacter(padString);
        expectPad(fmt, "*P##.##", DecimalFormat.PAD_BEFORE_PREFIX, 5, padString);
        fmt.setPadCharacter('^');
        expectPad(fmt, "*^#", DecimalFormat.PAD_BEFORE_PREFIX, 1, '^');
        //commented untill implementation is complete
        /*  fmt.setPadCharacter((UnicodeString)"^^^");
          expectPad(fmt, "*^^^#", DecimalFormat.kPadBeforePrefix, 3, (UnicodeString)"^^^");
          padString.remove();
          padString.append((UChar)0x0061);
          padString.append((UChar)0x0302);
          fmt.setPadCharacter(padString);
          UChar patternChars[]={0x002a, 0x0061, 0x0302, 0x0061, 0x0302, 0x0023, 0x0000};
          UnicodeString pattern(patternChars);
          expectPad(fmt, pattern , DecimalFormat.kPadBeforePrefix, 4, padString);
         */

        // Test multi-char padding sequence specified via pattern
        expect2(new DecimalFormat("*'😃'####.00", US), 1.1, "😃😃😃1.10");
    }

    @Test
    public void TestIgnorePadding() {
        DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt = new DecimalFormat("", dfs);
        fmt.setGroupingUsed(false);
        fmt.setFormatWidth(0);
        fmt.setPadCharacter('*');
        fmt.setPadPosition(0);
        fmt.setMinimumIntegerDigits(0);
        fmt.setMaximumIntegerDigits(8);
        fmt.setMinimumFractionDigits(0);
        fmt.setMaximumFractionDigits(0);
        String pattern = fmt.toPattern();
        if (pattern.startsWith("*")) {
            errln("ERROR toPattern result should ignore padding but get \"" + pattern + "\"");
        }
        fmt.applyPattern(pattern);
        String format = fmt.format(24);
        if (!format.equals("24")) {
             errln("ERROR format result expect 24 but get \"" + format + "\"");
       }
    }

    /**
     * Upgrade to alphaWorks
     */
    @Test
    public void TestPatterns2() {
        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt = new DecimalFormat("#", US);

        char hat = 0x005E; /*^*/

        expectPad(fmt, "*^#", DecimalFormat.PAD_BEFORE_PREFIX, 1, hat);
        expectPad(fmt, "$*^#", DecimalFormat.PAD_AFTER_PREFIX, 2, hat);
        expectPad(fmt, "#*^", DecimalFormat.PAD_BEFORE_SUFFIX, 1, hat);
        expectPad(fmt, "#$*^", DecimalFormat.PAD_AFTER_SUFFIX, 2, hat);
        expectPad(fmt, "$*^$#", -1);
        expectPad(fmt, "#$*^$", -1);
        expectPad(fmt, "'pre'#,##0*x'post'", DecimalFormat.PAD_BEFORE_SUFFIX, 12, (char) 0x0078 /*x*/);
        expectPad(fmt, "''#0*x", DecimalFormat.PAD_BEFORE_SUFFIX, 3, (char) 0x0078 /*x*/);
        expectPad(fmt, "'I''ll'*a###.##", DecimalFormat.PAD_AFTER_PREFIX, 10, (char) 0x0061 /*a*/);

        fmt.applyPattern("AA#,##0.00ZZ");
        fmt.setPadCharacter(hat);

        fmt.setFormatWidth(10);

        fmt.setPadPosition(DecimalFormat.PAD_BEFORE_PREFIX);
        expectPat(fmt, "*^AA#,##0.00ZZ");

        fmt.setPadPosition(DecimalFormat.PAD_BEFORE_SUFFIX);
        expectPat(fmt, "AA#,##0.00*^ZZ");

        fmt.setPadPosition(DecimalFormat.PAD_AFTER_SUFFIX);
        expectPat(fmt, "AA#,##0.00ZZ*^");

        //            12  3456789012
        String exp = "AA*^#,##0.00ZZ";
        fmt.setFormatWidth(12);
        fmt.setPadPosition(DecimalFormat.PAD_AFTER_PREFIX);
        expectPat(fmt, exp);

        fmt.setFormatWidth(13);
        //              12  34567890123
        expectPat(fmt, "AA*^##,##0.00ZZ");

        fmt.setFormatWidth(14);
        //              12  345678901234
        expectPat(fmt, "AA*^###,##0.00ZZ");

        fmt.setFormatWidth(15);
        //              12  3456789012345
        expectPat(fmt, "AA*^####,##0.00ZZ"); // This is the interesting case

        // The new implementation produces "AA*^#####,##0.00ZZ", which is functionally equivalent
        // to what the old implementation produced, "AA*^#,###,##0.00ZZ"
        fmt.setFormatWidth(16);
        //              12  34567890123456
        //expectPat(fmt, "AA*^#,###,##0.00ZZ");
        expectPat(fmt, "AA*^#####,##0.00ZZ");
    }

    @Test
    public void TestRegistration() {
        final ULocale SRC_LOC = ULocale.FRANCE;
        final ULocale SWAP_LOC = ULocale.US;

        class TestFactory extends SimpleNumberFormatFactory {
            NumberFormat currencyStyle;

            TestFactory() {
                super(SRC_LOC, true);
                currencyStyle = NumberFormat.getIntegerInstance(SWAP_LOC);
            }

            @Override
            public NumberFormat createFormat(ULocale loc, int formatType) {
                if (formatType == FORMAT_CURRENCY) {
                    return currencyStyle;
                }
                return null;
            }
        }

        NumberFormat f0 = NumberFormat.getIntegerInstance(SWAP_LOC);
        NumberFormat f1 = NumberFormat.getIntegerInstance(SRC_LOC);
        NumberFormat f2 = NumberFormat.getCurrencyInstance(SRC_LOC);
        Object key = NumberFormat.registerFactory(new TestFactory());
        NumberFormat f3 = NumberFormat.getCurrencyInstance(SRC_LOC);
        NumberFormat f4 = NumberFormat.getIntegerInstance(SRC_LOC);
        NumberFormat.unregister(key); // restore for other tests
        NumberFormat f5 = NumberFormat.getCurrencyInstance(SRC_LOC);

        float n = 1234.567f;
        logln("f0 swap int: " + f0.format(n));
        logln("f1 src int: " + f1.format(n));
        logln("f2 src cur: " + f2.format(n));
        logln("f3 reg cur: " + f3.format(n));
        logln("f4 reg int: " + f4.format(n));
        logln("f5 unreg cur: " + f5.format(n));

        if (!f3.format(n).equals(f0.format(n))) {
            errln("registered service did not match");
        }
        if (!f4.format(n).equals(f1.format(n))) {
            errln("registered service did not inherit");
        }
        if (!f5.format(n).equals(f2.format(n))) {
            errln("unregistered service did not match original");
        }
    }

    @Test
    public void TestScientific2() {
        // jb 2552
        DecimalFormat fmt = (DecimalFormat)NumberFormat.getCurrencyInstance();
        Number num = new Double(12.34);
        expect(fmt, num, "$12.34");
        fmt.setScientificNotation(true);
        expect(fmt, num, "$1.23E1");
        fmt.setScientificNotation(false);
        expect(fmt, num, "$12.34");
    }

    @Test
    public void TestScientificGrouping() {
        // jb 2552
        DecimalFormat fmt = new DecimalFormat("###.##E0");
        expect(fmt, .01234, "12.3E-3");
        expect(fmt, .1234, "123E-3");
        expect(fmt, 1.234, "1.23E0");
        expect(fmt, 12.34, "12.3E0");
        expect(fmt, 123.4, "123E0");
        expect(fmt, 1234, "1.23E3");
    }

    // additional coverage tests

    // sigh, can't have static inner classes, why not?

    static final class PI extends Number {
        /**
         * For serialization
         */
        private static final long serialVersionUID = -305601227915602172L;

        private PI() {}
        @Override
        public int intValue() { return (int)Math.PI; }
        @Override
        public long longValue() { return (long)Math.PI; }
        @Override
        public float  floatValue() { return (float)Math.PI; }
        @Override
        public double doubleValue() { return Math.PI; }
        @Override
        public byte byteValue() { return (byte)Math.PI; }
        @Override
        public short shortValue() { return (short)Math.PI; }

        public static final Number INSTANCE = new PI();
    }

    @Test
    public void TestCoverage() {
        NumberFormat fmt = NumberFormat.getNumberInstance(); // default locale
        logln(fmt.format(new BigInteger("1234567890987654321234567890987654321", 10)));

        fmt = NumberFormat.getScientificInstance(); // default locale

        logln(fmt.format(PI.INSTANCE));

        try {
            logln(fmt.format("12345"));
            errln("numberformat of string did not throw exception");
        }
        catch (Exception e) {
            logln("PASS: numberformat of string failed as expected");
        }

        int hash = fmt.hashCode();
        logln("hash code " + hash);

        logln("compare to string returns: " + fmt.equals(""));

        // For ICU 2.6 - alan
        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        DecimalFormat df = new DecimalFormat("'*&'' '\u00A4' ''&*' #,##0.00", US);
        df.setCurrency(Currency.getInstance("INR"));
        expect2(df, 1.0, "*&' \u20B9 '&* 1.00");
        expect2(df, -2.0, "-*&' \u20B9 '&* 2.00");
        df.applyPattern("#,##0.00 '*&'' '\u00A4' ''&*'");
        expect2(df, 2.0, "2.00 *&' \u20B9 '&*");
        expect2(df, -1.0, "-1.00 *&' \u20B9 '&*");

        java.math.BigDecimal r;

        r = df.getRoundingIncrement();
        if (r != null) {
            errln("FAIL: rounding = " + r + ", expect null");
        }

        if (df.isScientificNotation()) {
            errln("FAIL: isScientificNotation = true, expect false");
        }

        // Create a new instance to flush out currency info
        df = new DecimalFormat("0.00000", US);
        df.setScientificNotation(true);
        if (!df.isScientificNotation()) {
            errln("FAIL: isScientificNotation = false, expect true");
        }
        df.setMinimumExponentDigits((byte)2);
        if (df.getMinimumExponentDigits() != 2) {
            errln("FAIL: getMinimumExponentDigits = " +
                    df.getMinimumExponentDigits() + ", expect 2");
        }
        df.setExponentSignAlwaysShown(true);
        if (!df.isExponentSignAlwaysShown()) {
            errln("FAIL: isExponentSignAlwaysShown = false, expect true");
        }
        df.setSecondaryGroupingSize(0);
        if (df.getSecondaryGroupingSize() != 0) {
            errln("FAIL: getSecondaryGroupingSize = " +
                    df.getSecondaryGroupingSize() + ", expect 0");
        }
        expect2(df, 3.14159, "3.14159E+00");

        // DecimalFormatSymbols#getInstance
        DecimalFormatSymbols decsym1 = DecimalFormatSymbols.getInstance();
        DecimalFormatSymbols decsym2 = new DecimalFormatSymbols();
        if (!decsym1.equals(decsym2)) {
            errln("FAIL: DecimalFormatSymbols returned by getInstance()" +
                    "does not match new DecimalFormatSymbols().");
        }
        decsym1 = DecimalFormatSymbols.getInstance(Locale.JAPAN);
        decsym2 = DecimalFormatSymbols.getInstance(ULocale.JAPAN);
        if (!decsym1.equals(decsym2)) {
            errln("FAIL: DecimalFormatSymbols returned by getInstance(Locale.JAPAN)" +
                    "does not match the one returned by getInstance(ULocale.JAPAN).");
        }

        // DecimalFormatSymbols#getAvailableLocales/#getAvailableULocales
        Locale[] allLocales = DecimalFormatSymbols.getAvailableLocales();
        if (allLocales.length == 0) {
            errln("FAIL: Got a empty list for DecimalFormatSymbols.getAvailableLocales");
        } else {
            logln("PASS: " + allLocales.length +
                    " available locales returned by DecimalFormatSymbols.getAvailableLocales");
        }
        ULocale[] allULocales = DecimalFormatSymbols.getAvailableULocales();
        if (allULocales.length == 0) {
            errln("FAIL: Got a empty list for DecimalFormatSymbols.getAvailableLocales");
        } else {
            logln("PASS: " + allULocales.length +
                    " available locales returned by DecimalFormatSymbols.getAvailableULocales");
        }
    }

    @Test
    public void TestLocalizedPatternSymbolCoverage() {
        String[] standardPatterns = { "#,##0.05+%;#,##0.05-%", "* @@@E0‰" };
        String[] localizedPatterns = { "▰⁖▰▰໐⁘໐໕†⁜⁙▰⁖▰▰໐⁘໐໕‡⁜", "⁂ ⁕⁕⁕⁑⁑໐‱" };

        DecimalFormatSymbols dfs = new DecimalFormatSymbols();
        dfs.setGroupingSeparator('⁖');
        dfs.setDecimalSeparator('⁘');
        dfs.setPatternSeparator('⁙');
        dfs.setDigit('▰');
        dfs.setZeroDigit('໐');
        dfs.setSignificantDigit('⁕');
        dfs.setPlusSign('†');
        dfs.setMinusSign('‡');
        dfs.setPercent('⁜');
        dfs.setPerMill('‱');
        dfs.setExponentSeparator("⁑⁑"); // tests multi-char sequence
        dfs.setPadEscape('⁂');

        for (int i=0; i<2; i++) {
            String standardPattern = standardPatterns[i];
            String localizedPattern = localizedPatterns[i];

            DecimalFormat df1 = new DecimalFormat("#", dfs);
            df1.applyPattern(standardPattern);
            DecimalFormat df2 = new DecimalFormat("#", dfs);
            df2.applyLocalizedPattern(localizedPattern);
            assertEquals("DecimalFormat instances should be equal",
                    df1, df2);
            assertEquals("toPattern should match on localizedPattern instance",
                    standardPattern, df2.toPattern());
            assertEquals("toLocalizedPattern should match on standardPattern instance",
                    localizedPattern, df1.toLocalizedPattern());
        }
    }

    @Test
    public void TestParseEmpty(){
        String parsetxt = "";
        NumberFormat numfmt = NumberFormat.getInstance(new ULocale("en_US"), NumberFormat.NUMBERSTYLE);
        ParsePosition ppos = new ParsePosition(0);
        Number value = null;
        try {
            value = numfmt.parse(parsetxt, ppos);
            if (value==null) {
                logln("NumberFormat.parse empty string succeeds (no exception) with null return as expected, ppos " + ppos.getIndex());
            } else {
                errln("NumberFormat.parse empty string succeeds (no exception) but returns non-null value " + value + ", ppos " + ppos.getIndex());
            }
        } catch (IllegalArgumentException e){
            errln("NumberFormat.parse empty string throws IllegalArgumentException");
        }
     }

    @Test
    public void TestParseNull() throws ParseException {
        DecimalFormat df = new DecimalFormat();
        try {
            df.parse(null);
            fail("df.parse(null) didn't throw an exception");
        } catch (IllegalArgumentException e){}
        try {
            df.parse(null, null);
            fail("df.parse(null) didn't throw an exception");
        } catch (IllegalArgumentException e){}
        try {
            df.parseCurrency(null, null);
            fail("df.parse(null) didn't throw an exception");
        } catch (IllegalArgumentException e){}
    }

    @Test
    public void TestWhiteSpaceParsing() {
        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt = new DecimalFormat("a  b#0c  ", US);
        int n = 1234;
        expect(fmt, "a b1234c ", n);
        expect(fmt, "a   b1234c   ", n);
        expect(fmt, "ab1234", n);

        fmt.applyPattern("a b #");
        expect(fmt, "ab1234", n);
        expect(fmt, "ab  1234", n);
        expect(fmt, "a b1234", n);
        expect(fmt, "a   b1234", n);
        expect(fmt, " a b 1234", n);

        // Horizontal whitespace is allowed, but not vertical whitespace.
        expect(fmt, "\ta\u00A0b\u20001234", n);
        expect(fmt, "a   \u200A    b1234", n);
        expectParseException(fmt, "\nab1234", n);
        expectParseException(fmt, "a    \n   b1234", n);
        expectParseException(fmt, "a    \u0085   b1234", n);
        expectParseException(fmt, "a    \u2028   b1234", n);

        // Test all characters in the UTS 18 "blank" set stated in the API docstring.
        UnicodeSet blanks = new UnicodeSet("[[:Zs:][\\u0009]]").freeze();
        for (String space : blanks) {
            String str = "a " + space + " b1234c  ";
            expect(fmt, str, n);
        }

        // Arbitrary whitespace is not accepted in strict mode.
        fmt.setParseStrict(true);
        for (String space : blanks) {
            String str = "a " + space + " b1234c  ";
            expectParseException(fmt, str, n);
        }

        // Test default ignorable characters.  These should work in both lenient and strict.
        UnicodeSet defaultIgnorables = new UnicodeSet("[[:Bidi_Control:]]").freeze();
        fmt.setParseStrict(false);
        for (String ignorable : defaultIgnorables) {
            String str = "a b " + ignorable + "1234c  ";
            expect(fmt, str, n);
        }
        fmt.setParseStrict(true);
        for (String ignorable : defaultIgnorables) {
            String str = "a b " + ignorable + "1234c  ";
            expect(fmt, str, n);
        }

        // Test that other whitespace characters do not work
        fmt.setParseStrict(false);
        UnicodeSet otherWhitespace = new UnicodeSet("[[:whitespace:]]").removeAll(blanks).freeze();
        for (String space : otherWhitespace) {
            String str = "a  " + space + "  b1234";
            expectParseException(fmt, str, n);
        }
    }

    /**
     * Test currencies whose display name is a ChoiceFormat.
     */
    @Test
    public void TestComplexCurrency() {
        //  CLDR No Longer uses complex currency symbols.
        //  Skipping this test.
        //        Locale loc = new Locale("kn", "IN", "");
        //        NumberFormat fmt = NumberFormat.getCurrencyInstance(loc);

        //        expect2(fmt, 1.0, "Re.\u00a01.00");
        //        expect(fmt, 1.001, "Re.\u00a01.00"); // tricky
        //        expect2(fmt, 12345678.0, "Rs.\u00a01,23,45,678.00");
        //        expect2(fmt, 0.5, "Rs.\u00a00.50");
        //        expect2(fmt, -1.0, "-Re.\u00a01.00");
        //        expect2(fmt, -10.0, "-Rs.\u00a010.00");
    }

    @Test
    public void TestCurrencyKeyword() {
        ULocale locale = new ULocale("th_TH@currency=QQQ");
        NumberFormat format = NumberFormat.getCurrencyInstance(locale);
        String result = format.format(12.34f);
        if (!"QQQ 12.34".equals(result)) {
            errln("got unexpected currency: " + result);
        }
    }

    /**
     * Test alternate numbering systems
     */
    @Test
    public void TestNumberingSystems() {
        class TestNumberingSystemItem {
            private final String localeName;
            private final double value;
            private final boolean isRBNF;
            private final String expectedResult;

            TestNumberingSystemItem(String loc, double val, boolean rbnf, String exp) {
                localeName  = loc;
                value = val;
                isRBNF = rbnf;
                expectedResult = exp;
            }
        }

        final TestNumberingSystemItem[] DATA = {
                new TestNumberingSystemItem( "en_US@numbers=thai",        1234.567, false, "\u0e51,\u0e52\u0e53\u0e54.\u0e55\u0e56\u0e57" ),
                new TestNumberingSystemItem( "en_US@numbers=thai",        1234.567, false, "\u0E51,\u0E52\u0E53\u0E54.\u0E55\u0E56\u0E57" ),
                new TestNumberingSystemItem( "en_US@numbers=hebr",        5678.0,   true,  "\u05D4\u05F3\u05EA\u05E8\u05E2\u05F4\u05D7" ),
                new TestNumberingSystemItem( "en_US@numbers=arabext",     1234.567, false, "\u06F1\u066c\u06F2\u06F3\u06F4\u066b\u06F5\u06F6\u06F7" ),
                new TestNumberingSystemItem( "de_DE@numbers=foobar",      1234.567, false, "1.234,567" ),
                new TestNumberingSystemItem( "ar_EG",                     1234.567, false, "\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666\u0667" ),
                new TestNumberingSystemItem( "th_TH@numbers=traditional", 1234.567, false, "\u0E51,\u0E52\u0E53\u0E54.\u0E55\u0E56\u0E57" ), // fall back to native per TR35
                new TestNumberingSystemItem( "ar_MA",                     1234.567, false, "1.234,567" ),
                new TestNumberingSystemItem( "en_US@numbers=hanidec",     1234.567, false, "\u4e00,\u4e8c\u4e09\u56db.\u4e94\u516d\u4e03" ),
                new TestNumberingSystemItem( "ta_IN@numbers=native",      1234.567, false, "\u0BE7,\u0BE8\u0BE9\u0BEA.\u0BEB\u0BEC\u0BED" ),
                new TestNumberingSystemItem( "ta_IN@numbers=traditional", 1235.0,   true,  "\u0BF2\u0BE8\u0BF1\u0BE9\u0BF0\u0BEB" ),
                new TestNumberingSystemItem( "ta_IN@numbers=finance",     1234.567, false, "1,234.567" ), // fall back to default per TR35
                new TestNumberingSystemItem( "zh_TW@numbers=native",      1234.567, false, "\u4e00,\u4e8c\u4e09\u56db.\u4e94\u516d\u4e03" ),
                new TestNumberingSystemItem( "zh_TW@numbers=traditional", 1234.567, true,  "\u4E00\u5343\u4E8C\u767E\u4E09\u5341\u56DB\u9EDE\u4E94\u516D\u4E03" ),
                new TestNumberingSystemItem( "zh_TW@numbers=finance",     1234.567, true,  "\u58F9\u4EDF\u8CB3\u4F70\u53C3\u62FE\u8086\u9EDE\u4F0D\u9678\u67D2" ),
                new TestNumberingSystemItem( "en_US@numbers=mathsanb",    1234.567, false,  "𝟭,𝟮𝟯𝟰.𝟱𝟲𝟳" ), // ticket #13286
        };


        for (TestNumberingSystemItem item : DATA) {
            ULocale loc = new ULocale(item.localeName);
            NumberFormat fmt = NumberFormat.getInstance(loc);
            if (item.isRBNF) {
                expect3(fmt,item.value,item.expectedResult);
            } else {
                expect2(fmt,item.value,item.expectedResult);
            }
        }
    }

    // Coverage tests for methods not being called otherwise.
    @Test
    public void TestNumberingSystemCoverage() {
        // Test getAvaliableNames
        String[] availableNames = NumberingSystem.getAvailableNames();
        if (availableNames == null || availableNames.length <= 0) {
            errln("ERROR: NumberingSystem.getAvailableNames() returned a null or empty array.");
        } else {
            // Check for alphabetical order
            for (int i=0; i<availableNames.length-1; i++) {
                assertTrue("Names should be in alphabetical order",
                        availableNames[i].compareTo(availableNames[i+1]) < 0);
            }

            boolean latnFound = false;
            for (String name : availableNames){
                assertNotEquals("should not throw and should not be null",
                        null, NumberingSystem.getInstanceByName(name));
                if ("latn".equals(name)) {
                    latnFound = true;
                    break;
                }
            }

            if (!latnFound) {
                errln("ERROR: 'latn' numbering system not found on NumberingSystem.getAvailableNames().");
            }
        }

        assertEquals("Non-existing numbering system should return null",
                null, NumberingSystem.getInstanceByName("dummy"));

        // Test NumberingSystem.getInstance()
        NumberingSystem ns1 = NumberingSystem.getInstance();
        if (ns1 == null || ns1.isAlgorithmic()) {
            errln("ERROR: NumberingSystem.getInstance() returned a null or invalid NumberingSystem");
        }

        // Test NumberingSystem.getInstance(int,boolean,String)
        /* Parameters used: the ones used in the default constructor
         * radix = 10;
         * algorithmic = false;
         * desc = "0123456789";
         */
        NumberingSystem ns2 = NumberingSystem.getInstance(10, false, "0123456789");
        if (ns2 == null || ns2.isAlgorithmic()) {
            errln("ERROR: NumberingSystem.getInstance(int,boolean,String) returned a null or invalid NumberingSystem");
        }

        // Test NumberingSystem.getInstance(Locale)
        NumberingSystem ns3 = NumberingSystem.getInstance(Locale.ENGLISH);
        if (ns3 == null || ns3.isAlgorithmic()) {
            errln("ERROR: NumberingSystem.getInstance(Locale) returned a null or invalid NumberingSystem");
        }
    }

    @Test
    public void Test6816() {
        Currency cur1 = Currency.getInstance(new Locale("und", "PH"));

        NumberFormat nfmt = NumberFormat.getCurrencyInstance(new Locale("und", "PH"));
        DecimalFormatSymbols decsym = ((DecimalFormat)nfmt).getDecimalFormatSymbols();
        Currency cur2 = decsym.getCurrency();

        if ( !cur1.getCurrencyCode().equals("PHP") || !cur2.getCurrencyCode().equals("PHP")) {
            errln("FAIL: Currencies should match PHP: cur1 = "+cur1.getCurrencyCode()+"; cur2 = "+cur2.getCurrencyCode());
        }

    }

    @Test
    public void TestThreadedFormat() {

        class FormatTask implements Runnable {
            DecimalFormat fmt;
            StringBuffer buf;
            boolean inc;
            float num;

            FormatTask(DecimalFormat fmt, int index) {
                this.fmt = fmt;
                this.buf = new StringBuffer();
                this.inc = (index & 0x1) == 0;
                this.num = inc ? 0 : 10000;
            }

            @Override
            public void run() {
                if (inc) {
                    while (num < 10000) {
                        buf.append(fmt.format(num) + "\n");
                        num += 3.14159;
                    }
                } else {
                    while (num > 0) {
                        buf.append(fmt.format(num) + "\n");
                        num -= 3.14159;
                    }
                }
            }

            String result() {
                return buf.toString();
            }
        }

        DecimalFormat fmt = new DecimalFormat("0.####");
        FormatTask[] tasks = new FormatTask[8];
        for (int i = 0; i < tasks.length; ++i) {
            tasks[i] = new FormatTask(fmt, i);
        }

        TestUtil.runUntilDone(tasks);

        for (int i = 2; i < tasks.length; i++) {
            String str1 = tasks[i].result();
            String str2 = tasks[i-2].result();
            if (!str1.equals(str2)) {
                System.out.println("mismatch at " + i);
                System.out.println(str1);
                System.out.println(str2);
                errln("decimal format thread mismatch");

                break;
            }
            str1 = str2;
        }
    }

    @Test
    public void TestPerMill() {
        DecimalFormat fmt = new DecimalFormat("###.###\u2030");
        assertEquals("0.4857 x ###.###\u2030",
                "485.7\u2030", fmt.format(0.4857));

        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.ENGLISH);
        sym.setPerMill('m');
        DecimalFormat fmt2 = new DecimalFormat("", sym);
        fmt2.applyLocalizedPattern("###.###m");
        assertEquals("0.4857 x ###.###m",
                "485.7m", fmt2.format(0.4857));
    }

    @Test
    public void TestIllegalPatterns() {
        // Test cases:
        // Prefix with "-:" for illegal patterns
        // Prefix with "+:" for legal patterns
        String DATA[] = {
                // Unquoted special characters in the suffix are illegal
                "-:000.000|###",
                "+:000.000'|###'",
        };
        for (int i=0; i<DATA.length; ++i) {
            String pat=DATA[i];
            boolean valid = pat.charAt(0) == '+';
            pat = pat.substring(2);
            Exception e = null;
            try {
                // locale doesn't matter here
                new DecimalFormat(pat);
            } catch (IllegalArgumentException e1) {
                e = e1;
            } catch (IndexOutOfBoundsException e1) {
                e = e1;
            }
            String msg = (e==null) ? "success" : e.getMessage();
            if ((e==null) == valid) {
                logln("Ok: pattern \"" + pat + "\": " + msg);
            } else {
                errln("FAIL: pattern \"" + pat + "\" should have " +
                        (valid?"succeeded":"failed") + "; got " + msg);
            }
        }
    }

    /**
     * Parse a CurrencyAmount using the given NumberFormat, with
     * the 'delim' character separating the number and the currency.
     */
    private static CurrencyAmount parseCurrencyAmount(String str, NumberFormat fmt,
            char delim)
                    throws ParseException {
        int i = str.indexOf(delim);
        return new CurrencyAmount(fmt.parse(str.substring(0,i)),
                Currency.getInstance(str.substring(i+1)));
    }

    /**
     * Return an integer representing the next token from this
     * iterator.  The integer will be an index into the given list, or
     * -1 if there are no more tokens, or -2 if the token is not on
     * the list.
     */
    private static int keywordIndex(String tok) {
        for (int i=0; i<KEYWORDS.length; ++i) {
            if (tok.equals(KEYWORDS[i])) {
                return i;
            }
        }
        return -1;
    }

    private static final String KEYWORDS[] = {
        /*0*/ "ref=", // <reference pattern to parse numbers>
        /*1*/ "loc=", // <locale for formats>
        /*2*/ "f:",   // <pattern or '-'> <number> <exp. string>
        /*3*/ "fp:",  // <pattern or '-'> <number> <exp. string> <exp. number>
        /*4*/ "rt:",  // <pattern or '-'> <(exp.) number> <(exp.) string>
        /*5*/ "p:",   // <pattern or '-'> <string> <exp. number>
        /*6*/ "perr:", // <pattern or '-'> <invalid string>
        /*7*/ "pat:", // <pattern or '-'> <exp. toPattern or '-' or 'err'>
        /*8*/ "fpc:", // <loc or '-'> <curr.amt> <exp. string> <exp. curr.amt>
        /*9*/ "strict=", // true or false
    };

    @SuppressWarnings("resource")  // InputStream is will be closed by the ResourceReader.
    @Test
    public void TestCases() {
        String caseFileName = "NumberFormatTestCases.txt";
        java.io.InputStream is = NumberFormatTest.class.getResourceAsStream(caseFileName);

        ResourceReader reader = new ResourceReader(is, caseFileName, "utf-8");
        TokenIterator tokens = new TokenIterator(reader);

        Locale loc = new Locale("en", "US", "");
        DecimalFormat ref = null, fmt = null;
        MeasureFormat mfmt = null;
        String pat = null, str = null, mloc = null;
        boolean strict = false;

        try {
            for (;;) {
                String tok = tokens.next();
                if (tok == null) {
                    break;
                }
                String where = "(" + tokens.getLineNumber() + ") ";
                int cmd = keywordIndex(tok);
                switch (cmd) {
                case 0:
                    // ref= <reference pattern>
                    ref = new DecimalFormat(tokens.next(),
                            new DecimalFormatSymbols(Locale.US));
                    ref.setParseStrict(strict);
                    logln("Setting reference pattern to:\t" + ref);
                    break;
                case 1:
                    // loc= <locale>
                    loc = LocaleUtility.getLocaleFromName(tokens.next());
                    pat = ((DecimalFormat) NumberFormat.getInstance(loc)).toPattern();
                    logln("Setting locale to:\t" + loc + ", \tand pattern to:\t" + pat);
                    break;
                case 2: // f:
                case 3: // fp:
                case 4: // rt:
                case 5: // p:
                    tok = tokens.next();
                    if (!tok.equals("-")) {
                        pat = tok;
                    }
                    try {
                        fmt = new DecimalFormat(pat, new DecimalFormatSymbols(loc));
                        fmt.setParseStrict(strict);
                    } catch (IllegalArgumentException iae) {
                        errln(where + "Pattern \"" + pat + '"');
                        iae.printStackTrace();
                        tokens.next(); // consume remaining tokens
                        //tokens.next();
                        if (cmd == 3) tokens.next();
                        continue;
                    }
                    str = null;
                    try {
                        if (cmd == 2 || cmd == 3 || cmd == 4) {
                            // f: <pattern or '-'> <number> <exp. string>
                            // fp: <pattern or '-'> <number> <exp. string> <exp. number>
                            // rt: <pattern or '-'> <number> <string>
                            String num = tokens.next();
                            str = tokens.next();
                            Number n = ref.parse(num);
                            assertEquals(where + '"' + pat + "\".format(" + num + ")",
                                    str, fmt.format(n));
                            if (cmd == 3) { // fp:
                                n = ref.parse(tokens.next());
                            }
                            if (cmd != 2) { // != f:
                                assertEquals(where + '"' + pat + "\".parse(\"" + str + "\")",
                                        n, fmt.parse(str));
                            }
                        }
                        // p: <pattern or '-'> <string to parse> <exp. number>
                        else {
                            str = tokens.next();
                            String expstr = tokens.next();
                            Number parsed = fmt.parse(str);
                            Number exp = ref.parse(expstr);
                            assertEquals(where + '"' + pat + "\".parse(\"" + str + "\")",
                                    exp, parsed);
                        }
                    } catch (ParseException e) {
                        errln(where + '"' + pat + "\".parse(\"" + str +
                                "\") threw an exception");
                        e.printStackTrace();
                    }
                    break;
                case 6:
                    // perr: <pattern or '-'> <invalid string>
                    errln("Under construction");
                    return;
                case 7:
                    // pat: <pattern> <exp. toPattern, or '-' or 'err'>
                    String testpat = tokens.next();
                    String exppat  = tokens.next();
                    boolean err    = exppat.equals("err");
                    if (testpat.equals("-")) {
                        if (err) {
                            errln("Invalid command \"pat: - err\" at " +  tokens.describePosition());
                            continue;
                        }
                        testpat = pat;
                    }
                    if (exppat.equals("-")) exppat = testpat;
                    try {
                        DecimalFormat f = null;
                        if (testpat == pat) { // [sic]
                            f = fmt;
                        } else {
                            f = new DecimalFormat(testpat);
                            f.setParseStrict(strict);
                        }
                        if (err) {
                            errln(where + "Invalid pattern \"" + testpat +
                                    "\" was accepted");
                        } else {
                            assertEquals(where + '"' + testpat + "\".toPattern()",
                                    exppat, f.toPattern());
                        }
                    } catch (IllegalArgumentException iae2) {
                        if (err) {
                            logln("Ok: " + where + "Invalid pattern \"" + testpat +
                                    "\" threw an exception");
                        } else {
                            errln(where + "Valid pattern \"" + testpat +
                                    "\" threw an exception");
                            iae2.printStackTrace();
                        }
                    }
                    break;
                case 8: // fpc:
                    tok = tokens.next();
                    if (!tok.equals("-")) {
                        mloc = tok;
                        ULocale l = new ULocale(mloc);
                        try {
                            mfmt = MeasureFormat.getCurrencyFormat(l);
                        } catch (IllegalArgumentException iae) {
                            errln(where + "Loc \"" + tok + '"');
                            iae.printStackTrace();
                            tokens.next(); // consume remaining tokens
                            tokens.next();
                            tokens.next();
                            continue;
                        }
                    }
                    str = null;
                    try {
                        // fpc: <loc or '-'> <curr.amt> <exp. string> <exp. curr.amt>
                        String currAmt = tokens.next();
                        str = tokens.next();
                        CurrencyAmount target = parseCurrencyAmount(currAmt, ref, '/');
                        String formatResult = mfmt.format(target);
                        assertEquals(where + "getCurrencyFormat(" + mloc + ").format(" + currAmt + ")",
                                str, formatResult);
                        target = parseCurrencyAmount(tokens.next(), ref, '/');
                        CurrencyAmount parseResult = (CurrencyAmount) mfmt.parseObject(str);
                        assertEquals(where + "getCurrencyFormat(" + mloc + ").parse(\"" + str + "\")",
                                target, parseResult);
                    } catch (ParseException e) {
                        errln(where + '"' + pat + "\".parse(\"" + str +
                                "\") threw an exception");
                        e.printStackTrace();
                    }
                    break;
                case 9: // strict= true or false
                    strict = "true".equalsIgnoreCase(tokens.next());
                    logln("Setting strict to:\t" + strict);
                    break;
                case -1:
                    errln("Unknown command \"" + tok + "\" at " + tokens.describePosition());
                    return;
                }
            }
        } catch (java.io.IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                reader.close();
            } catch (IOException ignored) {
            }
        }
    }

    @Test
    public void TestFieldPositionDecimal() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        nf.setPositivePrefix("FOO");
        nf.setPositiveSuffix("BA");
        StringBuffer buffer = new StringBuffer();
        FieldPosition fp = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
        nf.format(35.47, buffer, fp);
        assertEquals("35.47", "FOO35.47BA", buffer.toString());
        assertEquals("fp begin", 5, fp.getBeginIndex());
        assertEquals("fp end", 6, fp.getEndIndex());
    }

    @Test
    public void TestFieldPositionInteger() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        nf.setPositivePrefix("FOO");
        nf.setPositiveSuffix("BA");
        StringBuffer buffer = new StringBuffer();
        FieldPosition fp = new FieldPosition(NumberFormat.Field.INTEGER);
        nf.format(35.47, buffer, fp);
        assertEquals("35.47", "FOO35.47BA", buffer.toString());
        assertEquals("fp begin", 3, fp.getBeginIndex());
        assertEquals("fp end", 5, fp.getEndIndex());
    }

    @Test
    public void TestFieldPositionFractionButInteger() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        nf.setPositivePrefix("FOO");
        nf.setPositiveSuffix("BA");
        StringBuffer buffer = new StringBuffer();
        FieldPosition fp = new FieldPosition(NumberFormat.Field.FRACTION);
        nf.format(35, buffer, fp);
        assertEquals("35", "FOO35BA", buffer.toString());
        assertEquals("fp begin", 5, fp.getBeginIndex());
        assertEquals("fp end", 5, fp.getEndIndex());
    }

    @Test
    public void TestFieldPositionFraction() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        nf.setPositivePrefix("FOO");
        nf.setPositiveSuffix("BA");
        StringBuffer buffer = new StringBuffer();
        FieldPosition fp = new FieldPosition(NumberFormat.Field.FRACTION);
        nf.format(35.47, buffer, fp);
        assertEquals("35.47", "FOO35.47BA", buffer.toString());
        assertEquals("fp begin", 6, fp.getBeginIndex());
        assertEquals("fp end", 8, fp.getEndIndex());
    }

    @Test
    public void TestFieldPositionCurrency() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.US);
        double amount = 35.47;
        double negAmount = -34.567;
        FieldPosition cp = new FieldPosition(NumberFormat.Field.CURRENCY);

        StringBuffer buffer0 = new StringBuffer();
        nf.format(amount, buffer0, cp);
        assertEquals("$35.47", "$35.47", buffer0.toString());
        assertEquals("cp begin", 0, cp.getBeginIndex());
        assertEquals("cp end", 1, cp.getEndIndex());

        StringBuffer buffer01 = new StringBuffer();
        nf.format(negAmount, buffer01, cp);
        assertEquals("-$34.57", "-$34.57", buffer01.toString());
        assertEquals("cp begin", 1, cp.getBeginIndex());
        assertEquals("cp end", 2, cp.getEndIndex());

        nf.setCurrency(Currency.getInstance(Locale.FRANCE));
        StringBuffer buffer1 = new StringBuffer();
        nf.format(amount, buffer1, cp);
        assertEquals("€35.47", "€35.47", buffer1.toString());
        assertEquals("cp begin", 0, cp.getBeginIndex());
        assertEquals("cp end", 1, cp.getEndIndex());

        nf.setCurrency(Currency.getInstance(new Locale("fr", "ch", "")));
        StringBuffer buffer2 = new StringBuffer();
        nf.format(amount, buffer2, cp);
        assertEquals("CHF 35.47", "CHF 35.47", buffer2.toString());
        assertEquals("cp begin", 0, cp.getBeginIndex());
        assertEquals("cp end", 3, cp.getEndIndex());

        StringBuffer buffer20 = new StringBuffer();
        nf.format(negAmount, buffer20, cp);
        assertEquals("-CHF 34.57", "-CHF 34.57", buffer20.toString());
        assertEquals("cp begin", 1, cp.getBeginIndex());
        assertEquals("cp end", 4, cp.getEndIndex());

        nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.FRANCE);
        StringBuffer buffer3 = new StringBuffer();
        nf.format(amount, buffer3, cp);
        assertEquals("35,47 €", "35,47 €", buffer3.toString());
        assertEquals("cp begin", 6, cp.getBeginIndex());
        assertEquals("cp end", 7, cp.getEndIndex());

        StringBuffer buffer4 = new StringBuffer();
        nf.format(negAmount, buffer4, cp);
        assertEquals("-34,57 €", "-34,57 €", buffer4.toString());
        assertEquals("cp begin", 7, cp.getBeginIndex());
        assertEquals("cp end", 8, cp.getEndIndex());

        nf.setCurrency(Currency.getInstance(new Locale("fr", "ch")));
        StringBuffer buffer5 = new StringBuffer();
        nf.format(negAmount, buffer5, cp);
        assertEquals("-34,57 CHF", "-34,57 CHF", buffer5.toString());
        assertEquals("cp begin", 7, cp.getBeginIndex());
        assertEquals("cp end", 10, cp.getEndIndex());

        NumberFormat plCurrencyFmt = NumberFormat.getInstance(new Locale("fr", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
        StringBuffer buffer6 = new StringBuffer();
        plCurrencyFmt.format(negAmount, buffer6, cp);
        assertEquals("-34.57 francs suisses", "-34.57 francs suisses", buffer6.toString());
        assertEquals("cp begin", 7, cp.getBeginIndex());
        assertEquals("cp end", 21, cp.getEndIndex());

        // Positive value with PLURALCURRENCYSTYLE.
        plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
        StringBuffer buffer7 = new StringBuffer();
        plCurrencyFmt.format(amount, buffer7, cp);
        assertEquals("35.47 スイス フラン", "35.47 スイス フラン", buffer7.toString());
        assertEquals("cp begin", 6, cp.getBeginIndex());
        assertEquals("cp end", 13, cp.getEndIndex());

        // PLURALCURRENCYSTYLE for non-ASCII.
        plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "de"), NumberFormat.PLURALCURRENCYSTYLE);
        StringBuffer buffer8 = new StringBuffer();
        plCurrencyFmt.format(negAmount, buffer8, cp);
        assertEquals("-34.57 ユーロ", "-34.57 ユーロ", buffer8.toString());
        assertEquals("cp begin", 7, cp.getBeginIndex());
        assertEquals("cp end", 10, cp.getEndIndex());

        nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.JAPAN);
        nf.setCurrency(Currency.getInstance(new Locale("ja", "jp")));
        StringBuffer buffer9 = new StringBuffer();
        nf.format(negAmount, buffer9, cp);
        assertEquals("-￥35", "-￥35", buffer9.toString());
        assertEquals("cp begin", 1, cp.getBeginIndex());
        assertEquals("cp end", 2, cp.getEndIndex());

        // Negative value with PLURALCURRENCYSTYLE.
        plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
        StringBuffer buffer10 = new StringBuffer();
        plCurrencyFmt.format(negAmount, buffer10, cp);
        assertEquals("-34.57 スイス フラン", "-34.57 スイス フラン", buffer10.toString());
        assertEquals("cp begin", 7, cp.getBeginIndex());
        assertEquals("cp end", 14, cp.getEndIndex());

        // Nagative value with PLURALCURRENCYSTYLE, Arabic digits.
        nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(new Locale("ar", "eg"));
        plCurrencyFmt = NumberFormat.getInstance(new Locale("ar", "eg"), NumberFormat.PLURALCURRENCYSTYLE);
        StringBuffer buffer11 = new StringBuffer();
        plCurrencyFmt.format(negAmount, buffer11, cp);
        assertEquals("؜-٣٤٫٥٧ جنيه مصري", "؜-٣٤٫٥٧ جنيه مصري", buffer11.toString());
        assertEquals("cp begin", 8, cp.getBeginIndex());
        assertEquals("cp end", 17, cp.getEndIndex());
    }

    @Test
    public void TestRounding() {
        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        if (false) { // for debugging specific value
            nf.setRoundingMode(BigDecimal.ROUND_HALF_UP);
            checkRounding(nf, new BigDecimal("300.0300000000"), 0, new BigDecimal("0.020000000"));
        }
        // full tests
        int[] roundingIncrements = {1, 2, 5, 20, 50, 100};
        int[] testValues = {0, 300};
        for (int j = 0; j < testValues.length; ++j) {
            for (int mode = BigDecimal.ROUND_UP; mode < BigDecimal.ROUND_HALF_EVEN; ++mode) {
                nf.setRoundingMode(mode);
                for (int increment = 0; increment < roundingIncrements.length; ++increment) {
                    BigDecimal base = new BigDecimal(testValues[j]);
                    BigDecimal rInc = new BigDecimal(roundingIncrements[increment]);
                    checkRounding(nf,  base, 20, rInc);
                    rInc = new BigDecimal("1.000000000").divide(rInc);
                    checkRounding(nf,  base, 20, rInc);
                }
            }
        }
    }

    @Test
    public void TestRoundingPattern() {
        class TestRoundingPatternItem {
            String     pattern;
            BigDecimal roundingIncrement;
            double     testCase;
            String     expected;

            TestRoundingPatternItem(String pattern, BigDecimal roundingIncrement, double testCase, String expected) {
                this.pattern = pattern;
                this.roundingIncrement = roundingIncrement;
                this.testCase = testCase;
                this.expected = expected;
            }
        };

        TestRoundingPatternItem []tests = {
                new TestRoundingPatternItem("##0.65", new BigDecimal("0.65"), 1.234, "1.30"),
                new TestRoundingPatternItem("#50", new BigDecimal("50"), 1230, "1250")
        };

        DecimalFormat df = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
        String result;
        for (int i = 0; i < tests.length; i++) {
            df.applyPattern(tests[i].pattern);

            result = df.format(tests[i].testCase);

            if (!tests[i].expected.equals(result)) {
                errln("String Pattern Rounding Test Failed: Pattern: \"" + tests[i].pattern + "\" Number: " + tests[i].testCase + " - Got: " + result + " Expected: " + tests[i].expected);
            }

            df.setRoundingIncrement(tests[i].roundingIncrement);

            result = df.format(tests[i].testCase);

            if (!tests[i].expected.equals(result)) {
                errln("BigDecimal Rounding Test Failed: Pattern: \"" + tests[i].pattern + "\" Number: " + tests[i].testCase + " - Got: " + result + " Expected: " + tests[i].expected);
            }
        }
    }

    @Test
    public void TestBigDecimalRounding() {
        String figure = "50.000000004";
        Double dbl = new Double(figure);
        BigDecimal dec = new BigDecimal(figure);

        DecimalFormat f = (DecimalFormat) NumberFormat.getInstance();
        f.applyPattern("00.00######");

        assertEquals("double format", "50.00", f.format(dbl));
        assertEquals("bigdec format", "50.00", f.format(dec));

        int maxFracDigits = f.getMaximumFractionDigits();
        BigDecimal roundingIncrement = new BigDecimal("1").movePointLeft(maxFracDigits);

        f.setRoundingIncrement(roundingIncrement);
        f.setRoundingMode(BigDecimal.ROUND_DOWN);
        assertEquals("Rounding down", f.format(dbl), f.format(dec));

        f.setRoundingIncrement(roundingIncrement);
        f.setRoundingMode(BigDecimal.ROUND_HALF_UP);
        assertEquals("Rounding half up", f.format(dbl), f.format(dec));
    }

    void checkRounding(DecimalFormat nf, BigDecimal base, int iterations, BigDecimal increment) {
        nf.setRoundingIncrement(increment.toBigDecimal());
        BigDecimal lastParsed = new BigDecimal(Integer.MIN_VALUE); // used to make sure that rounding is monotonic
        for (int i = -iterations; i <= iterations; ++i) {
            BigDecimal iValue = base.add(increment.multiply(new BigDecimal(i)).movePointLeft(1));
            BigDecimal smallIncrement = new BigDecimal("0.00000001");
            if (iValue.signum() != 0) {
                smallIncrement.multiply(iValue); // scale unless zero
            }
            // we not only test the value, but some values in a small range around it.
            lastParsed = checkRound(nf, iValue.subtract(smallIncrement), lastParsed);
            lastParsed = checkRound(nf, iValue, lastParsed);
            lastParsed = checkRound(nf, iValue.add(smallIncrement), lastParsed);
        }
    }

    private BigDecimal checkRound(DecimalFormat nf, BigDecimal iValue, BigDecimal lastParsed) {
        String formatedBigDecimal = nf.format(iValue);
        String formattedDouble = nf.format(iValue.doubleValue());
        if (!equalButForTrailingZeros(formatedBigDecimal, formattedDouble)) {

            errln("Failure at: " + iValue + " (" + iValue.doubleValue() + ")"
                    + ",\tRounding-mode: " + roundingModeNames[nf.getRoundingMode()]
                            + ",\tRounding-increment: " + nf.getRoundingIncrement()
                            + ",\tdouble: " + formattedDouble
                            + ",\tBigDecimal: " + formatedBigDecimal);

        } else {
            logln("Value: " + iValue
                    + ",\tRounding-mode: " + roundingModeNames[nf.getRoundingMode()]
                            + ",\tRounding-increment: " + nf.getRoundingIncrement()
                            + ",\tdouble: " + formattedDouble
                            + ",\tBigDecimal: " + formatedBigDecimal);
        }
        try {
            // Number should have compareTo(...)
            BigDecimal parsed = toBigDecimal(nf.parse(formatedBigDecimal));
            if (lastParsed.compareTo(parsed) > 0) {
                errln("Rounding wrong direction!: " + lastParsed + " > " + parsed);
            }
            lastParsed = parsed;
        } catch (ParseException e) {
            errln("Parse Failure with: " + formatedBigDecimal);
        }
        return lastParsed;
    }

    static BigDecimal toBigDecimal(Number number) {
        return number instanceof BigDecimal ? (BigDecimal) number
                : number instanceof BigInteger ? new BigDecimal((BigInteger)number)
        : number instanceof java.math.BigDecimal ? new BigDecimal((java.math.BigDecimal)number)
                : number instanceof Double ? new BigDecimal(number.doubleValue())
        : number instanceof Float ? new BigDecimal(number.floatValue())
                : new BigDecimal(number.longValue());
    }

    static String[] roundingModeNames = {
        "ROUND_UP", "ROUND_DOWN", "ROUND_CEILING", "ROUND_FLOOR",
        "ROUND_HALF_UP", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN",
        "ROUND_UNNECESSARY"
    };

    private static boolean equalButForTrailingZeros(String formatted1, String formatted2) {
        if (formatted1.length() == formatted2.length()) return formatted1.equals(formatted2);
        return stripFinalZeros(formatted1).equals(stripFinalZeros(formatted2));
    }

    private static String stripFinalZeros(String formatted) {
        int len1 = formatted.length();
        char ch;
        while (len1 > 0 && ((ch = formatted.charAt(len1-1)) == '0' || ch == '.')) --len1;
        if (len1==1 && ((ch = formatted.charAt(len1-1)) == '-')) --len1;
        return formatted.substring(0,len1);
    }

    //------------------------------------------------------------------
    // Support methods
    //------------------------------------------------------------------

    /** Format-Parse test */
    public void expect2(NumberFormat fmt, Number n, String exp) {
        // Don't round-trip format test, since we explicitly do it
        expect(fmt, n, exp, false);
        expect(fmt, exp, n);
    }
    /** Format-Parse test */
    public void expect3(NumberFormat fmt, Number n, String exp) {
        // Don't round-trip format test, since we explicitly do it
        expect_rbnf(fmt, n, exp, false);
        expect_rbnf(fmt, exp, n);
    }

    /** Format-Parse test (convenience) */
    public void expect2(NumberFormat fmt, double n, String exp) {
        expect2(fmt, new Double(n), exp);
    }
    /** RBNF Format-Parse test (convenience) */
    public void expect3(NumberFormat fmt, double n, String exp) {
        expect3(fmt, new Double(n), exp);
    }

    /** Format-Parse test (convenience) */
    public void expect2(NumberFormat fmt, long n, String exp) {
        expect2(fmt, new Long(n), exp);
    }
    /** RBNF Format-Parse test (convenience) */
    public void expect3(NumberFormat fmt, long n, String exp) {
        expect3(fmt, new Long(n), exp);
    }

    /** Format test */
    public void expect(NumberFormat fmt, Number n, String exp, boolean rt) {
        StringBuffer saw = new StringBuffer();
        FieldPosition pos = new FieldPosition(0);
        fmt.format(n, saw, pos);
        String pat = ((DecimalFormat)fmt).toPattern();
        if (saw.toString().equals(exp)) {
            logln("Ok   " + n + " x " +
                    pat + " = \"" +
                    saw + "\"");
            // We should be able to round-trip the formatted string =>
            // number => string (but not the other way around: number
            // => string => number2, might have number2 != number):
            if (rt) {
                try {
                    Number n2 = fmt.parse(exp);
                    StringBuffer saw2 = new StringBuffer();
                    fmt.format(n2, saw2, pos);
                    if (!saw2.toString().equals(exp)) {
                        errln("expect() format test rt, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                                ", FAIL \"" + exp + "\" => " + n2 + " => \"" + saw2 + '"');
                    }
                } catch (ParseException e) {
                    errln("expect() format test rt, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                            ", " + e.getMessage());
                    return;
                }
            }
        } else {
            errln("expect() format test, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                    ", FAIL " + n + " x " + pat + " = \"" + saw + "\", expected \"" + exp + "\"");
        }
    }
    /** RBNF format test */
    public void expect_rbnf(NumberFormat fmt, Number n, String exp, boolean rt) {
        StringBuffer saw = new StringBuffer();
        FieldPosition pos = new FieldPosition(0);
        fmt.format(n, saw, pos);
        if (saw.toString().equals(exp)) {
            logln("Ok   " + n + " = \"" +
                    saw + "\"");
            // We should be able to round-trip the formatted string =>
            // number => string (but not the other way around: number
            // => string => number2, might have number2 != number):
            if (rt) {
                try {
                    Number n2 = fmt.parse(exp);
                    StringBuffer saw2 = new StringBuffer();
                    fmt.format(n2, saw2, pos);
                    if (!saw2.toString().equals(exp)) {
                        errln("expect_rbnf() format test rt, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                                ", FAIL \"" + exp + "\" => " + n2 + " => \"" + saw2 + '"');
                    }
                } catch (ParseException e) {
                    errln("expect_rbnf() format test rt, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                            ", " + e.getMessage());
                    return;
                }
            }
        } else {
            errln("expect_rbnf() format test, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                    ", FAIL " + n + " = \"" + saw + "\", expected \"" + exp + "\"");
        }
    }

    /** Format test (convenience) */
    public void expect(NumberFormat fmt, Number n, String exp) {
        expect(fmt, n, exp, true);
    }

    /** Format test (convenience) */
    public void expect(NumberFormat fmt, double n, String exp) {
        expect(fmt, new Double(n), exp);
    }

    /** Format test (convenience) */
    public void expect(NumberFormat fmt, long n, String exp) {
        expect(fmt, new Long(n), exp);
    }

    /** Parse test */
    public void expect(NumberFormat fmt, String str, Number n) {
        Number num = null;
        try {
            num = fmt.parse(str);
        } catch (ParseException e) {
            errln(e.getMessage());
            return;
        }
        String pat = ((DecimalFormat)fmt).toPattern();
        // A little tricky here -- make sure Double(12345.0) and
        // Long(12345) match.
        if (num.equals(n) || num.doubleValue() == n.doubleValue()) {
            logln("Ok   \"" + str + "\" x " +
                    pat + " = " +
                    num);
        } else {
            errln("expect() parse test, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                    ", FAIL \"" + str + "\" x " + pat + " = " + num + ", expected " + n);
        }
    }

    /** RBNF Parse test */
    public void expect_rbnf(NumberFormat fmt, String str, Number n) {
        Number num = null;
        try {
            num = fmt.parse(str);
        } catch (ParseException e) {
            errln(e.getMessage());
            return;
        }
        // A little tricky here -- make sure Double(12345.0) and
        // Long(12345) match.
        if (num.equals(n) || num.doubleValue() == n.doubleValue()) {
            logln("Ok   \"" + str + " = " +
                    num);
        } else {
            errln("expect_rbnf() parse test, locale " + fmt.getLocale(ULocale.VALID_LOCALE) +
                    ", FAIL \"" + str + " = " + num + ", expected " + n);
        }
    }

    /** Parse test (convenience) */
    public void expect(NumberFormat fmt, String str, double n) {
        expect(fmt, str, new Double(n));
    }

    /** Parse test (convenience) */
    public void expect(NumberFormat fmt, String str, long n) {
        expect(fmt, str, new Long(n));
    }

    /** Parse test */
    public void expectParseException(DecimalFormat fmt, String str, Number n) {
        Number num = null;
        try {
            num = fmt.parse(str);
            errln("Expected failure, but passed: " + n + " on " + fmt.toPattern() + " -> " + num);
        } catch (ParseException e) {
        }
    }

    private void expectCurrency(NumberFormat nf, Currency curr,
            double value, String string) {
        DecimalFormat fmt = (DecimalFormat) nf;
        if (curr != null) {
            fmt.setCurrency(curr);
        }
        String s = fmt.format(value).replace('\u00A0', ' ');

        if (s.equals(string)) {
            logln("Ok: " + value + " x " + curr + " => " + s);
        } else {
            errln("FAIL: " + value + " x " + curr + " => " + s +
                    ", expected " + string);
        }
    }

    public void expectPad(DecimalFormat fmt, String pat, int pos) {
        expectPad(fmt, pat, pos, 0, (char)0);
    }

    public void expectPad(DecimalFormat fmt, final String pat, int pos, int width, final char pad) {
        int apos = 0, awidth = 0;
        char apadStr;
        try {
            fmt.applyPattern(pat);
            apos = fmt.getPadPosition();
            awidth = fmt.getFormatWidth();
            apadStr = fmt.getPadCharacter();
        } catch (Exception e) {
            apos = -1;
            awidth = width;
            apadStr = pad;
        }

        if (apos == pos && awidth == width && apadStr == pad) {
            logln("Ok   \"" + pat + "\" pos="
                    + apos + ((pos == -1) ? "" : " width=" + awidth + " pad=" + apadStr));
        } else {
            errln("FAIL \"" + pat + "\" pos=" + apos + " width="
                    + awidth + " pad=" + apadStr + ", expected "
                    + pos + " " + width + " " + pad);
        }
    }

    public void expectPat(DecimalFormat fmt, final String exp) {
        String pat = fmt.toPattern();
        if (pat.equals(exp)) {
            logln("Ok   \"" + pat + "\"");
        } else {
            errln("FAIL \"" + pat + "\", expected \"" + exp + "\"");
        }
    }


    private void expectParseCurrency(NumberFormat fmt, Currency expected, String text) {
        ParsePosition pos = new ParsePosition(0);
        CurrencyAmount currencyAmount = fmt.parseCurrency(text, pos);
        assertTrue("Parse of " + text + " should have succeeded.", pos.getIndex() > 0);
        assertEquals("Currency should be correct.", expected, currencyAmount.getCurrency());
    }

    @Test
    public void TestJB3832(){
        ULocale locale = new ULocale("pt_PT@currency=PTE");
        NumberFormat format = NumberFormat.getCurrencyInstance(locale);
        Currency curr = Currency.getInstance(locale);
        logln("\nName of the currency is: " + curr.getName(locale, Currency.LONG_NAME, new boolean[] {false}));
        CurrencyAmount cAmt = new CurrencyAmount(1150.50, curr);
        logln("CurrencyAmount object's hashCode is: " + cAmt.hashCode()); //cover hashCode
        String str = format.format(cAmt);
        String expected = "1,150$50\u00a0\u200b";
        if(!expected.equals(str)){
            errln("Did not get the expected output Expected: "+expected+" Got: "+ str);
        }
    }

    @Test
    public void TestScientificWithGrouping() {
        // Grouping separators are not allowed in the pattern, but we can enable them via the API.
        DecimalFormat df = new DecimalFormat("###0.000E0");
        df.setGroupingUsed(true);
        df.setGroupingSize(3);
        expect2(df, 123, "123.0E0");
        expect2(df, 1234, "1,234E0");
        expect2(df, 12340, "1.234E4");
    }

    @Test
    public void TestStrictParse() {
        // Pass both strict and lenient:
        String[] pass = {
                "0",           // single zero before end of text is not leading
                "0 ",          // single zero at end of number is not leading
                "0.",          // single zero before period (or decimal, it's ambiguous) is not leading
                "0,",          // single zero before comma (not group separator) is not leading
                "0.0",         // single zero before decimal followed by digit is not leading
                "0. ",         // same as above before period (or decimal) is not leading
                "0.100,5",     // comma stops parse of decimal (no grouping)
                "0.100,,5",    // two commas also stops parse
                ".00",         // leading decimal is ok, even with zeros
                "1234567",     // group separators are not required
                "12345, ",     // comma not followed by digit is not a group separator, but end of number
                "1,234, ",     // if group separator is present, group sizes must be appropriate
                "1,234,567",   // ...secondary too
                "0E",          // an exponent not followed by zero or digits is not an exponent
                "00",          // leading zero before zero - used to be error - see ticket #7913
                "012",         // leading zero before digit - used to be error - see ticket #7913
                "0,456",       // leading zero before group separator - used to be error - see ticket #7913
                "999,999",     // see ticket #6863
                "-99,999",     // see ticket #6863
                "-999,999",    // see ticket #6863
                "-9,999,999",  // see ticket #6863
        };
        // Pass lenient, fail strict:
        String[] fail = {
                "1,2",       // wrong number of digits after group separator
                ",.02",      // leading group separator before decimal
                "1,.02",     // group separator before decimal
                ",0",        // leading group separator before a single digit
                ",1",        // leading group separator before a single digit
                "1,45",      // wrong number of digits in primary group
                "1,45 that", // wrong number of digits in primary group
                "1,45.34",   // wrong number of digits in primary group
                "1234,567",  // wrong number of digits in secondary group
                "12,34,567", // wrong number of digits in secondary group
                "1,23,456,7890", // wrong number of digits in primary and secondary groups
        };
        // Fail both lenient and strict:
        String[] failBoth = {
        };

        DecimalFormat nf = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
        runStrictParseBatch(nf, pass, fail, failBoth);

        String[] scientificPass = {
                "0E2",      // single zero before exponent is ok
                "1234E2",   // any number of digits before exponent is ok
                "1,234E",   // an exponent string not followed by zero or digits is not an exponent
                "00E2",     // leading zeroes now allowed in strict mode - see ticket #
        };
        String[] scientificFail = {
        };
        String[] scientificFailBoth = {
        };

        nf = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
        runStrictParseBatch(nf, scientificPass, scientificFail, scientificFailBoth);

        String[] mixedPass = {
                "12,34,567",
                "12,34,567,",
                "12,34,567, that",
                "12,34,567 that",
        };
        String[] mixedFail = {
                "12,34,56",
                "12,34,56,",
                "12,34,56, that ",
                "12,34,56 that",
        };
        String[] mixedFailBoth = {
        };

        nf = new DecimalFormat("#,##,##0.#");
        runStrictParseBatch(nf, mixedPass, mixedFail, mixedFailBoth);
    }

    void runStrictParseBatch(DecimalFormat nf, String[] pass, String[] fail, String[] failBoth) {
        nf.setParseStrict(false);
        runStrictParseTests("should pass", nf, pass, true);
        runStrictParseTests("should also pass", nf, fail, true);
        runStrictParseTests("should fail", nf, failBoth, false);
        nf.setParseStrict(true);
        runStrictParseTests("should still pass", nf, pass, true);
        runStrictParseTests("should fail", nf, fail, false);
        runStrictParseTests("should still fail", nf, failBoth, false);
    }

    void runStrictParseTests(String msg, DecimalFormat nf, String[] tests, boolean pass) {
        logln("");
        logln("pattern: '" + nf.toPattern() + "'");
        logln(msg);
        for (int i = 0; i < tests.length; ++i) {
            String str = tests[i];
            ParsePosition pp = new ParsePosition(0);
            Number n = nf.parse(str, pp);
            String formatted = n != null ? nf.format(n) : "null";
            String err = pp.getErrorIndex() == -1 ? "" : "(error at " + pp.getErrorIndex() + ")";
            if ((err.length() == 0) != pass) {
                errln("'" + str + "' parsed '" +
                        str.substring(0, pp.getIndex()) +
                        "' returned " + n + " formats to '" +
                        formatted + "' " + err);
            } else {
                if (err.length() > 0) {
                    err = "got expected " + err;
                }
                logln("'" + str + "' parsed '" +
                        str.substring(0, pp.getIndex()) +
                        "' returned " + n + " formats to '" +
                        formatted + "' " + err);
            }
        }
    }

    @Test
    public void TestJB5251(){
        //save default locale
        ULocale defaultLocale = ULocale.getDefault();
        ULocale.setDefault(new ULocale("qr_QR"));
        try {
            NumberFormat.getInstance();
        }
        catch (Exception e) {
            errln("Numberformat threw exception for non-existent locale. It should use the default.");
        }
        //reset default locale
        ULocale.setDefault(defaultLocale);
    }

    @Test
    public void TestParseReturnType() {
        String[] defaultLong = {
                "123",
                "123.0",
                "0.0",
                "-9223372036854775808", // Min Long
                "9223372036854775807" // Max Long
        };

        String[] defaultNonLong = {
                "12345678901234567890",
                "9223372036854775808",
                "-9223372036854775809"
        };

        String[] doubles = {
                "-0.0",
                "NaN",
                "\u221E"    // Infinity
        };

        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        DecimalFormat nf = new DecimalFormat("#.#", sym);

        if (nf.isParseBigDecimal()) {
            errln("FAIL: isParseDecimal() must return false by default");
        }

        // isParseBigDecimal() is false
        for (int i = 0; i < defaultLong.length; i++) {
            try {
                Number n = nf.parse(defaultLong[i]);
                if (!(n instanceof Long)) {
                    errln("FAIL: parse does not return Long instance");
                }
            } catch (ParseException e) {
                errln("parse of '" + defaultLong[i] + "' threw exception: " + e);
            }
        }
        for (int i = 0; i < defaultNonLong.length; i++) {
            try {
                Number n = nf.parse(defaultNonLong[i]);
                if (n instanceof Long) {
                    errln("FAIL: parse returned a Long");
                }
            } catch (ParseException e) {
                errln("parse of '" + defaultNonLong[i] + "' threw exception: " + e);
            }
        }
        // parse results for doubls must be always Double
        for (int i = 0; i < doubles.length; i++) {
            try {
                Number n = nf.parse(doubles[i]);
                if (!(n instanceof Double)) {
                    errln("FAIL: parse does not return Double instance");
                }
            } catch (ParseException e) {
                errln("parse of '" + doubles[i] + "' threw exception: " + e);
            }
        }

        // force this DecimalFormat to return BigDecimal
        nf.setParseBigDecimal(true);
        if (!nf.isParseBigDecimal()) {
            errln("FAIL: isParseBigDecimal() must return true");
        }

        // isParseBigDecimal() is true
        for (int i = 0; i < defaultLong.length + defaultNonLong.length; i++) {
            String input = (i < defaultLong.length) ? defaultLong[i] : defaultNonLong[i - defaultLong.length];
            try {
                Number n = nf.parse(input);
                if (!(n instanceof BigDecimal)) {
                    errln("FAIL: parse does not return BigDecimal instance");
                }
            } catch (ParseException e) {
                errln("parse of '" + input + "' threw exception: " + e);
            }
        }
        // parse results for doubls must be always Double
        for (int i = 0; i < doubles.length; i++) {
            try {
                Number n = nf.parse(doubles[i]);
                if (!(n instanceof Double)) {
                    errln("FAIL: parse does not return Double instance");
                }
            } catch (ParseException e) {
                errln("parse of '" + doubles[i] + "' threw exception: " + e);
            }
        }
    }

    @Test
    public void TestNonpositiveMultiplier() {
        DecimalFormat df = new DecimalFormat("0");

        // test zero multiplier

        try {
            df.setMultiplier(0);

            // bad
            errln("DecimalFormat.setMultiplier(0) did not throw an IllegalArgumentException");
        } catch (IllegalArgumentException ex) {
            // good
        }

        // test negative multiplier

        try {
            df.setMultiplier(-1);

            if (df.getMultiplier() != -1) {
                errln("DecimalFormat.setMultiplier(-1) did not change the multiplier to -1");
                return;
            }

            // good
        } catch (IllegalArgumentException ex) {
            // bad
            errln("DecimalFormat.setMultiplier(-1) threw an IllegalArgumentException");
            return;
        }

        expect(df, "1122.123", -1122.123);
        expect(df, "-1122.123", 1122.123);
        expect(df, "1.2", -1.2);
        expect(df, "-1.2", 1.2);

        expect2(df, Long.MAX_VALUE, BigInteger.valueOf(Long.MAX_VALUE).negate().toString());
        expect2(df, Long.MIN_VALUE, BigInteger.valueOf(Long.MIN_VALUE).negate().toString());
        expect2(df, Long.MAX_VALUE / 2, BigInteger.valueOf(Long.MAX_VALUE / 2).negate().toString());
        expect2(df, Long.MIN_VALUE / 2, BigInteger.valueOf(Long.MIN_VALUE / 2).negate().toString());

        expect2(df, BigDecimal.valueOf(Long.MAX_VALUE), BigDecimal.valueOf(Long.MAX_VALUE).negate().toString());
        expect2(df, BigDecimal.valueOf(Long.MIN_VALUE), BigDecimal.valueOf(Long.MIN_VALUE).negate().toString());

        expect2(df, java.math.BigDecimal.valueOf(Long.MAX_VALUE), java.math.BigDecimal.valueOf(Long.MAX_VALUE).negate().toString());
        expect2(df, java.math.BigDecimal.valueOf(Long.MIN_VALUE), java.math.BigDecimal.valueOf(Long.MIN_VALUE).negate().toString());
    }

    @Test
    public void TestJB5358() {
        int numThreads = 10;
        String numstr = "12345";
        double expected = 12345;
        DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt = new DecimalFormat("#.#", sym);
        ArrayList errors = new ArrayList();

        ParseThreadJB5358[] threads = new ParseThreadJB5358[numThreads];
        for (int i = 0; i < numThreads; i++) {
            threads[i] = new ParseThreadJB5358((DecimalFormat)fmt.clone(), numstr, expected, errors);
            threads[i].start();
        }
        for (int i = 0; i < numThreads; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
        if (errors.size() != 0) {
            StringBuffer errBuf = new StringBuffer();
            for (int i = 0; i < errors.size(); i++) {
                errBuf.append((String)errors.get(i));
                errBuf.append("\n");
            }
            errln("FAIL: " + errBuf);
        }
    }

    static private class ParseThreadJB5358 extends Thread {
        private final DecimalFormat decfmt;
        private final String numstr;
        private final double expect;
        private final ArrayList errors;

        public ParseThreadJB5358(DecimalFormat decfmt, String numstr, double expect, ArrayList errors) {
            this.decfmt = decfmt;
            this.numstr = numstr;
            this.expect = expect;
            this.errors = errors;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                try {
                    Number n = decfmt.parse(numstr);
                    if (n.doubleValue() != expect) {
                        synchronized(errors) {
                            errors.add(new String("Bad parse result - expected:" + expect + " actual:" + n.doubleValue()));
                        }
                    }
                } catch (Throwable t) {
                    synchronized(errors) {
                        errors.add(new String(t.getClass().getName() + " - " + t.getMessage()));
                    }
                }
            }
        }
    }

    @Test
    public void TestSetCurrency() {
        DecimalFormatSymbols decf1 = DecimalFormatSymbols.getInstance(ULocale.US);
        DecimalFormatSymbols decf2 = DecimalFormatSymbols.getInstance(ULocale.US);
        decf2.setCurrencySymbol("UKD");
        DecimalFormat format1 = new DecimalFormat("000.000", decf1);
        DecimalFormat format2 = new DecimalFormat("000.000", decf2);
        Currency euro = Currency.getInstance("EUR");
        format1.setCurrency(euro);
        format2.setCurrency(euro);
        assertEquals("Reset with currency symbol", format1, format2);
    }

    /*
     * Testing the method public StringBuffer format(Object number, ...)
     */
    @Test
    public void TestFormat() {
        NumberFormat nf = NumberFormat.getInstance();
        StringBuffer sb = new StringBuffer("dummy");
        FieldPosition fp = new FieldPosition(0);

        // Tests when "if (number instanceof Long)" is true
        try {
            nf.format(new Long("0"), sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "return an exception for a Long object. Error: " + e);
        }

        // Tests when "else if (number instanceof BigInteger)" is true
        try {
            nf.format((Object)new BigInteger("0"), sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "return an exception for a BigInteger object. Error: " + e);
        }

        // Tests when "else if (number instanceof java.math.BigDecimal)" is true
        try {
            nf.format((Object)new java.math.BigDecimal("0"), sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "return an exception for a java.math.BigDecimal object. Error: " + e);
        }

        // Tests when "else if (number instanceof com.ibm.icu.math.BigDecimal)" is true
        try {
            nf.format((Object)new com.ibm.icu.math.BigDecimal("0"), sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "return an exception for a com.ibm.icu.math.BigDecimal object. Error: " + e);
        }

        // Tests when "else if (number instanceof CurrencyAmount)" is true
        try {
            CurrencyAmount ca = new CurrencyAmount(0.0, Currency.getInstance(new ULocale("en_US")));
            nf.format((Object)ca, sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "return an exception for a CurrencyAmount object. Error: " + e);
        }

        // Tests when "else if (number instanceof Number)" is true
        try {
            nf.format(0.0, sb, fp);
        } catch (Exception e) {
            errln("NumberFormat.format(Object number, ...) was not suppose to "
                    + "to return an exception for a Number object. Error: " + e);
        }

        // Tests when "else" is true
        try {
            nf.format(new Object(), sb, fp);
            errln("NumberFormat.format(Object number, ...) was suppose to "
                    + "return an exception for an invalid object.");
        } catch (Exception e) {
        }

        try {
            nf.format(new String("dummy"), sb, fp);
            errln("NumberFormat.format(Object number, ...) was suppose to "
                    + "return an exception for an invalid object.");
        } catch (Exception e) {
        }
    }

    /*
     * Coverage tests for the implementation of abstract format methods not being called otherwise
     */
    @Test
    public void TestFormatAbstractImplCoverage() {
        NumberFormat df = DecimalFormat.getInstance(Locale.ENGLISH);
        NumberFormat cdf = CompactDecimalFormat.getInstance(Locale.ENGLISH, CompactDecimalFormat.CompactStyle.SHORT);
        NumberFormat rbf = new RuleBasedNumberFormat(ULocale.ENGLISH, RuleBasedNumberFormat.SPELLOUT);

        /*
         *  Test  NumberFormat.format(BigDecimal,StringBuffer,FieldPosition)
         */
        StringBuffer sb = new StringBuffer();
        String result = df.format(new BigDecimal(2000.43), sb, new FieldPosition(0)).toString();
        if (!"2,000.43".equals(result)) {
            errln("DecimalFormat failed. Expected: 2,000.43 - Actual: " + result);
        }

        sb.delete(0, sb.length());
        result = cdf.format(new BigDecimal(2000.43), sb, new FieldPosition(0)).toString();
        if (!"2K".equals(result)) {
            errln("DecimalFormat failed. Expected: 2K - Actual: " + result);
        }

        sb.delete(0, sb.length());
        result = rbf.format(new BigDecimal(2000.43), sb, new FieldPosition(0)).toString();
        if (!"two thousand point four three".equals(result)) {
            errln("DecimalFormat failed. Expected: 'two thousand point four three' - Actual: '" + result + "'");
        }
    }

    /*
     * Tests the method public final static NumberFormat getInstance(int style) public static NumberFormat
     * getInstance(Locale inLocale, int style) public static NumberFormat getInstance(ULocale desiredLocale, int choice)
     */
    @Test
    public void TestGetInstance() {
        // Tests "public final static NumberFormat getInstance(int style)"
        int maxStyle = NumberFormat.STANDARDCURRENCYSTYLE;

        int[] invalid_cases = { NumberFormat.NUMBERSTYLE - 1, NumberFormat.NUMBERSTYLE - 2,
                maxStyle + 1, maxStyle + 2 };

        for (int i = NumberFormat.NUMBERSTYLE; i < maxStyle; i++) {
            try {
                NumberFormat.getInstance(i);
            } catch (Exception e) {
                errln("NumberFormat.getInstance(int style) was not suppose to "
                        + "return an exception for passing value of " + i);
            }
        }

        for (int i = 0; i < invalid_cases.length; i++) {
            try {
                NumberFormat.getInstance(invalid_cases[i]);
                errln("NumberFormat.getInstance(int style) was suppose to "
                        + "return an exception for passing value of " + invalid_cases[i]);
            } catch (Exception e) {
            }
        }

        // Tests "public static NumberFormat getInstance(Locale inLocale, int style)"
        String[] localeCases = { "en_US", "fr_FR", "de_DE", "jp_JP" };

        for (int i = NumberFormat.NUMBERSTYLE; i < maxStyle; i++) {
            for (int j = 0; j < localeCases.length; j++) {
                try {
                    NumberFormat.getInstance(new Locale(localeCases[j]), i);
                } catch (Exception e) {
                    errln("NumberFormat.getInstance(Locale inLocale, int style) was not suppose to "
                            + "return an exception for passing value of " + localeCases[j] + ", " + i);
                }
            }
        }

        // Tests "public static NumberFormat getInstance(ULocale desiredLocale, int choice)"
        // Tests when "if (choice < NUMBERSTYLE || choice > PLURALCURRENCYSTYLE)" is true
        for (int i = 0; i < invalid_cases.length; i++) {
            try {
                NumberFormat.getInstance((ULocale) null, invalid_cases[i]);
                errln("NumberFormat.getInstance(ULocale inLocale, int choice) was not suppose to "
                        + "return an exception for passing value of " + invalid_cases[i]);
            } catch (Exception e) {
            }
        }
    }

    /*
     * Tests the class public static abstract class NumberFormatFactory
     */
    @Test
    public void TestNumberFormatFactory() {
        /*
         * The following class allows the method public NumberFormat createFormat(Locale loc, int formatType) to be
         * tested.
         */
        class TestFactory extends NumberFormatFactory {
            @Override
            public Set<String> getSupportedLocaleNames() {
                return null;
            }

            @Override
            public NumberFormat createFormat(ULocale loc, int formatType) {
                return null;
            }
        }

        /*
         * The following class allows the method public NumberFormat createFormat(ULocale loc, int formatType) to be
         * tested.
         */
        class TestFactory1 extends NumberFormatFactory {
            @Override
            public Set<String> getSupportedLocaleNames() {
                return null;
            }

            @Override
            public NumberFormat createFormat(Locale loc, int formatType) {
                return null;
            }
        }

        TestFactory tf = new TestFactory();
        TestFactory1 tf1 = new TestFactory1();

        /*
         * Tests the method public boolean visible()
         */
        if (tf.visible() != true) {
            errln("NumberFormatFactory.visible() was suppose to return true.");
        }

        /*
         * Tests the method public NumberFormat createFormat(Locale loc, int formatType)
         */
        if (tf.createFormat(new Locale(""), 0) != null) {
            errln("NumberFormatFactory.createFormat(Locale loc, int formatType) " + "was suppose to return null");
        }

        /*
         * Tests the method public NumberFormat createFormat(ULocale loc, int formatType)
         */
        if (tf1.createFormat(new ULocale(""), 0) != null) {
            errln("NumberFormatFactory.createFormat(ULocale loc, int formatType) " + "was suppose to return null");
        }
    }

    /*
     * Tests the class public static abstract class SimpleNumberFormatFactory extends NumberFormatFactory
     */
    @Test
    public void TestSimpleNumberFormatFactory() {
        class TestSimpleNumberFormatFactory extends SimpleNumberFormatFactory {
            /*
             * Tests the method public SimpleNumberFormatFactory(Locale locale)
             */
            TestSimpleNumberFormatFactory() {
                super(new Locale(""));
            }
        }
        @SuppressWarnings("unused")
        TestSimpleNumberFormatFactory tsnff = new TestSimpleNumberFormatFactory();
    }

    /*
     * Tests the method public static ULocale[] getAvailableLocales()
     */
    @SuppressWarnings("static-access")
    @Test
    public void TestGetAvailableLocales() {
        // Tests when "if (shim == null)" is true
        @SuppressWarnings("serial")
        class TestGetAvailableLocales extends NumberFormat {
            @Override
            public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public Number parse(String text, ParsePosition parsePosition) {
                return null;
            }
        }

        try {
            TestGetAvailableLocales test = new TestGetAvailableLocales();
            test.getAvailableLocales();
        } catch (Exception e) {
            errln("NumberFormat.getAvailableLocales() was not suppose to "
                    + "return an exception when getting getting available locales.");
        }
    }

    /*
     * Tests the method public void setMinimumIntegerDigits(int newValue)
     */
    @Test
    public void TestSetMinimumIntegerDigits() {
        NumberFormat nf = NumberFormat.getInstance();
        // For valid array, it is displayed as {min value, max value}
        // Tests when "if (minimumIntegerDigits > maximumIntegerDigits)" is true
        int[][] cases = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 2, 0 }, { 2, 1 }, { 10, 0 } };
        int[] expectedMax = { 1, 1, 0, 0, 1, 0 };
        if (cases.length != expectedMax.length) {
            errln("Can't continue test case method TestSetMinimumIntegerDigits "
                    + "since the test case arrays are unequal.");
        } else {
            for (int i = 0; i < cases.length; i++) {
                nf.setMinimumIntegerDigits(cases[i][0]);
                nf.setMaximumIntegerDigits(cases[i][1]);
                if (nf.getMaximumIntegerDigits() != expectedMax[i]) {
                    errln("NumberFormat.setMinimumIntegerDigits(int newValue "
                            + "did not return an expected result for parameter " + cases[i][0] + " and " + cases[i][1]
                                    + " and expected " + expectedMax[i] + " but got " + nf.getMaximumIntegerDigits());
                }
            }
        }
    }

    /*
     * Tests the method public int getRoundingMode() public void setRoundingMode(int roundingMode)
     */
    @Test
    public void TestRoundingMode() {
        @SuppressWarnings("serial")
        class TestRoundingMode extends NumberFormat {
            @Override
            public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                return null;
            }

            @Override
            public Number parse(String text, ParsePosition parsePosition) {
                return null;
            }
        }
        TestRoundingMode tgrm = new TestRoundingMode();

        // Tests the function 'public void setRoundingMode(int roundingMode)'
        try {
            tgrm.setRoundingMode(0);
            errln("NumberFormat.setRoundingMode(int) was suppose to return an exception");
        } catch (Exception e) {
        }

        // Tests the function 'public int getRoundingMode()'
        try {
            tgrm.getRoundingMode();
            errln("NumberFormat.getRoundingMode() was suppose to return an exception");
        } catch (Exception e) {
        }
    }

    /*
     * Testing lenient decimal/grouping separator parsing
     */
    @Test
    public void TestLenientSymbolParsing() {
        DecimalFormat fmt = new DecimalFormat();
        DecimalFormatSymbols sym = new DecimalFormatSymbols();

        expect(fmt, "12\u300234", 12.34);

        // Ticket#7345 - case 1
        // Even strict parsing, the decimal separator set in the symbols
        // should be successfully parsed.

        sym.setDecimalSeparator('\u3002');

        // non-strict
        fmt.setDecimalFormatSymbols(sym);

        // strict - failed before the fix for #7345
        fmt.setParseStrict(true);
        expect(fmt, "23\u300245", 23.45);
        fmt.setParseStrict(false);


        // Ticket#7345 - case 2
        // Decimal separator variants other than DecimalFormatSymbols.decimalSeparator
        // should not hide the grouping separator DecimalFormatSymbols.groupingSeparator.
        sym.setDecimalSeparator('.');
        sym.setGroupingSeparator(',');
        fmt.setDecimalFormatSymbols(sym);

        expect(fmt, "1,234.56", 1234.56);

        sym.setGroupingSeparator('\uFF61');
        fmt.setDecimalFormatSymbols(sym);

        expect(fmt, "2\uFF61345.67", 2345.67);

        // Ticket#7128
        //
        sym.setGroupingSeparator(',');
        fmt.setDecimalFormatSymbols(sym);

        String skipExtSepParse = ICUConfig.get("com.ibm.icu.text.DecimalFormat.SkipExtendedSeparatorParsing", "false");
        if (skipExtSepParse.equals("true")) {
            // When the property SkipExtendedSeparatorParsing is true,
            // DecimalFormat does not use the extended equivalent separator
            // data and only uses the one in DecimalFormatSymbols.
            expect(fmt, "23 456", 23);
        } else {
            // Lenient separator parsing is enabled by default.
            // A space character below is interpreted as a
            // group separator, even ',' is used as grouping
            // separator in the symbols.
            expect(fmt, "12 345", 12345);
        }
    }

    /*
     * Testing currency driven max/min fraction digits problem
     * reported by ticket#7282
     */
    @Test
    public void TestCurrencyFractionDigits() {
        double value = 99.12345;

        // Create currency instance
        NumberFormat cfmt  = NumberFormat.getCurrencyInstance(new ULocale("ja_JP"));
        String text1 = cfmt.format(value);

        // Reset the same currency and format the test value again
        cfmt.setCurrency(cfmt.getCurrency());
        String text2 = cfmt.format(value);

        // output1 and output2 must be identical
        if (!text1.equals(text2)) {
            errln("NumberFormat.format() should return the same result - text1="
                    + text1 + " text2=" + text2);
        }
    }

    /*
     * Testing rounding to negative zero problem
     * reported by ticket#7609
     */
    @Test
    public void TestNegZeroRounding() {

        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
        df.setRoundingMode(MathContext.ROUND_HALF_UP);
        df.setMinimumFractionDigits(1);
        df.setMaximumFractionDigits(1);
        String text1 = df.format(-0.01);

        df.setRoundingIncrement(0.1);
        String text2 = df.format(-0.01);

        // output1 and output2 must be identical
        if (!text1.equals(text2)) {
            errln("NumberFormat.format() should return the same result - text1="
                    + text1 + " text2=" + text2);
        }

    }

    @Test
    public void TestCurrencyAmountCoverage() {
        CurrencyAmount ca, cb;

        try {
            ca = new CurrencyAmount(null, (Currency) null);
            errln("NullPointerException should have been thrown.");
        } catch (NullPointerException ex) {
        }
        try {
            ca = new CurrencyAmount(new Integer(0), (Currency) null);
            errln("NullPointerException should have been thrown.");
        } catch (NullPointerException ex) {
        }

        ca = new CurrencyAmount(new Integer(0), Currency.getInstance(new ULocale("ja_JP")));
        cb = new CurrencyAmount(new Integer(1), Currency.getInstance(new ULocale("ja_JP")));
        if (ca.equals(null)) {
            errln("Comparison should return false.");
        }
        if (!ca.equals(ca)) {
            errln("Comparision should return true.");
        }
        if (ca.equals(cb)) {
            errln("Comparison should return false.");
        }
    }

    @Test
    public void TestExponentParse() {
        ParsePosition parsePos = new ParsePosition(0);
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
        DecimalFormat fmt = new DecimalFormat("#####", symbols);
        Number result = fmt.parse("5.06e-27", parsePos);
        if ( result.doubleValue() != 5.06E-27 || parsePos.getIndex() != 8) {
            errln("ERROR: ERROR: parse failed - expected 5.06E-27, 8; got " + result.doubleValue() + ", " + parsePos.getIndex());
        }
    }

    @Test
    public void TestExplicitParents() {
        // We use these for testing because decimal and grouping separators will be inherited from es_419
        // starting with CLDR 2.0
        String[] DATA = {
                "es", "CO", "", "1.250,75",
                "es", "ES", "", "1.250,75",
                "es", "GQ", "", "1.250,75",
                "es", "MX", "", "1,250.75",
                "es", "US", "", "1,250.75",
                "es", "VE", "", "1.250,75",

        };

        for (int i=0; i<DATA.length; i+=4) {
            Locale locale = new Locale(DATA[i], DATA[i+1], DATA[i+2]);
            NumberFormat fmt = NumberFormat.getInstance(locale);
            String s = fmt.format(1250.75);
            if (s.equals(DATA[i+3])) {
                logln("Ok: 1250.75 x " + locale + " => " + s);
            } else {
                errln("FAIL: 1250.75 x " + locale + " => " + s +
                        ", expected " + DATA[i+3]);
            }
        }
    }

    /*
     * Test case for #9240
     * ICU4J 49.1 DecimalFormat did not clone the internal object holding
     * formatted text attribute information properly. Therefore, DecimalFormat
     * created by cloning may return incorrect results or may throw an exception
     * when formatToCharacterIterator is invoked from multiple threads.
     */
    @Test
    public void TestFormatToCharacterIteratorThread() {
        final int COUNT = 10;

        DecimalFormat fmt1 = new DecimalFormat("#0");
        DecimalFormat fmt2 = (DecimalFormat)fmt1.clone();

        int[] res1 = new int[COUNT];
        int[] res2 = new int[COUNT];

        Thread t1 = new Thread(new FormatCharItrTestThread(fmt1, 1, res1));
        Thread t2 = new Thread(new FormatCharItrTestThread(fmt2, 100, res2));

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            //TODO
        }

        int val1 = res1[0];
        int val2 = res2[0];

        for (int i = 0; i < COUNT; i++) {
            if (res1[i] != val1) {
                errln("Inconsistent first run limit in test thread 1");
            }
            if (res2[i] != val2) {
                errln("Inconsistent first run limit in test thread 2");
            }
        }
    }

    /*
     * This feature had to do with a limitation in DigitList.java that no longer exists in the
     * new implementation.
     *
    @Test
    public void TestParseMaxDigits() {
        DecimalFormat fmt = new DecimalFormat();
        String number = "100000000000";
        int newParseMax = number.length() - 1;

        fmt.setParseMaxDigits(-1);

        // Default value is 1000
        if (fmt.getParseMaxDigits() != 1000) {
            errln("Fail valid value checking in setParseMaxDigits.");
        }

        try {
            if (fmt.parse(number).doubleValue() == Float.POSITIVE_INFINITY) {
                errln("Got Infinity but should NOT when parsing number: " + number);
            }

            fmt.setParseMaxDigits(newParseMax);

            if (fmt.parse(number).doubleValue() != Float.POSITIVE_INFINITY) {
                errln("Did not get Infinity but should when parsing number: " + number);
            }
        } catch (ParseException ex) {

        }
    }
    */

    private static class FormatCharItrTestThread implements Runnable {
        private final NumberFormat fmt;
        private final int num;
        private final int[] result;

        FormatCharItrTestThread(NumberFormat fmt, int num, int[] result) {
            this.fmt = fmt;
            this.num = num;
            this.result = result;
        }

        @Override
        public void run() {
            for (int i = 0; i < result.length; i++) {
                AttributedCharacterIterator acitr = fmt.formatToCharacterIterator(num);
                acitr.first();
                result[i] = acitr.getRunLimit();
            }
        }
    }

    @Test
    public void TestRoundingBehavior() {
        final Object[][] TEST_CASES = {
                {
                    ULocale.US,                             // ULocale - null for default locale
                    "#.##",                                 // Pattern
                    Integer.valueOf(BigDecimal.ROUND_DOWN), // Rounding Mode or null (implicit)
                    Double.valueOf(0.0d),                   // Rounding increment, Double or BigDecimal, or null (implicit)
                    Double.valueOf(123.4567d),              // Input value, Long, Double, BigInteger or BigDecimal
                    "123.45"                                // Expected result, null for exception
                },
                {
                    ULocale.US,
                    "#.##",
                    null,
                    Double.valueOf(0.1d),
                    Double.valueOf(123.4567d),
                    "123.5"
                },
                {
                    ULocale.US,
                    "#.##",
                    Integer.valueOf(BigDecimal.ROUND_DOWN),
                    Double.valueOf(0.1d),
                    Double.valueOf(123.4567d),
                    "123.4"
                },
                {
                    ULocale.US,
                    "#.##",
                    Integer.valueOf(BigDecimal.ROUND_UNNECESSARY),
                    null,
                    Double.valueOf(123.4567d),
                    null
                },
                {
                    ULocale.US,
                    "#.##",
                    Integer.valueOf(BigDecimal.ROUND_DOWN),
                    null,
                    Long.valueOf(1234),
                    "1234"
                },
        };

        int testNum = 1;

        for (Object[] testCase : TEST_CASES) {
            // 0: locale
            // 1: pattern
            ULocale locale = testCase[0] == null ? ULocale.getDefault() : (ULocale)testCase[0];
            String pattern = (String)testCase[1];

            DecimalFormat fmt = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance(locale));

            // 2: rounding mode
            Integer roundingMode = null;
            if (testCase[2] != null) {
                roundingMode = (Integer)testCase[2];
                fmt.setRoundingMode(roundingMode);
            }

            // 3: rounding increment
            if (testCase[3] != null) {
                if (testCase[3] instanceof Double) {
                    fmt.setRoundingIncrement((Double)testCase[3]);
                } else if (testCase[3] instanceof BigDecimal) {
                    fmt.setRoundingIncrement((BigDecimal)testCase[3]);
                } else if (testCase[3] instanceof java.math.BigDecimal) {
                    fmt.setRoundingIncrement((java.math.BigDecimal)testCase[3]);
                }
            }

            // 4: input number
            String s = null;
            boolean bException = false;
            try {
                s = fmt.format(testCase[4]);
            } catch (ArithmeticException e) {
                bException = true;
            }

            if (bException) {
                if (testCase[5] != null) {
                    errln("Test case #" + testNum + ": ArithmeticException was thrown.");
                }
            } else {
                if (testCase[5] == null) {
                    errln("Test case #" + testNum +
                            ": ArithmeticException must be thrown, but got formatted result: " +
                            s);
                } else {
                    assertEquals("Test case #" + testNum, testCase[5], s);
                }
            }

            testNum++;
        }
    }

    @Test
    public void TestSignificantDigits() {
        double input[] = {
                0, 0,
                123, -123,
                12345, -12345,
                123.45, -123.45,
                123.44501, -123.44501,
                0.001234, -0.001234,
                0.00000000123, -0.00000000123,
                0.0000000000000000000123, -0.0000000000000000000123,
                1.2, -1.2,
                0.0000000012344501, -0.0000000012344501,
                123445.01, -123445.01,
                12344501000000000000000000000000000.0, -12344501000000000000000000000000000.0,
        };
        String[] expected = {
                "0.00", "0.00",
                "123", "-123",
                "12345", "-12345",
                "123.45", "-123.45",
                "123.45", "-123.45",
                "0.001234", "-0.001234",
                "0.00000000123", "-0.00000000123",
                "0.0000000000000000000123", "-0.0000000000000000000123",
                "1.20", "-1.20",
                "0.0000000012345", "-0.0000000012345",
                "123450", "-123450",
                "12345000000000000000000000000000000", "-12345000000000000000000000000000000",
        };
        DecimalFormat numberFormat =
                (DecimalFormat) NumberFormat.getInstance(ULocale.US);
        numberFormat.setSignificantDigitsUsed(true);
        numberFormat.setMinimumSignificantDigits(3);
        numberFormat.setMaximumSignificantDigits(5);
        numberFormat.setGroupingUsed(false);
        for (int i = 0; i < input.length; i++) {
            assertEquals("TestSignificantDigits", expected[i], numberFormat.format(input[i]));
        }

        // Test for ICU-20063
        {
            DecimalFormat df = new DecimalFormat("0.######", DecimalFormatSymbols.getInstance(ULocale.US));
            df.setSignificantDigitsUsed(true);
            expect(df, 9.87654321, "9.87654");
            df.setMaximumSignificantDigits(3);
            expect(df, 9.87654321, "9.88");
            // setSignificantDigitsUsed with maxSig only
            df.setSignificantDigitsUsed(true);
            expect(df, 9.87654321, "9.88");
            df.setMinimumSignificantDigits(2);
            expect(df, 9, "9.0");
            // setSignificantDigitsUsed with both minSig and maxSig
            df.setSignificantDigitsUsed(true);
            expect(df, 9, "9.0");
            // setSignificantDigitsUsed to false: should revert to fraction rounding
            df.setSignificantDigitsUsed(false);
            expect(df, 9.87654321, "9.876543");
            expect(df, 9, "9");
            df.setSignificantDigitsUsed(true);
            df.setMinimumSignificantDigits(2);
            expect(df, 9.87654321, "9.87654");
            expect(df, 9, "9.0");
            // setSignificantDigitsUsed with minSig only
            df.setSignificantDigitsUsed(true);
            expect(df, 9.87654321, "9.87654");
            expect(df, 9, "9.0");
        }
    }

    @Test
    public void TestSetMaxFracAndRoundIncr() {
        class SetMxFrAndRndIncrItem {
            String descrip;
            String localeID;
            int    style;
            int    minInt;
            int    minFrac;
            int    maxFrac;
            double roundIncr;
            String expPattern;
            double valueToFmt;
            String expFormat;
             // Simple constructor
            public SetMxFrAndRndIncrItem(String desc, String loc, int stl, int mnI, int mnF, int mxF,
                                                double rdIn, String ePat, double val, String eFmt) {
                descrip = desc;
                localeID = loc;
                style = stl;
                minInt = mnI;
                minFrac = mnF;
                maxFrac = mxF;
                roundIncr = rdIn;
                expPattern = ePat;
                valueToFmt = val;
                expFormat = eFmt ;
            }
        };

        final SetMxFrAndRndIncrItem[] items = {
            //                         descrip                     locale   style                      mnI mnF mxF rdInc   expPat        value  expFmt
            new SetMxFrAndRndIncrItem( "01 en_US DEC 1/0/3/0.0",    "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  3, 0.0,    "#,##0.###",  0.128, "0.128" ),
            new SetMxFrAndRndIncrItem( "02 en_US DEC 1/0/1/0.0",    "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  1, 0.0,    "#,##0.#",    0.128, "0.1"   ),
            new SetMxFrAndRndIncrItem( "03 en_US DEC 1/0/1/0.01",   "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  1, 0.01,   "#,##0.#",    0.128, "0.1"   ),
            new SetMxFrAndRndIncrItem( "04 en_US DEC 1/1/1/0.01",   "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  1, 0.01,   "#,##0.0",    0.128, "0.1"   ),
            new SetMxFrAndRndIncrItem( "05 en_US DEC 1/0/1/0.1",    "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  1, 0.1,    "#,##0.1",    0.128, "0.1"   ), // use incr
            new SetMxFrAndRndIncrItem( "06 en_US DEC 1/1/1/0.1",    "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  1, 0.1,    "#,##0.1",    0.128, "0.1"   ), // use incr

            new SetMxFrAndRndIncrItem( "10 en_US DEC 1/0/1/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  1, 0.02,   "#,##0.#",    0.128, "0.1"   ),
            new SetMxFrAndRndIncrItem( "11 en_US DEC 1/0/2/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  2, 0.02,   "#,##0.02",   0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "12 en_US DEC 1/0/3/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  0,  3, 0.02,   "#,##0.02#",  0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "13 en_US DEC 1/1/1/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  1, 0.02,   "#,##0.0",    0.128, "0.1"   ),
            new SetMxFrAndRndIncrItem( "14 en_US DEC 1/1/2/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  2, 0.02,   "#,##0.02",   0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "15 en_US DEC 1/1/3/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  3, 0.02,   "#,##0.02#",  0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "16 en_US DEC 1/2/2/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  2, 0.02,   "#,##0.02",   0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "17 en_US DEC 1/2/3/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  3, 0.02,   "#,##0.02#",  0.128, "0.12"  ), // use incr
            new SetMxFrAndRndIncrItem( "18 en_US DEC 1/3/3/0.02",   "en_US", NumberFormat.NUMBERSTYLE,  1,  3,  3, 0.02,   "#,##0.020",  0.128, "0.12"  ), // use incr; expFmt != ICU4C

            new SetMxFrAndRndIncrItem( "20 en_US DEC 1/1/1/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  1, 0.0075, "#,##0.0",    0.019, "0.0"    ),
            new SetMxFrAndRndIncrItem( "21 en_US DEC 1/1/2/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  2, 0.0075, "#,##0.0075", 0.004, "0.0075" ), // use incr
            new SetMxFrAndRndIncrItem( "22 en_US DEC 1/1/2/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  2, 0.0075, "#,##0.0075", 0.019, "0.0225" ), // use incr
            new SetMxFrAndRndIncrItem( "23 en_US DEC 1/1/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  3, 0.0075, "#,##0.0075", 0.004, "0.0075" ), // use incr
            new SetMxFrAndRndIncrItem( "24 en_US DEC 1/1/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  1,  3, 0.0075, "#,##0.0075", 0.019, "0.0225" ), // use incr
            new SetMxFrAndRndIncrItem( "25 en_US DEC 1/2/2/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  2, 0.0075, "#,##0.0075", 0.004, "0.0075" ), // use incr
            new SetMxFrAndRndIncrItem( "26 en_US DEC 1/2/2/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  2, 0.0075, "#,##0.0075", 0.019, "0.0225" ), // use incr
            new SetMxFrAndRndIncrItem( "27 en_US DEC 1/2/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  3, 0.0075, "#,##0.0075", 0.004, "0.0075" ), // use incr
            new SetMxFrAndRndIncrItem( "28 en_US DEC 1/2/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  2,  3, 0.0075, "#,##0.0075", 0.019, "0.0225" ), // use incr
            new SetMxFrAndRndIncrItem( "29 en_US DEC 1/3/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  3,  3, 0.0075, "#,##0.0075", 0.004, "0.0075" ), // use incr
            new SetMxFrAndRndIncrItem( "2A en_US DEC 1/3/3/0.0075", "en_US", NumberFormat.NUMBERSTYLE,  1,  3,  3, 0.0075, "#,##0.0075", 0.019, "0.0225" ), // use incr
        };

        for (SetMxFrAndRndIncrItem item: items) {
            ULocale locale = new ULocale(item.localeID);
            DecimalFormat df = (DecimalFormat)NumberFormat.getInstance(locale, item.style);
            df.setMinimumIntegerDigits(item.minInt);
            df.setMinimumFractionDigits(item.minFrac);
            df.setMaximumFractionDigits(item.maxFrac);
            df.setRoundingIncrement(item.roundIncr);

            boolean roundIncrUsed = (item.roundIncr != 0 &&
                    !PatternStringUtils.ignoreRoundingIncrement(java.math.BigDecimal.valueOf(item.roundIncr),item.maxFrac));
            int fracForRoundIncr = 0;
            if (roundIncrUsed) {
                double  testIncr = item.roundIncr;
                for (; testIncr > ((int)testIncr); testIncr *= 10.0, fracForRoundIncr++);
            }

            int minInt = df.getMinimumIntegerDigits();
            if (minInt != item.minInt) {
                errln("test " + item.descrip + ": getMinimumIntegerDigits, expected " + item.minInt + ", got " + minInt);
            }
            int minFrac = df.getMinimumFractionDigits();
            int expMinFrac = (roundIncrUsed)? fracForRoundIncr: item.minFrac;
            if (minFrac != expMinFrac) {
                errln("test " + item.descrip + ": getMinimumFractionDigits, expected " + expMinFrac + ", got " + minFrac);
            }
            int maxFrac = df.getMaximumFractionDigits();
            int expMaxFrac = (roundIncrUsed)? fracForRoundIncr: item.maxFrac;
            if (maxFrac != expMaxFrac) {
                errln("test " + item.descrip + ": getMaximumFractionDigits, expected " + expMaxFrac + ", got " + maxFrac);
            }
            java.math.BigDecimal bigdec = df.getRoundingIncrement(); // why doesn't this return com.ibm.icu.math.BigDecimal?
            double roundIncr = (bigdec != null)? bigdec.doubleValue(): 0.0;
            double expRoundIncr = (roundIncrUsed)? item.roundIncr: 0.0;
            if (roundIncr != expRoundIncr) {
                errln("test " + item.descrip + ": getRoundingIncrement, expected " + expRoundIncr + ", got " + roundIncr);
            }

            String getPattern = df.toPattern();
            if (!getPattern.equals(item.expPattern)) {
                errln("test " + item.descrip + ": toPattern, expected " + item.expPattern + ", got " + getPattern);
            }
            String getFormat = df.format(item.valueToFmt);
            if (!getFormat.equals(item.expFormat)) {
                errln("test " + item.descrip + ": format, expected " + item.expFormat + ", got " + getFormat);
            }
        }
    }

    @Test
    public void TestBug9936() {
        DecimalFormat numberFormat =
                (DecimalFormat) NumberFormat.getInstance(ULocale.US);
        assertFalse("", numberFormat.areSignificantDigitsUsed());

        numberFormat.setSignificantDigitsUsed(true);
        assertTrue("", numberFormat.areSignificantDigitsUsed());

        numberFormat.setSignificantDigitsUsed(false);
        assertFalse("", numberFormat.areSignificantDigitsUsed());

        numberFormat.setMinimumSignificantDigits(3);
        assertTrue("", numberFormat.areSignificantDigitsUsed());

        numberFormat.setSignificantDigitsUsed(false);
        numberFormat.setMaximumSignificantDigits(6);
        assertTrue("", numberFormat.areSignificantDigitsUsed());
    }

    @Test
    public void TestShowZero() {
        DecimalFormat numberFormat =
                (DecimalFormat) NumberFormat.getInstance(ULocale.US);
        numberFormat.setSignificantDigitsUsed(true);
        numberFormat.setMaximumSignificantDigits(3);
        assertEquals("TestShowZero", "0", numberFormat.format(0.0));
    }

    @Test
    public void TestCurrencyPlurals() {
        String[][] tests = {
                {"en", "USD", "1", "1 US dollar"},
                {"en", "USD", "1.0", "1.0 US dollars"},
                {"en", "USD", "1.00", "1.00 US dollars"},
                {"en", "USD", "1.99", "1.99 US dollars"},
                {"en", "AUD", "1", "1 Australian dollar"},
                {"en", "AUD", "1.00", "1.00 Australian dollars"},
                {"sl", "USD", "1", "1 ameri\u0161ki dolar"},
                {"sl", "USD", "2", "2 ameri\u0161ka dolarja"},
                {"sl", "USD", "3", "3 ameri\u0161ki dolarji"},
                {"sl", "USD", "5", "5 ameriških dolarjev"},
                {"fr", "USD", "1.99", "1,99 dollar des États-Unis"},
                {"ru", "RUB", "1", "1 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0439 \u0440\u0443\u0431\u043B\u044C"},
                {"ru", "RUB", "2", "2 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0445 \u0440\u0443\u0431\u043B\u044F"},
                {"ru", "RUB", "5", "5 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0445 \u0440\u0443\u0431\u043B\u0435\u0439"},
        };
        for (String test[] : tests) {
            DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance(new ULocale(test[0]), NumberFormat.PLURALCURRENCYSTYLE);
            numberFormat.setCurrency(Currency.getInstance(test[1]));
            double number = Double.parseDouble(test[2]);
            int dotPos = test[2].indexOf('.');
            int decimals = dotPos < 0 ? 0 : test[2].length() - dotPos - 1;
            int digits = dotPos < 0 ? test[2].length() : test[2].length() - 1;
            numberFormat.setMaximumFractionDigits(decimals);
            numberFormat.setMinimumFractionDigits(decimals);
            String actual = numberFormat.format(number);
            assertEquals(test[0] + "\t" + test[1] + "\t" + test[2], test[3], actual);
            numberFormat.setMaximumSignificantDigits(digits);
            numberFormat.setMinimumSignificantDigits(digits);
            actual = numberFormat.format(number);
            assertEquals(test[0] + "\t" + test[1] + "\t" + test[2], test[3], actual);
        }
    }

    @Test
    public void TestCustomCurrencySignAndSeparator() {
        DecimalFormatSymbols custom = new DecimalFormatSymbols(ULocale.US);

        custom.setCurrencySymbol("*");
        custom.setMonetaryGroupingSeparator('^');
        custom.setMonetaryDecimalSeparator(':');

        DecimalFormat fmt = new DecimalFormat("\u00A4 #,##0.00", custom);

        final String numstr = "* 1^234:56";
        expect2(fmt, 1234.56, numstr);
    }

    @Test
    public void TestParseSignsAndMarks() {
        class SignsAndMarksItem {
            public String locale;
            public boolean lenient;
            public String numString;
            public double value;
             // Simple constructor
            public SignsAndMarksItem(String loc, boolean lnt, String numStr, double val) {
                locale = loc;
                lenient = lnt;
                numString = numStr;
                value = val;
            }
        };
        final SignsAndMarksItem[] items = {
            // *** Note, ICU4J lenient number parsing does not handle arbitrary whitespace, but can
            // treat some whitespace as a grouping separator. The cases marked *** below depend
            // on isGroupingUsed() being set for the locale, which in turn depends on grouping
            // separators being present in the decimalFormat pattern for the locale (& num sys).
            //
            //                    locale                lenient numString                               value
            new SignsAndMarksItem("en",                 false,  "12",                                    12 ),
            new SignsAndMarksItem("en",                 true,   "12",                                    12 ),
            new SignsAndMarksItem("en",                 false,  "-23",                                  -23 ),
            new SignsAndMarksItem("en",                 true,   "-23",                                  -23 ),
            new SignsAndMarksItem("en",                 true,   "- 23",                                 -23 ), // ***
            new SignsAndMarksItem("en",                 false,  "\u200E-23",                            -23 ),
            new SignsAndMarksItem("en",                 true,   "\u200E-23",                            -23 ),
            new SignsAndMarksItem("en",                 true,   "\u200E- 23",                           -23 ), // ***

            new SignsAndMarksItem("en@numbers=arab",    false,  "\u0663\u0664",                          34 ),
            new SignsAndMarksItem("en@numbers=arab",    true,   "\u0663\u0664",                          34 ),
            new SignsAndMarksItem("en@numbers=arab",    false,  "-\u0664\u0665",                        -45 ),
            new SignsAndMarksItem("en@numbers=arab",    true,   "-\u0664\u0665",                        -45 ),
            new SignsAndMarksItem("en@numbers=arab",    true,   "- \u0664\u0665",                       -45 ), // ***
            new SignsAndMarksItem("en@numbers=arab",    false,  "\u200F-\u0664\u0665",                  -45 ),
            new SignsAndMarksItem("en@numbers=arab",    true,   "\u200F-\u0664\u0665",                  -45 ),
            new SignsAndMarksItem("en@numbers=arab",    true,   "\u200F- \u0664\u0665",                 -45 ), // ***

            new SignsAndMarksItem("en@numbers=arabext", false,  "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("en@numbers=arabext", true,   "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("en@numbers=arabext", false,  "-\u06F6\u06F7",                        -67 ),
            new SignsAndMarksItem("en@numbers=arabext", true,   "-\u06F6\u06F7",                        -67 ),
            new SignsAndMarksItem("en@numbers=arabext", true,   "- \u06F6\u06F7",                       -67 ), // ***
            new SignsAndMarksItem("en@numbers=arabext", false,  "\u200E-\u200E\u06F6\u06F7",            -67 ),
            new SignsAndMarksItem("en@numbers=arabext", true,   "\u200E-\u200E\u06F6\u06F7",            -67 ),
            new SignsAndMarksItem("en@numbers=arabext", true,   "\u200E-\u200E \u06F6\u06F7",           -67 ), // ***

            new SignsAndMarksItem("he",                 false,  "12",                                    12 ),
            new SignsAndMarksItem("he",                 true,   "12",                                    12 ),
            new SignsAndMarksItem("he",                 false,  "-23",                                  -23 ),
            new SignsAndMarksItem("he",                 true,   "-23",                                  -23 ),
            new SignsAndMarksItem("he",                 true,   "- 23",                                 -23 ), // ***
            new SignsAndMarksItem("he",                 false,  "\u200E-23",                            -23 ),
            new SignsAndMarksItem("he",                 true,   "\u200E-23",                            -23 ),
            new SignsAndMarksItem("he",                 true,   "\u200E- 23",                           -23 ), // ***

            new SignsAndMarksItem("ar",                 false,  "\u0663\u0664",                          34 ),
            new SignsAndMarksItem("ar",                 true,   "\u0663\u0664",                          34 ),
            new SignsAndMarksItem("ar",                 false,  "-\u0664\u0665",                        -45 ),
            new SignsAndMarksItem("ar",                 true,   "-\u0664\u0665",                        -45 ),
            new SignsAndMarksItem("ar",                 true,   "- \u0664\u0665",                       -45 ), // ***
            new SignsAndMarksItem("ar",                 false,  "\u200F-\u0664\u0665",                  -45 ),
            new SignsAndMarksItem("ar",                 true,   "\u200F-\u0664\u0665",                  -45 ),
            new SignsAndMarksItem("ar",                 true,   "\u200F- \u0664\u0665",                 -45 ), // ***

            new SignsAndMarksItem("ar_MA",              false,  "12",                                    12 ),
            new SignsAndMarksItem("ar_MA",              true,   "12",                                    12 ),
            new SignsAndMarksItem("ar_MA",              false,  "-23",                                  -23 ),
            new SignsAndMarksItem("ar_MA",              true,   "-23",                                  -23 ),
            new SignsAndMarksItem("ar_MA",              true,   "- 23",                                 -23 ), // ***
            new SignsAndMarksItem("ar_MA",              false,  "\u200E-23",                            -23 ),
            new SignsAndMarksItem("ar_MA",              true,   "\u200E-23",                            -23 ),
            new SignsAndMarksItem("ar_MA",              true,   "\u200E- 23",                           -23 ), // ***

            new SignsAndMarksItem("fa",                 false,  "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("fa",                 true,   "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("fa",                 false,  "\u2212\u06F6\u06F7",                   -67 ),
            new SignsAndMarksItem("fa",                 true,   "\u2212\u06F6\u06F7",                   -67 ),
            new SignsAndMarksItem("fa",                 true,   "\u2212 \u06F6\u06F7",                  -67 ), // ***
            new SignsAndMarksItem("fa",                 false,  "\u200E\u2212\u200E\u06F6\u06F7",       -67 ),
            new SignsAndMarksItem("fa",                 true,   "\u200E\u2212\u200E\u06F6\u06F7",       -67 ),
            new SignsAndMarksItem("fa",                 true,   "\u200E\u2212\u200E \u06F6\u06F7",      -67 ), // ***

            new SignsAndMarksItem("ps",                 false,  "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("ps",                 true,   "\u06F5\u06F6",                          56 ),
            new SignsAndMarksItem("ps",                 false,  "-\u06F6\u06F7",                        -67 ),
            new SignsAndMarksItem("ps",                 true,   "-\u06F6\u06F7",                        -67 ),
            new SignsAndMarksItem("ps",                 true,   "- \u06F6\u06F7",                       -67 ), // ***
            new SignsAndMarksItem("ps",                 false,  "\u200E-\u200E\u06F6\u06F7",            -67 ),
            new SignsAndMarksItem("ps",                 true,   "\u200E-\u200E\u06F6\u06F7",            -67 ),
            new SignsAndMarksItem("ps",                 true,   "\u200E-\u200E \u06F6\u06F7",           -67 ), // ***
            new SignsAndMarksItem("ps",                 false,  "-\u200E\u06F6\u06F7",                  -67 ),
            new SignsAndMarksItem("ps",                 true,   "-\u200E\u06F6\u06F7",                  -67 ),
            new SignsAndMarksItem("ps",                 true,   "-\u200E \u06F6\u06F7",                 -67 ), // ***
        };
        for (SignsAndMarksItem item: items) {
            ULocale locale = new ULocale(item.locale);
            NumberFormat numfmt = NumberFormat.getInstance(locale);
            if (numfmt != null) {
                numfmt.setParseStrict(!item.lenient);
                ParsePosition ppos = new ParsePosition(0);
                Number num = numfmt.parse(item.numString, ppos);
                if (num != null && ppos.getIndex() == item.numString.length()) {
                    double parsedValue = num.doubleValue();
                    if (parsedValue != item.value) {
                        errln("FAIL: locale " + item.locale + ", lenient " + item.lenient + ", parse of \"" + item.numString + "\" gives value " + parsedValue);
                    }
                } else {
                    errln("FAIL: locale " + item.locale + ", lenient " + item.lenient + ", parse of \"" + item.numString + "\" gives position " + ppos.getIndex());
                }
            } else {
                errln("FAIL: NumberFormat.getInstance for locale " + item.locale);
            }
        }
    }

    @Test
    public void TestContext() {
        // just a minimal sanity check for now
        NumberFormat nfmt = NumberFormat.getInstance();
        DisplayContext context = nfmt.getContext(DisplayContext.Type.CAPITALIZATION);
        if (context != DisplayContext.CAPITALIZATION_NONE) {
            errln("FAIL: Initial NumberFormat.getContext() is not CAPITALIZATION_NONE");
        }
        nfmt.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
        context = nfmt.getContext(DisplayContext.Type.CAPITALIZATION);
        if (context != DisplayContext.CAPITALIZATION_FOR_STANDALONE) {
            errln("FAIL: NumberFormat.getContext() does not return the value set, CAPITALIZATION_FOR_STANDALONE");
        }
    }

    @Test
    public void TestAccountingCurrency() {
        String[][] tests = {
                //locale              num         curr fmt per loc     curr std fmt         curr acct fmt        rt
                {"en_US",             "1234.5",   "$1,234.50",         "$1,234.50",         "$1,234.50",         "true"},
                {"en_US@cf=account",  "1234.5",   "$1,234.50",         "$1,234.50",         "$1,234.50",         "true"},
                {"en_US",             "-1234.5",  "-$1,234.50",        "-$1,234.50",        "($1,234.50)",       "true"},
                {"en_US@cf=standard", "-1234.5",  "-$1,234.50",        "-$1,234.50",        "($1,234.50)",       "true"},
                {"en_US@cf=account",  "-1234.5",  "($1,234.50)",       "-$1,234.50",        "($1,234.50)",       "true"},
                {"en_US",             "0",        "$0.00",             "$0.00",             "$0.00",             "true"},
                {"en_US",             "-0.2",     "-$0.20",            "-$0.20",            "($0.20)",           "true"},
                {"en_US@cf=standard", "-0.2",     "-$0.20",            "-$0.20",            "($0.20)",           "true"},
                {"en_US@cf=account",  "-0.2",     "($0.20)",           "-$0.20",            "($0.20)",           "true"},
                {"ja_JP",             "10000",    "￥10,000",          "￥10,000",          "￥10,000",          "true" },
                {"ja_JP",             "-1000.5",  "-￥1,000",          "-￥1,000",          "(￥1,000)",         "false"},
                {"ja_JP@cf=account",  "-1000.5",  "(￥1,000)",         "-￥1,000",          "(￥1,000)",         "false"},
                {"de_DE",             "-23456.7", "-23.456,70\u00A0€", "-23.456,70\u00A0€", "-23.456,70\u00A0€", "true" },
                {"en_ID",             "1234.5",   "IDR 1,234.50",      "IDR 1,234.50",      "IDR 1,234.50",      "true"},
                {"en_ID@cf=account",  "1234.5",   "IDR 1,234.50",      "IDR 1,234.50",      "IDR 1,234.50",      "true"},
                {"en_ID@cf=standard", "1234.5",   "IDR 1,234.50",      "IDR 1,234.50",      "IDR 1,234.50",      "true"},
                {"en_ID",             "-1234.5",  "-IDR 1,234.50",     "-IDR 1,234.50",     "(IDR 1,234.50)",    "true"},
                {"en_ID@cf=account",  "-1234.5",  "(IDR 1,234.50)",    "-IDR 1,234.50",     "(IDR 1,234.50)",    "true"},
                {"en_ID@cf=standard", "-1234.5",  "-IDR 1,234.50",     "-IDR 1,234.50",     "(IDR 1,234.50)",    "true"},
                {"sh_ME",             "1234.5",   "1.234,50 €",        "1.234,50 €",        "1.234,50 €",        "true"},
                {"sh_ME@cf=account",  "1234.5",   "1.234,50 €",        "1.234,50 €",        "1.234,50 €",        "true"},
                {"sh_ME@cf=standard", "1234.5",   "1.234,50 €",        "1.234,50 €",        "1.234,50 €",        "true"},
                {"sh_ME",             "-1234.5",  "-1.234,50 €",       "-1.234,50 €",       "(1.234,50 €)",      "true"},
                {"sh_ME@cf=account",  "-1234.5",  "(1.234,50 €)",      "-1.234,50 €",       "(1.234,50 €)",      "true"},
                {"sh_ME@cf=standard", "-1234.5",  "-1.234,50 €",       "-1.234,50 €",       "(1.234,50 €)",      "true"},
        };
        for (String[] data : tests) {
            ULocale loc = new ULocale(data[0]);
            double num = Double.parseDouble(data[1]);
            String fmtPerLocExpected   = data[2];
            String fmtStandardExpected = data[3];
            String fmtAccountExpected  = data[4];
            boolean rt = Boolean.parseBoolean(data[5]);

            NumberFormat fmtPerLoc = NumberFormat.getInstance(loc, NumberFormat.CURRENCYSTYLE);
            expect(fmtPerLoc, num, fmtPerLocExpected, rt);

            NumberFormat fmtStandard = NumberFormat.getInstance(loc, NumberFormat.STANDARDCURRENCYSTYLE);
            expect(fmtStandard, num, fmtStandardExpected, rt);

            NumberFormat fmtAccount = NumberFormat.getInstance(loc, NumberFormat.ACCOUNTINGCURRENCYSTYLE);
            expect(fmtAccount, num, fmtAccountExpected, rt);
        }
    }

    /**
     * en_ID/sh_ME uses language only locales en/sh which requires NumberFormatServiceShim to fill in the currency, but
     * prior to ICU-20631, currency was not filled in for accounting, cash and standard, so currency placeholder was
     * used instead of the desired locale's currency.
     */
    @Test
    public void TestCurrencyFormatForMissingLocale() {
        ULocale loc = new ULocale("sh_ME");
        Currency cur = Currency.getInstance(loc);
        NumberFormat curFmt = NumberFormat.getInstance(loc, NumberFormat.CURRENCYSTYLE);
        assertNotNull("NumberFormat is missing currency instance for CURRENCYSTYLE", curFmt.getCurrency());
        assertEquals("Currency instance is not for the desired locale for CURRENCYSTYLE", cur, curFmt.getCurrency());
        assertEquals("NumberFormat format outputs wrong value for CURRENCYSTYLE", "-1.234,50 €", curFmt.format(-1234.5d));
        NumberFormat accFmt = NumberFormat.getInstance(loc, NumberFormat.ACCOUNTINGCURRENCYSTYLE);
        assertNotNull("NumberFormat is missing currency instance for ACCOUNTINGCURRENCYSTYLE", accFmt.getCurrency());
        assertEquals("Currency instance is not for the desired locale for ACCOUNTINGCURRENCYSTYLE", cur, accFmt.getCurrency());
        assertEquals("NumberFormat format outputs wrong value for ACCOUNTINGCURRENCYSTYLE", "(1.234,50 €)", accFmt.format(-1234.5d));
        NumberFormat cashFmt = NumberFormat.getInstance(loc, NumberFormat.CASHCURRENCYSTYLE);
        assertNotNull("NumberFormat is missing currency instance for CASHCURRENCYSTYLE", cashFmt.getCurrency());
        assertEquals("Currency instance is not for the desired locale for CASHCURRENCYSTYLE", cur, cashFmt.getCurrency());
        assertEquals("NumberFormat format outputs wrong value for CASHCURRENCYSTYLE", "-1.234,50 €", cashFmt.format(-1234.5d));
        NumberFormat stdFmt = NumberFormat.getInstance(loc, NumberFormat.STANDARDCURRENCYSTYLE);
        assertNotNull("NumberFormat is missing currency instance for STANDARDCURRENCYSTYLE", stdFmt.getCurrency());
        assertEquals("Currency instance is not for the desired locale for STANDARDCURRENCYSTYLE", cur, stdFmt.getCurrency());
        assertEquals("NumberFormat format outputs wrong value for STANDARDCURRENCYSTYLE", "-1.234,50 €", stdFmt.format(-1234.5d));
    }

    @Test
    public void TestCurrencyUsage() {
        // the 1st one is checking setter/getter, while the 2nd one checks for getInstance
        // compare the Currency and Currency Cash Digits
        // Note that as of CLDR 26:
        // * TWD and PKR switched from 0 decimals to 2; ISK still has 0, so change test to that
        // * CAD rounds to .05 in the cash style only.
        for (int i = 0; i < 2; i++) {
            String original_expected = "ISK 124";
            DecimalFormat custom = null;
            if (i == 0) {
                custom = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=ISK"),
                        DecimalFormat.CURRENCYSTYLE);

                String original = custom.format(123.567);
                assertEquals("Test Currency Context", original_expected, original);

                // test the getter
                assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(),
                        Currency.CurrencyUsage.STANDARD);
                custom.setCurrencyUsage(Currency.CurrencyUsage.CASH);
                assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(), Currency.CurrencyUsage.CASH);
            } else {
                custom = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=ISK"),
                        DecimalFormat.CASHCURRENCYSTYLE);

                // test the getter
                assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(), Currency.CurrencyUsage.CASH);
            }

            String cash_currency = custom.format(123.567);
            String cash_currency_expected = "ISK 124";
            assertEquals("Test Currency Context", cash_currency_expected, cash_currency);
        }

        // the 1st one is checking setter/getter, while the 2nd one checks for getInstance
        // compare the Currency and Currency Cash Rounding
        for (int i = 0; i < 2; i++) {
            String original_rounding_expected = "CA$123.57";
            DecimalFormat fmt = null;
            if (i == 0) {
                fmt = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=CAD"),
                        DecimalFormat.CURRENCYSTYLE);

                String original_rounding = fmt.format(123.566);
                assertEquals("Test Currency Context", original_rounding_expected, original_rounding);

                fmt.setCurrencyUsage(Currency.CurrencyUsage.CASH);
            } else {
                fmt = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=CAD"),
                        DecimalFormat.CASHCURRENCYSTYLE);
            }

            String cash_rounding_currency = fmt.format(123.567);
            String cash__rounding_currency_expected = "CA$123.55";
            assertEquals("Test Currency Context", cash__rounding_currency_expected, cash_rounding_currency);
        }

        // the 1st one is checking setter/getter, while the 2nd one checks for getInstance
        // Test the currency change
        for (int i = 0; i < 2; i++) {
            DecimalFormat fmt2 = null;
            if (i == 1) {
                fmt2 = (DecimalFormat) NumberFormat.getInstance(new ULocale("en_US@currency=JPY"),
                        NumberFormat.CURRENCYSTYLE);
                fmt2.setCurrencyUsage(Currency.CurrencyUsage.CASH);
            } else {
                fmt2 = (DecimalFormat) NumberFormat.getInstance(new ULocale("en_US@currency=JPY"),
                        NumberFormat.CASHCURRENCYSTYLE);
            }

            fmt2.setCurrency(Currency.getInstance("PKR"));
            String PKR_changed = fmt2.format(123.567);
            String PKR_changed_expected = "PKR 124";
            assertEquals("Test Currency Context", PKR_changed_expected, PKR_changed);
        }
    }

    @Test
    public void TestCurrencyWithMinMaxFractionDigits() {
        DecimalFormat df = new DecimalFormat();
        df.applyPattern("¤#,##0.00");
        df.setCurrency(Currency.getInstance("USD"));
        assertEquals("Basic currency format fails", "$1.23", df.format(1.234));
        df.setMaximumFractionDigits(4);
        assertEquals("Currency with max fraction == 4", "$1.234", df.format(1.234));
        df.setMinimumFractionDigits(4);
        assertEquals("Currency with min fraction == 4", "$1.2340", df.format(1.234));
    }

    @Test
    public void TestParseRequiredDecimalPoint() {

        String[] testPattern = { "00.####", "00.0", "00" };

        String value2Parse = "99";
        String value2ParseWithDecimal = "99.9";
        double parseValue  =  99;
        double parseValueWithDecimal = 99.9;
        DecimalFormat parser = new DecimalFormat();
        double result;
        boolean hasDecimalPoint;
        for (int i = 0; i < testPattern.length; i++) {
            parser.applyPattern(testPattern[i]);
            hasDecimalPoint = testPattern[i].contains(".");

            parser.setDecimalPatternMatchRequired(false);
            try {
                result = parser.parse(value2Parse).doubleValue();
                assertEquals("wrong parsed value", parseValue, result);
            } catch (ParseException e) {
                TestFmwk.errln("Parsing " + value2Parse + " should have succeeded with " + testPattern[i] +
                            " and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired());
            }
            try {
                result = parser.parse(value2ParseWithDecimal).doubleValue();
                assertEquals("wrong parsed value", parseValueWithDecimal, result);
            } catch (ParseException e) {
                TestFmwk.errln("Parsing " + value2ParseWithDecimal + " should have succeeded with " + testPattern[i] +
                            " and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired());
            }

            parser.setDecimalPatternMatchRequired(true);
            try {
                result = parser.parse(value2Parse).doubleValue();
                if(hasDecimalPoint){
                    TestFmwk.errln("Parsing " + value2Parse + " should NOT have succeeded with " + testPattern[i] +
                            " and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired());
                }
            } catch (ParseException e) {
                    // OK, should fail
            }
            try {
                result = parser.parse(value2ParseWithDecimal).doubleValue();
                if(!hasDecimalPoint){
                    TestFmwk.errln("Parsing " + value2ParseWithDecimal + " should NOT have succeeded with " + testPattern[i] +
                            " and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired() +
                            " (got: " + result + ")");
                }
            } catch (ParseException e) {
                    // OK, should fail
            }
        }
    }

    @Test
    public void TestCurrFmtNegSameAsPositive() {
        DecimalFormatSymbols decfmtsym = DecimalFormatSymbols.getInstance(Locale.US);
        decfmtsym.setMinusSign('\u200B'); // ZERO WIDTH SPACE, in ICU4J cannot set to empty string
        DecimalFormat decfmt = new DecimalFormat("\u00A4#,##0.00;-\u00A4#,##0.00", decfmtsym);
        String currFmtResult = decfmt.format(-100.0);
        if (!currFmtResult.equals("\u200B$100.00")) {
            errln("decfmt.toPattern results wrong, expected \u200B$100.00, got " + currFmtResult);
        }
    }

    @Test
    public void TestNumberFormatTestDataToString() {
        new DataDrivenNumberFormatTestData().toString();
    }

   // Testing for Issue 11805.
    @Test
    public void TestFormatToCharacterIteratorIssue11805 () {
        final double number = -350.76;
        DecimalFormat dfUS = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.US);
        String strUS = dfUS.format(number);
        Set<AttributedCharacterIterator.Attribute> resultUS  = dfUS.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative US Results: " + strUS, 5, resultUS.size());

        // For each test, add assert that all the fields are present and in the right spot.
        // TODO: Add tests for identify and position of each field, as in IntlTestDecimalFormatAPIC.

        DecimalFormat dfDE = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.GERMANY);
        String strDE = dfDE.format(number);
        Set<AttributedCharacterIterator.Attribute> resultDE  = dfDE.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative DE Results: " + strDE, 5, resultDE.size());

        DecimalFormat dfIN = (DecimalFormat) DecimalFormat.getCurrencyInstance(new Locale("hi", "in"));
        String strIN = dfIN.format(number);
        Set<AttributedCharacterIterator.Attribute> resultIN  = dfIN.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative IN Results: " + strIN, 5, resultIN.size());

        DecimalFormat dfJP = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.JAPAN);
        String strJP = dfJP.format(number);
        Set<AttributedCharacterIterator.Attribute> resultJP  = dfJP.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative JA Results: " + strJP, 3, resultJP.size());

        DecimalFormat dfGB = (DecimalFormat) DecimalFormat.getCurrencyInstance(new Locale("en", "gb"));
        String strGB = dfGB.format(number);
        Set<AttributedCharacterIterator.Attribute> resultGB  = dfGB.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative GB Results: " + strGB , 5, resultGB.size());

        DecimalFormat dfPlural = (DecimalFormat) NumberFormat.getInstance(new Locale("en", "gb"),
            NumberFormat.PLURALCURRENCYSTYLE);
        strGB = dfPlural.format(number);
        resultGB = dfPlural.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative GB Results: " + strGB , 5, resultGB.size());

        strGB = dfPlural.format(1);
        resultGB = dfPlural.formatToCharacterIterator(1).getAllAttributeKeys();
        assertEquals("Negative GB Results: " + strGB , 4, resultGB.size());

        // Test output with unit value.
        DecimalFormat auPlural = (DecimalFormat) NumberFormat.getInstance(new Locale("en", "au"),
                NumberFormat.PLURALCURRENCYSTYLE);
        String strAU = auPlural.format(1L);
        Set<AttributedCharacterIterator.Attribute> resultAU  =
                auPlural.formatToCharacterIterator(1L).getAllAttributeKeys();
        assertEquals("Unit AU Result: " + strAU , 4, resultAU.size());

        // Verify Permille fields.
        DecimalFormatSymbols sym = new DecimalFormatSymbols(new Locale("en", "gb"));
        DecimalFormat dfPermille = new DecimalFormat("####0.##\u2030", sym);
        strGB = dfPermille.format(number);
        resultGB = dfPermille.formatToCharacterIterator(number).getAllAttributeKeys();
        assertEquals("Negative GB Permille Results: " + strGB , 3, resultGB.size());
    }

    // Testing for Issue 11808.
    @Test
    public void TestRoundUnnecessarytIssue11808 () {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance();
        StringBuffer result = new StringBuffer("");
        df.setRoundingMode(BigDecimal.ROUND_UNNECESSARY);
        df.applyPattern("00.0#E0");

        try {
            df.format(99999.0, result, new FieldPosition(0));
            fail("Missing ArithmeticException for double: " + result);
        } catch (ArithmeticException expected) {
            // The exception should be thrown, since rounding is needed.
        }

        try {
            result = df.format(99999, result, new FieldPosition(0));
            fail("Missing ArithmeticException for int: " + result);
       } catch (ArithmeticException expected) {
           // The exception should be thrown, since rounding is needed.
        }

        try {
            result = df.format(new BigInteger("999999"), result, new FieldPosition(0));
            fail("Missing ArithmeticException for BigInteger: " + result);
        } catch (ArithmeticException expected) {
            // The exception should be thrown, since rounding is needed.
        }

        try {
            result = df.format(new BigDecimal("99999"), result, new FieldPosition(0));
            fail("Missing ArithmeticException for BigDecimal: " + result);
        } catch (ArithmeticException expected) {
            // The exception should be thrown, since rounding is needed.
        }

        try {
            result = df.format(new BigDecimal("-99999"), result, new FieldPosition(0));
            fail("Missing ArithmeticException for BigDecimal: " + result);
        } catch (ArithmeticException expected) {
            // The exception should be thrown, since rounding is needed.
        }
    }

    // Testing for Issue 11735.
    @Test
    public void TestNPEIssue11735() {
        DecimalFormat fmt = new DecimalFormat("0", new DecimalFormatSymbols(new ULocale("en")));
        ParsePosition ppos = new ParsePosition(0);
        assertEquals("Currency symbol missing in parse. Expect null result.",
                fmt.parseCurrency("53.45", ppos), null);
    }

    private void CompareAttributedCharacterFormatOutput(AttributedCharacterIterator iterator,
        List<FieldContainer> expected, String formattedOutput) {

        List<FieldContainer> result = new ArrayList<>();
        while (iterator.getIndex() != iterator.getEndIndex()) {
            int start = iterator.getRunStart();
            int end = iterator.getRunLimit();
            Iterator it = iterator.getAttributes().keySet().iterator();
            AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it.next();
            // For positions with both INTEGER and GROUPING attributes, we want the GROUPING attribute.
            if (it.hasNext() && attribute.equals(NumberFormat.Field.INTEGER)) {
                attribute = (AttributedCharacterIterator.Attribute) it.next();
            }
            Object value = iterator.getAttribute(attribute);
            result.add(new FieldContainer(start, end, attribute, value));
            iterator.setIndex(end);
        }
        assertEquals("Comparing vector length for " + formattedOutput,
            expected.size(), result.size());

        if (!expected.containsAll(result)) {
          // Print information on the differences.
          for (int i = 0; i < expected.size(); i++) {
            System.out.println("     expected[" + i + "] =" +
                expected.get(i).start + " " +
                expected.get(i).end + " " +
                expected.get(i).attribute + " " +
                expected.get(i).value);
            System.out.println(" result[" + i + "] =" +
                result.get(i).start + " " +
                result.get(i).end + " " +
                result.get(i).attribute + " " +
                result.get(i).value);
          }
        }
        assertTrue("Comparing vector results for " + formattedOutput, expected.containsAll(result));
    }

    // Testing for Issue 11914, missing FieldPositions for some field types.
    @Test
    public void TestNPEIssue11914() {
        // First test: Double value with grouping separators.
        List<FieldContainer> v1 = new ArrayList<>(7);
        v1.add(new FieldContainer(0, 3, NumberFormat.Field.INTEGER));
        v1.add(new FieldContainer(3, 4, NumberFormat.Field.GROUPING_SEPARATOR));
        v1.add(new FieldContainer(4, 7, NumberFormat.Field.INTEGER));
        v1.add(new FieldContainer(7, 8, NumberFormat.Field.GROUPING_SEPARATOR));
        v1.add(new FieldContainer(8, 11, NumberFormat.Field.INTEGER));
        v1.add(new FieldContainer(11, 12, NumberFormat.Field.DECIMAL_SEPARATOR));
        v1.add(new FieldContainer(12, 15, NumberFormat.Field.FRACTION));

        Number number = new Double(123456789.9753);
        ULocale usLoc = new ULocale("en-US");
        DecimalFormatSymbols US = new DecimalFormatSymbols(usLoc);

        NumberFormat outFmt = NumberFormat.getNumberInstance(usLoc);
        String numFmtted = outFmt.format(number);
        AttributedCharacterIterator iterator =
                outFmt.formatToCharacterIterator(number);
        CompareAttributedCharacterFormatOutput(iterator, v1, numFmtted);

        // Second test: Double with scientific notation formatting.
        List<FieldContainer> v2 = new ArrayList<>(7);
        v2.add(new FieldContainer(0, 1, NumberFormat.Field.INTEGER));
        v2.add(new FieldContainer(1, 2, NumberFormat.Field.DECIMAL_SEPARATOR));
        v2.add(new FieldContainer(2, 5, NumberFormat.Field.FRACTION));
        v2.add(new FieldContainer(5, 6, NumberFormat.Field.EXPONENT_SYMBOL));
        v2.add(new FieldContainer(6, 7, NumberFormat.Field.EXPONENT_SIGN));
        v2.add(new FieldContainer(7, 8, NumberFormat.Field.EXPONENT));
        DecimalFormat fmt2 = new DecimalFormat("0.###E+0", US);

        numFmtted = fmt2.format(number);
        iterator = fmt2.formatToCharacterIterator(number);
        CompareAttributedCharacterFormatOutput(iterator, v2, numFmtted);

        // Third test. BigInteger with grouping separators.
        List<FieldContainer> v3 = new ArrayList<>(7);
        v3.add(new FieldContainer(0, 1, NumberFormat.Field.SIGN));
        v3.add(new FieldContainer(1, 2, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(2, 3, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(3, 6, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(6, 7, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(7, 10, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(10, 11, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(11, 14, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(14, 15, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(15, 18, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(18, 19, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(19, 22, NumberFormat.Field.INTEGER));
        v3.add(new FieldContainer(22, 23, NumberFormat.Field.GROUPING_SEPARATOR));
        v3.add(new FieldContainer(23, 26, NumberFormat.Field.INTEGER));
        BigInteger bigNumberInt = new BigInteger("-1234567890246813579");
        String fmtNumberBigInt = outFmt.format(bigNumberInt);

        iterator = outFmt.formatToCharacterIterator(bigNumberInt);
        CompareAttributedCharacterFormatOutput(iterator, v3, fmtNumberBigInt);

        // Fourth test: BigDecimal with exponential formatting.
        List<FieldContainer> v4 = new ArrayList<>(7);
        v4.add(new FieldContainer(0, 1, NumberFormat.Field.SIGN));
        v4.add(new FieldContainer(1, 2, NumberFormat.Field.INTEGER));
        v4.add(new FieldContainer(2, 3, NumberFormat.Field.DECIMAL_SEPARATOR));
        v4.add(new FieldContainer(3, 6, NumberFormat.Field.FRACTION));
        v4.add(new FieldContainer(6, 7, NumberFormat.Field.EXPONENT_SYMBOL));
        v4.add(new FieldContainer(7, 8, NumberFormat.Field.EXPONENT_SIGN));
        v4.add(new FieldContainer(8, 9, NumberFormat.Field.EXPONENT));

        java.math.BigDecimal numberBigD = new java.math.BigDecimal(-123456789);
        String fmtNumberBigDExp = fmt2.format(numberBigD);

        iterator = fmt2.formatToCharacterIterator(numberBigD);
        CompareAttributedCharacterFormatOutput(iterator, v4, fmtNumberBigDExp);

    }

    // Test that the decimal is shown even when there are no fractional digits
    @Test
    public void Test11621() throws Exception {
        String pat = "0.##E0";

        DecimalFormatSymbols icuSym = new DecimalFormatSymbols(Locale.US);
        DecimalFormat icuFmt = new DecimalFormat(pat, icuSym);
        icuFmt.setDecimalSeparatorAlwaysShown(true);
        String icu = ((NumberFormat)icuFmt).format(299792458);

        java.text.DecimalFormatSymbols jdkSym = new java.text.DecimalFormatSymbols(Locale.US);
        java.text.DecimalFormat jdkFmt = new java.text.DecimalFormat(pat,jdkSym);
        jdkFmt.setDecimalSeparatorAlwaysShown(true);
        String jdk = ((java.text.NumberFormat)jdkFmt).format(299792458);

        assertEquals("ICU and JDK placement of decimal in exponent", jdk, icu);
    }

    private void checkFormatWithField(String testInfo, Format format, Object object,
            String expected, Format.Field field, int begin, int end) {
        StringBuffer buffer = new StringBuffer();
        FieldPosition pos = new FieldPosition(field);
        format.format(object, buffer, pos);

        assertEquals("Test " + testInfo + ": incorrect formatted text", expected, buffer.toString());

        if (begin != pos.getBeginIndex() || end != pos.getEndIndex()) {
            assertEquals("Index mismatch", field + " " + begin + ".." + end,
                pos.getFieldAttribute() + " " + pos.getBeginIndex() + ".." + pos.getEndIndex());
        }
    }

    @Test
    public void TestMissingFieldPositionsCurrency() {
        DecimalFormat formatter = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.US);
        Number number = new Double(92314587.66);
        String result = "$92,314,587.66";

        checkFormatWithField("currency", formatter, number, result,
            NumberFormat.Field.CURRENCY, 0, 1);
        checkFormatWithField("integer", formatter, number, result,
            NumberFormat.Field.INTEGER, 1, 11);
        checkFormatWithField("grouping separator", formatter, number, result,
            NumberFormat.Field.GROUPING_SEPARATOR, 3, 4);
        checkFormatWithField("decimal separator", formatter, number, result,
            NumberFormat.Field.DECIMAL_SEPARATOR, 11, 12);
        checkFormatWithField("fraction", formatter, number, result,
            NumberFormat.Field.FRACTION, 12, 14);
    }

    @Test
    public void TestMissingFieldPositionsNegativeDouble() {
        // test for exponential fields with double
        DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        Number number = new Double(-12345678.90123);
        DecimalFormat formatter = new DecimalFormat("0.#####E+00", us_symbols);
        String numFmtted = formatter.format(number);

        checkFormatWithField("sign", formatter, number, numFmtted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", formatter, number, numFmtted,
            NumberFormat.Field.INTEGER, 1, 2);
        checkFormatWithField("decimal separator", formatter, number, numFmtted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3);
        checkFormatWithField("exponent symbol", formatter, number, numFmtted,
            NumberFormat.Field.EXPONENT_SYMBOL, 8, 9);
        checkFormatWithField("exponent sign", formatter, number, numFmtted,
            NumberFormat.Field.EXPONENT_SIGN, 9, 10);
        checkFormatWithField("exponent", formatter, number, numFmtted,
            NumberFormat.Field.EXPONENT, 10, 12);
    }

    @Test
    public void TestMissingFieldPositionsPerCent() {
        // Check PERCENT
        DecimalFormat percentFormat = (DecimalFormat) NumberFormat.getPercentInstance(ULocale.US);
        Number number = new Double(-0.986);
        String numberFormatted = percentFormat.format(number);
        checkFormatWithField("sign", percentFormat, number, numberFormatted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", percentFormat, number, numberFormatted,
            NumberFormat.Field.INTEGER, 1, 3);
        checkFormatWithField("percent", percentFormat, number, numberFormatted,
            NumberFormat.Field.PERCENT, 3, 4);
    }

    @Test
    public void TestMissingFieldPositionsPerCentPattern() {
        // Check PERCENT with more digits
        DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat fmtPercent = new DecimalFormat("0.#####%", us_symbols);
        Number number = new Double(-0.986);
        String numFmtted = fmtPercent.format(number);

        checkFormatWithField("sign", fmtPercent, number, numFmtted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", fmtPercent, number, numFmtted,
            NumberFormat.Field.INTEGER, 1, 3);
        checkFormatWithField("decimal separator", fmtPercent, number, numFmtted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 3, 4);
        checkFormatWithField("fraction", fmtPercent, number, numFmtted,
            NumberFormat.Field.FRACTION, 4, 5);
        checkFormatWithField("percent", fmtPercent, number, numFmtted,
            NumberFormat.Field.PERCENT, 5, 6);
    }

    @Test
    public void TestMissingFieldPositionsPerMille() {
        // Check PERMILLE
        DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat fmtPerMille = new DecimalFormat("0.######‰", us_symbols);
        Number numberPermille = new Double(-0.98654);
        String numFmtted = fmtPerMille.format(numberPermille);

        checkFormatWithField("sign", fmtPerMille, numberPermille, numFmtted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", fmtPerMille, numberPermille, numFmtted,
            NumberFormat.Field.INTEGER, 1, 4);
        checkFormatWithField("decimal separator", fmtPerMille, numberPermille, numFmtted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 4, 5);
        checkFormatWithField("fraction", fmtPerMille, numberPermille, numFmtted,
            NumberFormat.Field.FRACTION, 5, 7);
        checkFormatWithField("permille", fmtPerMille, numberPermille, numFmtted,
            NumberFormat.Field.PERMILLE, 7, 8);
    }

    @Test
    public void TestMissingFieldPositionsNegativeBigInt() {
      DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat formatter = new DecimalFormat("0.#####E+0", us_symbols);
        Number number = new BigDecimal("-123456789987654321");
        String bigDecFmtted = formatter.format(number);

        checkFormatWithField("sign", formatter, number, bigDecFmtted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", formatter, number, bigDecFmtted,
            NumberFormat.Field.INTEGER, 1, 2);
        checkFormatWithField("decimal separator", formatter, number, bigDecFmtted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3);
        checkFormatWithField("exponent symbol", formatter, number, bigDecFmtted,
            NumberFormat.Field.EXPONENT_SYMBOL, 8, 9);
        checkFormatWithField("exponent sign", formatter, number, bigDecFmtted,
            NumberFormat.Field.EXPONENT_SIGN, 9, 10);
        checkFormatWithField("exponent", formatter, number, bigDecFmtted,
            NumberFormat.Field.EXPONENT, 10, 12);
    }

    @Test
    public void TestMissingFieldPositionsNegativeLong() {
        Number number = new Long("-123456789987654321");
        DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat formatter = new DecimalFormat("0.#####E+0", us_symbols);
        String longFmtted = formatter.format(number);

        checkFormatWithField("sign", formatter, number, longFmtted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", formatter, number, longFmtted,
            NumberFormat.Field.INTEGER, 1, 2);
        checkFormatWithField("decimal separator", formatter, number, longFmtted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3);
        checkFormatWithField("exponent symbol", formatter, number, longFmtted,
            NumberFormat.Field.EXPONENT_SYMBOL, 8, 9);
        checkFormatWithField("exponent sign", formatter, number, longFmtted,
            NumberFormat.Field.EXPONENT_SIGN, 9, 10);
        checkFormatWithField("exponent", formatter, number, longFmtted,
            NumberFormat.Field.EXPONENT, 10, 12);
    }

    @Test
    public void TestMissingFieldPositionsPositiveBigDec() {
        // Check complex positive;negative pattern.
        DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat fmtPosNegSign = new DecimalFormat("+0.####E+00;-0.#######E+0", us_symbols);
        Number positiveExp = new Double("9876543210");
        String posExpFormatted = fmtPosNegSign.format(positiveExp);

        checkFormatWithField("sign", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.INTEGER, 1, 2);
        checkFormatWithField("decimal separator", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3);
        checkFormatWithField("fraction", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.FRACTION, 3, 7);
        checkFormatWithField("exponent symbol", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.EXPONENT_SYMBOL, 7, 8);
        checkFormatWithField("exponent sign", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.EXPONENT_SIGN, 8, 9);
        checkFormatWithField("exponent", fmtPosNegSign, positiveExp, posExpFormatted,
            NumberFormat.Field.EXPONENT, 9, 11);
    }

    @Test
    public void TestMissingFieldPositionsNegativeBigDec() {
        // Check complex positive;negative pattern.
      DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US);
        DecimalFormat fmtPosNegSign = new DecimalFormat("+0.####E+00;-0.#######E+0", us_symbols);
        Number negativeExp = new BigDecimal("-0.000000987654321083");
        String negExpFormatted = fmtPosNegSign.format(negativeExp);

        checkFormatWithField("sign", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.SIGN, 0, 1);
        checkFormatWithField("integer", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.INTEGER, 1, 2);
        checkFormatWithField("decimal separator", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3);
        checkFormatWithField("fraction", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.FRACTION, 3, 7);
        checkFormatWithField("exponent symbol", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.EXPONENT_SYMBOL, 7, 8);
        checkFormatWithField("exponent sign", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.EXPONENT_SIGN, 8, 9);
        checkFormatWithField("exponent", fmtPosNegSign, negativeExp, negExpFormatted,
            NumberFormat.Field.EXPONENT, 9, 11);
    }

    @Test
    public void TestStringSymbols() {
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(ULocale.US);

        // Attempt digits with multiple code points.
        String[] customDigits = {"(0)", "(1)", "(2)", "(3)", "(4)", "(5)", "(6)", "(7)", "(8)", "(9)"};
        symbols.setDigitStrings(customDigits);
        DecimalFormat fmt = new DecimalFormat("#,##0.0#", symbols);
        expect2(fmt, 1234567.89, "(1),(2)(3)(4),(5)(6)(7).(8)(9)");

        // Scientific notation should work.
        fmt.applyPattern("@@@E0");
        expect2(fmt, 1230000, "(1).(2)(3)E(6)");

        // Grouping and decimal with multiple code points (supported in parsing since ICU 61)
        symbols.setDecimalSeparatorString("~~");
        symbols.setGroupingSeparatorString("^^");
        fmt.setDecimalFormatSymbols(symbols);
        fmt.applyPattern("#,##0.0#");
        expect2(fmt, 1234567.89, "(1)^^(2)(3)(4)^^(5)(6)(7)~~(8)(9)");

        // Digits starting at U+1D7CE MATHEMATICAL BOLD DIGIT ZERO
        // These are all single code points, so parsing will work.
        for (int i=0; i<10; i++) customDigits[i] = new String(Character.toChars(0x1D7CE+i));
        symbols.setDigitStrings(customDigits);
        symbols.setDecimalSeparatorString("😁");
        symbols.setGroupingSeparatorString("😎");
        fmt.setDecimalFormatSymbols(symbols);
        expect2(fmt, 1234.56, "𝟏😎𝟐𝟑𝟒😁𝟓𝟔");
    }

    @Test
    public void TestArabicCurrencyPatternInfo() {
        ULocale arLocale = new ULocale("ar");

        DecimalFormatSymbols symbols = new DecimalFormatSymbols(arLocale);
        String currSpacingPatn = symbols.getPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_CURRENCY_MATCH, true);
        if (currSpacingPatn==null || currSpacingPatn.length() == 0) {
            errln("locale ar, getPatternForCurrencySpacing returns null or 0-length string");
        }

        DecimalFormat currAcctFormat = (DecimalFormat)NumberFormat.getInstance(arLocale, NumberFormat.ACCOUNTINGCURRENCYSTYLE);
        String currAcctPatn = currAcctFormat.toPattern();
        if (currAcctPatn==null || currAcctPatn.length() == 0) {
            errln("locale ar, toPattern for ACCOUNTINGCURRENCYSTYLE returns null or 0-length string");
        }
    }

    @Test
    public void TestMinMaxOverrides()
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException,
                NoSuchMethodException, SecurityException {
        Class<?>[] baseClasses = {NumberFormat.class, NumberFormat.class, DecimalFormat.class};
        String[] names = {"Integer", "Fraction", "Significant"};
        for (int i = 0; i < 3; i++) {
            DecimalFormat df = new DecimalFormat();
            Class<?> base = baseClasses[i];
            String name = names[i];
            Method getMinimum = base.getDeclaredMethod("getMinimum" + name + "Digits");
            Method setMinimum = base.getDeclaredMethod("setMinimum" + name + "Digits", Integer.TYPE);
            Method getMaximum = base.getDeclaredMethod("getMaximum" + name + "Digits");
            Method setMaximum = base.getDeclaredMethod("setMaximum" + name + "Digits", Integer.TYPE);

            // Check max overrides min
            setMinimum.invoke(df, 2);
            assertEquals(name + " getMin A", 2, getMinimum.invoke(df));
            setMaximum.invoke(df, 3);
            assertEquals(name + " getMin B", 2, getMinimum.invoke(df));
            assertEquals(name + " getMax B", 3, getMaximum.invoke(df));
            setMaximum.invoke(df, 2);
            assertEquals(name + " getMin C", 2, getMinimum.invoke(df));
            assertEquals(name + " getMax C", 2, getMaximum.invoke(df));
            setMaximum.invoke(df, 1);
            assertEquals(name + " getMin D", 1, getMinimum.invoke(df));
            assertEquals(name + " getMax D", 1, getMaximum.invoke(df));

            // Check min overrides max
            setMaximum.invoke(df, 2);
            assertEquals(name + " getMax E", 2, getMaximum.invoke(df));
            setMinimum.invoke(df, 1);
            assertEquals(name + " getMin F", 1, getMinimum.invoke(df));
            assertEquals(name + " getMax F", 2, getMaximum.invoke(df));
            setMinimum.invoke(df, 2);
            assertEquals(name + " getMin G", 2, getMinimum.invoke(df));
            assertEquals(name + " getMax G", 2, getMaximum.invoke(df));
            setMinimum.invoke(df, 3);
            assertEquals(name + " getMin H", 3, getMinimum.invoke(df));
            assertEquals(name + " getMax H", 3, getMaximum.invoke(df));
        }
    }

    @Test
    public void TestSetMathContext() throws ParseException {
        java.math.MathContext fourDigits = new java.math.MathContext(4);
        java.math.MathContext unlimitedCeiling = new java.math.MathContext(0, RoundingMode.CEILING);

        // Test rounding
        DecimalFormat df = new DecimalFormat();
        assertEquals("Default format", "9,876.543", df.format(9876.5432));
        df.setMathContext(fourDigits);
        assertEquals("Format with fourDigits", "9,877", df.format(9876.5432));
        df.setMathContext(unlimitedCeiling);
        assertEquals("Format with unlimitedCeiling", "9,876.544", df.format(9876.5432));

        // Test multiplication
        df = new DecimalFormat("0.000%");
        assertEquals("Default multiplication", "12.001%", df.format(0.120011));
        df.setMathContext(fourDigits);
        assertEquals("Multiplication with fourDigits", "12.000%", df.format(0.120011));
        df.setMathContext(unlimitedCeiling);
        assertEquals("Multiplication with unlimitedCeiling", "12.002%", df.format(0.120011));

        // Test simple division
        df = new DecimalFormat("0%");
        assertEquals("Default division", 0.12001, df.parse("12.001%").doubleValue());
        df.setMathContext(fourDigits);
        // NOTE: Since ICU 61, division no longer occurs with percentage parsing.
        // assertEquals("Division with fourDigits", 0.12, df.parse("12.001%").doubleValue());
        assertEquals("Division with fourDigits", 0.12001, df.parse("12.001%").doubleValue());
        df.setMathContext(unlimitedCeiling);
        assertEquals("Division with unlimitedCeiling", 0.12001, df.parse("12.001%").doubleValue());

        // Test extreme division
        df = new DecimalFormat();
        df.setMultiplier(1000000007); // prime number
        String hugeNumberString = "9876543212345678987654321234567898765432123456789"; // 49 digits
        BigInteger huge34Digits = new BigInteger("9876543143209876985185182338271622000000");
        BigInteger huge4Digits = new BigInteger("9877000000000000000000000000000000000000");
        BigInteger actual34Digits = ((BigDecimal) df.parse(hugeNumberString)).toBigIntegerExact();
        assertEquals("Default extreme division", huge34Digits, actual34Digits);
        df.setMathContext(fourDigits);
        BigInteger actual4Digits = ((BigDecimal) df.parse(hugeNumberString)).toBigIntegerExact();
        assertEquals("Extreme division with fourDigits", huge4Digits, actual4Digits);
    }

    /**
     * ArithmeticException is thrown when inverting multiplier produces a non-terminating
     * decimal result in conjunction with MathContext of unlimited precision.
     */
    @Test
    public void testSetMathContextArithmeticException() {
        DecimalFormat df = new DecimalFormat();
        df.setMultiplier(7);
        try {
            df.setMathContext(java.math.MathContext.UNLIMITED);
            fail("Extreme division with unlimited precision should throw ArithmeticException");
        } catch (ArithmeticException e) {
            // expected
        }
    }

    /**
     * ArithmeticException is thrown when inverting multiplier produces a non-terminating
     * decimal result in conjunction with MathContext of unlimited precision.
     */
    @Test
    public void testSetMathContextICUArithmeticException() {
        DecimalFormat df = new DecimalFormat();
        df.setMultiplier(7);
        try {
            df.setMathContextICU(new MathContext(0));
            fail("Extreme division with unlimited precision should throw ArithmeticException");
        } catch (ArithmeticException e) {
            // expected
        }
    }

    /**
     * ArithmeticException is thrown when inverting multiplier produces a non-terminating
     * decimal result in conjunction with MathContext of unlimited precision.
     */
    @Test
    public void testSetMultiplierArithmeticException() {
        DecimalFormat df = new DecimalFormat();
        df.setMathContext(java.math.MathContext.UNLIMITED);
        try {
            df.setMultiplier(7);
            fail("Extreme division with unlimited precision should throw ArithmeticException");
        } catch (ArithmeticException e) {
            // expected
        }
    }

    @Test
    public void Test10436() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
        df.setRoundingMode(MathContext.ROUND_CEILING);
        df.setMinimumFractionDigits(0);
        df.setMaximumFractionDigits(0);
        assertEquals("-.99 should round toward infinity", "-0", df.format(-0.99));
    }

    @Test
    public void Test10765() {
        NumberFormat fmt = NumberFormat.getInstance(new ULocale("en"));
        fmt.setMinimumIntegerDigits(10);
        FieldPosition pos = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
        StringBuffer sb = new StringBuffer();
        fmt.format(1234567, sb, pos);
        assertEquals("Should have multiple grouping separators", "0,001,234,567", sb.toString());
        assertEquals("FieldPosition should report the first occurence", 1, pos.getBeginIndex());
        assertEquals("FieldPosition should report the first occurence", 2, pos.getEndIndex());
    }

    @Test
    public void Test10997() {
        NumberFormat fmt = NumberFormat.getCurrencyInstance(new ULocale("en-US"));
        fmt.setMinimumFractionDigits(4);
        fmt.setMaximumFractionDigits(4);
        String str1 = fmt.format(new CurrencyAmount(123.45, Currency.getInstance("USD")));
        String str2 = fmt.format(new CurrencyAmount(123.45, Currency.getInstance("EUR")));
        assertEquals("minFrac 4 should be respected in default currency", "$123.4500", str1);
        assertEquals("minFrac 4 should be respected in different currency", "€123.4500", str2);
    }

    @Test
    public void Test11020() {
        DecimalFormatSymbols sym = new DecimalFormatSymbols(ULocale.FRANCE);
        DecimalFormat fmt = new DecimalFormat("0.05E0", sym);
        String result = fmt.format(12301.2).replace('\u00a0', ' ');
        assertEquals("Rounding increment should be applied after magnitude scaling", "1,25E4", result);
    }

    @Test
    public void Test11025() {
        String pattern = "¤¤ **####0.00";
        DecimalFormatSymbols sym = new DecimalFormatSymbols(ULocale.FRANCE);
        DecimalFormat fmt = new DecimalFormat(pattern, sym);
        String result = fmt.format(433.0);
        assertEquals("Number should be padded to 11 characters", "EUR *433,00", result);
    }

    @Test
    public void Test11640_TripleCurrencySymbol() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
        df.applyPattern("¤¤¤ 0");
        String result = df.getPositivePrefix();
        assertEquals("Triple-currency should give long name on getPositivePrefix", "US dollars ", result);
    }

    @Test
    public void Test11645() {
        String pattern = "#,##0.0#";
        DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance();
        fmt.applyPattern(pattern);
        DecimalFormat fmtCopy;

        final int newMultiplier = 37;
        fmtCopy = (DecimalFormat) fmt.clone();
        assertNotEquals("Value before setter", fmtCopy.getMultiplier(), newMultiplier);
        fmtCopy.setMultiplier(newMultiplier);
        assertEquals("Value after setter", fmtCopy.getMultiplier(), newMultiplier);
        fmtCopy.applyPattern(pattern);
        assertEquals("Value after applyPattern", fmtCopy.getMultiplier(), newMultiplier);
        assertFalse("multiplier", fmt.equals(fmtCopy));

        final int newRoundingMode = RoundingMode.CEILING.ordinal();
        fmtCopy = (DecimalFormat) fmt.clone();
        assertNotEquals("Value before setter", fmtCopy.getRoundingMode(), newRoundingMode);
        fmtCopy.setRoundingMode(newRoundingMode);
        assertEquals("Value after setter", fmtCopy.getRoundingMode(), newRoundingMode);
        fmtCopy.applyPattern(pattern);
        assertEquals("Value after applyPattern", fmtCopy.getRoundingMode(), newRoundingMode);
        assertFalse("roundingMode", fmt.equals(fmtCopy));

        final Currency newCurrency = Currency.getInstance("EAT");
        fmtCopy = (DecimalFormat) fmt.clone();
        assertNotEquals("Value before setter", fmtCopy.getCurrency(), newCurrency);
        fmtCopy.setCurrency(newCurrency);
        assertEquals("Value after setter", fmtCopy.getCurrency(), newCurrency);
        fmtCopy.applyPattern(pattern);
        assertEquals("Value after applyPattern", fmtCopy.getCurrency(), newCurrency);
        assertFalse("currency", fmt.equals(fmtCopy));

        final CurrencyUsage newCurrencyUsage = CurrencyUsage.CASH;
        fmtCopy = (DecimalFormat) fmt.clone();
        assertNotEquals("Value before setter", fmtCopy.getCurrencyUsage(), newCurrencyUsage);
        fmtCopy.setCurrencyUsage(CurrencyUsage.CASH);
        assertEquals("Value after setter", fmtCopy.getCurrencyUsage(), newCurrencyUsage);
        fmtCopy.applyPattern(pattern);
        assertEquals("Value after applyPattern", fmtCopy.getCurrencyUsage(), newCurrencyUsage);
        assertFalse("currencyUsage", fmt.equals(fmtCopy));
    }

    @Test
    public void Test11646() {
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(new ULocale("en_US"));
        String pattern = "\u00a4\u00a4\u00a4 0.00 %\u00a4\u00a4";
        DecimalFormat fmt = new DecimalFormat(pattern, symbols);

        // Test equality with affixes. set affix methods can't capture special
        // characters which is why equality should fail.
        {
          DecimalFormat fmtCopy = (DecimalFormat) fmt.clone();
          assertEquals("", fmt, fmtCopy);
          fmtCopy.setPositivePrefix(fmtCopy.getPositivePrefix());
          assertNotEquals("", fmt, fmtCopy);
        }
        {
          DecimalFormat fmtCopy = (DecimalFormat) fmt.clone();
          assertEquals("", fmt, fmtCopy);
          fmtCopy.setPositiveSuffix(fmtCopy.getPositiveSuffix());
          assertNotEquals("", fmt, fmtCopy);
        }
        {
          DecimalFormat fmtCopy = (DecimalFormat) fmt.clone();
          assertEquals("", fmt, fmtCopy);
          fmtCopy.setNegativePrefix(fmtCopy.getNegativePrefix());
          assertNotEquals("", fmt, fmtCopy);
        }
        {
          DecimalFormat fmtCopy = (DecimalFormat) fmt.clone();
          assertEquals("", fmt, fmtCopy);
          fmtCopy.setNegativeSuffix(fmtCopy.getNegativeSuffix());
          assertNotEquals("", fmt, fmtCopy);
        }
    }

    @Test
    public void Test11648() {
        DecimalFormat df = new DecimalFormat("0.00");
        df.setScientificNotation(true);
        String pat = df.toPattern();
        assertEquals("A valid scientific notation pattern should be produced", "0.00E0", pat);
    }

    @Test
    public void Test11649() {
        String pattern = "\u00a4\u00a4\u00a4 0.00";
        DecimalFormat fmt = new DecimalFormat(pattern);
        fmt.setCurrency(Currency.getInstance("USD"));
        assertEquals("Triple currency sign should format long name", "US dollars 12.34", fmt.format(12.34));

        String newPattern = fmt.toPattern();
        assertEquals("Should produce a valid pattern", pattern, newPattern);

        DecimalFormat fmt2 = new DecimalFormat(newPattern);
        fmt2.setCurrency(Currency.getInstance("USD"));
        assertEquals("Triple currency sign pattern should round-trip", "US dollars 12.34", fmt2.format(12.34));

        String quotedPattern = "\u00a4\u00a4'\u00a4' 0.00";
        DecimalFormat fmt3 = new DecimalFormat(quotedPattern);
        assertEquals("Should be treated as double currency sign", "USD\u00a4 12.34", fmt3.format(12.34));

        String outQuotedPattern = fmt3.toPattern();
        assertEquals("Double currency sign with quoted sign should round-trip", quotedPattern, outQuotedPattern);
    }

    @Test
    @Ignore
    public void Test11686() {
        // Only passes with slow mode.
        // TODO: Re-enable this test with slow mode.
        DecimalFormat df = new DecimalFormat();
        df.setPositiveSuffix("0K");
        df.setNegativeSuffix("0N");
        expect2(df, 123, "1230K");
        expect2(df, -123, "-1230N");
    }

    @Test
    public void Test11839() {
        DecimalFormatSymbols dfs = new DecimalFormatSymbols(ULocale.ENGLISH);
        dfs.setMinusSignString("a∸");
        dfs.setPlusSignString("b∔"); //  ∔  U+2214 DOT PLUS
        DecimalFormat df = new DecimalFormat("0.00+;0.00-", dfs);
        String result = df.format(-1.234);
        assertEquals("Locale-specific minus sign should be used", "1.23a∸", result);
        result = df.format(1.234);
        assertEquals("Locale-specific plus sign should be used", "1.23b∔", result);
        // Test round-trip with parse
        expect2(df, -456, "456.00a∸");
        expect2(df, 456, "456.00b∔");
    }

    @Test
    public void Test12753() {
        ULocale locale = new ULocale("en-US");
        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
        symbols.setDecimalSeparator('*');
        DecimalFormat df = new DecimalFormat("0.00", symbols);
        df.setDecimalPatternMatchRequired(true);
        try {
            df.parse("123");
            fail("Parsing integer succeeded even though setDecimalPatternMatchRequired was set");
        } catch (ParseException e) {
            // Parse failed (expected)
        }
    }

    @Test
    public void Test12962() {
        String pat = "**0.00";
        DecimalFormat df = new DecimalFormat(pat);
        String newPat = df.toPattern();
        assertEquals("Format width changed upon calling applyPattern", pat.length(), newPat.length());
    }

    @Test
    public void Test10354() {
        DecimalFormatSymbols dfs = new DecimalFormatSymbols();
        dfs.setNaN("");
        DecimalFormat df = new DecimalFormat();
        df.setDecimalFormatSymbols(dfs);
        try {
            df.formatToCharacterIterator(Double.NaN);
            // pass
        } catch (IllegalArgumentException e) {
            throw new AssertionError(e);
        }
    }

    @Test
    public void Test11913() {
        NumberFormat df = DecimalFormat.getInstance();
        String result = df.format(new BigDecimal("1.23456789E400"));
        assertEquals("Should format more than 309 digits", "12,345,678", result.substring(0, 10));
        assertEquals("Should format more than 309 digits", 534, result.length());
    }

    @Test
    public void Test12045() {
        if (logKnownIssue("12045", "XSU is missing from fr")) { return; }

        NumberFormat nf = NumberFormat.getInstance(new ULocale("fr"), NumberFormat.PLURALCURRENCYSTYLE);
        ParsePosition ppos = new ParsePosition(0);
        try {
            CurrencyAmount result = nf.parseCurrency("2,34 XSU", ppos);
            assertEquals("Parsing should succeed on XSU",
                         new CurrencyAmount(2.34, Currency.getInstance("XSU")), result);
            // pass
        } catch (Exception e) {
            //throw new AssertionError("Should have been able to parse XSU", e);
            throw new AssertionError("Should have been able to parse XSU: " + e.getMessage());
        }
    }

    @Test
    public void Test11739() {
        NumberFormat nf = NumberFormat.getCurrencyInstance(new ULocale("sr_BA"));
        ((DecimalFormat) nf).applyPattern("#,##0.0 ¤¤¤");
        ParsePosition ppos = new ParsePosition(0);
        CurrencyAmount result = nf.parseCurrency("1.500 амерички долар", ppos);
        assertEquals("Should parse to 1500 USD", new CurrencyAmount(1500, Currency.getInstance("USD")), result);
    }

    @Test
    public void Test11647() {
        DecimalFormat df = new DecimalFormat();
        df.applyPattern("¤¤¤¤#");
        String actual = df.format(123);
        assertEquals("Should replace 4 currency signs with U+FFFD", "\uFFFD123", actual);
    }

    @Test
    public void Test12567() {
        DecimalFormat df1 = (DecimalFormat) NumberFormat.getInstance(NumberFormat.PLURALCURRENCYSTYLE);
        DecimalFormat df2 = (DecimalFormat) NumberFormat.getInstance(NumberFormat.NUMBERSTYLE);
        df2.setCurrency(df1.getCurrency());
        df2.setCurrencyPluralInfo(df1.getCurrencyPluralInfo());
        df1.applyPattern("0.00");
        df2.applyPattern("0.00");
        assertEquals("df1 == df2", df1, df2);
        assertEquals("df2 == df1", df2, df1);
        df2.setPositivePrefix("abc");
        assertNotEquals("df1 != df2", df1, df2);
        assertNotEquals("df2 != df1", df2, df1);
    }

    @Test
    public void Test11897_LocalizedPatternSeparator() {
        // In a locale with a different <list> symbol, like arabic,
        // kPatternSeparatorSymbol should still be ';'
        {
            DecimalFormatSymbols dfs = new DecimalFormatSymbols(new ULocale("ar"));
            assertEquals("pattern separator symbol should be ;",
                    ';',
                    dfs.getPatternSeparator());
        }

        // However, the custom symbol should be used in localized notation
        // when set manually via API
        {
            DecimalFormatSymbols dfs = new DecimalFormatSymbols(new ULocale("en"));
            dfs.setPatternSeparator('!');
            DecimalFormat df = new DecimalFormat("0", dfs);
            df.applyPattern("a0;b0"); // should not throw
            assertEquals("should apply the normal pattern",
                    df.getNegativePrefix(),
                    "b");
            df.applyLocalizedPattern("c0!d0"); // should not throw
            assertEquals("should apply the localized pattern",
                    df.getNegativePrefix(),
                    "d");
        }
    }

    @Test
    public void Test13055() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
        df.setMaximumFractionDigits(0);
        df.setRoundingMode(BigDecimal.ROUND_HALF_EVEN);
        assertEquals("Should round percent toward even number", "216%", df.format(2.155));
    }

    @Test
    public void Test13056() {
        DecimalFormat df = new DecimalFormat("#,##0");
        assertEquals("Primary grouping should return 3", 3, df.getGroupingSize());
        assertEquals("Secondary grouping should return 0", 0, df.getSecondaryGroupingSize());
        df.setSecondaryGroupingSize(3);
        assertEquals("Primary grouping should still return 3", 3, df.getGroupingSize());
        assertEquals("Secondary grouping should round-trip", 3, df.getSecondaryGroupingSize());
        df.setGroupingSize(4);
        assertEquals("Primary grouping should return 4", 4, df.getGroupingSize());
        assertEquals("Secondary should remember explicit setting and return 3", 3, df.getSecondaryGroupingSize());
    }

    @Test
    public void Test13074() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(new ULocale("bg-BG"));
        String result = df.format(987654.321);
        assertEquals("Locale 'bg' should not use monetary grouping", "987654,32 лв.", result);
    }

    @Test
    public void Test13088and13162() {
        ULocale loc = new ULocale("fa");
        String pattern1 = "%\u00A0#,##0;%\u00A0-#,##0";
        double num = -12.34;
        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(loc);
        // If the symbols ever change in locale data, please call the setters so that this test
        // continues to use the old symbols.
        // The fa percent symbol does change in CLDR 32, so....
        symbols.setPercentString("‎٪");
        assertEquals("Checking for expected symbols", "‎−", symbols.getMinusSignString());
        assertEquals("Checking for expected symbols", "‎٪", symbols.getPercentString());
        DecimalFormat numfmt = new DecimalFormat(pattern1, symbols);
        expect2(numfmt, num, "‎٪ ‎−۱٬۲۳۴");
        String pattern2 = "%#,##0;%-#,##0";
        numfmt = new DecimalFormat(pattern2, symbols);
        expect2(numfmt, num, "‎٪‎−۱٬۲۳۴");
    }

    @Test
    public void Test13113_MalformedPatterns() {
        String[][] cases = {
                {"'", "quoted literal"},
                {"ab#c'd", "quoted literal"},
                {"ab#c*", "unquoted literal"},
                {"0#", "# cannot follow 0"},
                {".#0", "0 cannot follow #"},
                {"@0", "Cannot mix @ and 0"},
                {"0@", "Cannot mix 0 and @"},
                {"#x#", "unquoted special character"},
                {"@#@", "# inside of a run of @"},
        };
        for (String[] cas : cases) {
            try {
                new DecimalFormat(cas[0]);
                fail("Should have thrown on malformed pattern");
            } catch (IllegalArgumentException ex) {
                assertTrue("Exception should contain \"Malformed pattern\": " + ex.getMessage(),
                        ex.getMessage().contains("Malformed pattern"));
                assertTrue("Exception should contain \"" + cas[1] + "\"" + ex.getMessage(),
                        ex.getMessage().contains(cas[1]));
            }
        }
    }

    @Test
    public void Test13118() {
        DecimalFormat df = new DecimalFormat("@@@");
        df.setScientificNotation(true);
        for (double d=12345.67; d>1e-6; d/=10) {
            String result = df.format(d);
            assertEquals("Should produce a string of expected length on " + d,
                    d > 1 ? 6 : 7, result.length());
        }
    }

    @Test
    public void Test13289() {
        DecimalFormat df = new DecimalFormat("#00.0#E0");
        String result = df.format(0.00123);
        assertEquals("Should ignore scientific minInt if maxInt>minInt", "1.23E-3", result);
    }

    @Test
    public void Test13310() {
        // Note: if minInt > 8, then maxInt can be greater than 8.
        assertEquals("Should not throw an assertion error",
                "100000007.6E-1",
                new DecimalFormat("000000000.0#E0").format(10000000.76d));
    }

    @Test
    public void Test13391() throws ParseException {
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(new ULocale("ccp"));
        df.setParseStrict(true);
        String expected = "\uD804\uDD37\uD804\uDD38,\uD804\uDD39\uD804\uDD3A\uD804\uDD3B";
        assertEquals("Should produce expected output in ccp", expected, df.format(12345));
        Number result = df.parse(expected);
        assertEquals("Should parse to 12345 in ccp", 12345, result.longValue());

        df = (DecimalFormat) NumberFormat.getScientificInstance(new ULocale("ccp"));
        df.setParseStrict(true);
        String expectedScientific = "\uD804\uDD37.\uD804\uDD39E\uD804\uDD38";
        assertEquals("Should produce expected scientific output in ccp",
                expectedScientific, df.format(130));
        Number resultScientific = df.parse(expectedScientific);
        assertEquals("Should parse scientific to 130 in ccp",
                130, resultScientific.longValue());
    }

    @Test
    public void Test13453_AffixContent() {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getScientificInstance();
        assertEquals("Scientific should NOT be included", "", df.getPositiveSuffix());

        df = CompactDecimalFormat.getInstance(ULocale.ENGLISH, CompactDecimalFormat.CompactStyle.SHORT);
        assertEquals("Compact should NOT be included", "", df.getPositiveSuffix());

        df = (DecimalFormat) DecimalFormat.getInstance(NumberFormat.ISOCURRENCYSTYLE);
        df.setCurrency(Currency.getInstance("GBP"));
        assertEquals("ISO currency SHOULD be included", "GBP", df.getPositivePrefix());

        df = (DecimalFormat) DecimalFormat.getInstance(NumberFormat.PLURALCURRENCYSTYLE);
        df.setCurrency(Currency.getInstance("GBP"));
        assertEquals("Plural name SHOULD be included", " British pounds", df.getPositiveSuffix());
    }

    @Test
    public void Test11035_FormatCurrencyAmount() {
        double amount = 12345.67;
        String expected = "12,345$67 ​";
        Currency cur = Currency.getInstance("PTE");

        // Test three ways to set currency via API

        ULocale loc1 = new ULocale("pt_PT");
        NumberFormat fmt1 = NumberFormat.getCurrencyInstance(loc1);
        fmt1.setCurrency(cur);
        String actualSetCurrency = fmt1.format(amount);

        ULocale loc2 = new ULocale("pt_PT@currency=PTE");
        NumberFormat fmt2 = NumberFormat.getCurrencyInstance(loc2);
        String actualLocaleString = fmt2.format(amount);

        ULocale loc3 = new ULocale("pt_PT");
        NumberFormat fmt3 = NumberFormat.getCurrencyInstance(loc3);
        CurrencyAmount curAmt = new CurrencyAmount(amount, cur);
        String actualCurrencyAmount = fmt3.format(curAmt);

        assertEquals("Custom Currency Pattern, Set Currency", expected, actualSetCurrency);
        assertEquals("Custom Currency Pattern, Locale String", expected, actualCurrencyAmount);
        assertEquals("Custom Currency Pattern, CurrencyAmount", expected, actualLocaleString);
    }

    @Test
    public void testPercentZero() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
        String actual = df.format(0);
        assertEquals("Should have one zero digit", "0%", actual);
    }

    @Test
    public void testCurrencyZeroRounding() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance();
        df.setMaximumFractionDigits(0);
        String actual = df.format(0);
        assertEquals("Should have zero fraction digits", "$0", actual);
    }

    @Test
    public void testCustomCurrencySymbol() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance();
        df.setCurrency(Currency.getInstance("USD"));
        DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
        symbols.setCurrencySymbol("#");
        df.setDecimalFormatSymbols(symbols);
        String actual = df.format(123);
        assertEquals("Should use '#' instad of '$'", "# 123.00", actual);
    }

    @Test
    public void TestBasicSerializationRoundTrip() throws IOException, ClassNotFoundException {
        DecimalFormat df0 = new DecimalFormat("A-**#####,#00.00b¤");

        // Write to byte stream
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(df0);
        oos.flush();
        baos.close();
        byte[] bytes = baos.toByteArray();

        // Read from byte stream
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Object obj = ois.readObject();
        ois.close();
        DecimalFormat df1 = (DecimalFormat) obj;

        // Test equality
        assertEquals("Did not round-trip through serialization", df0, df1);

        // Test basic functionality
        String str0 = df0.format(12345.67);
        String str1 = df1.format(12345.67);
        assertEquals("Serialized formatter does not produce same output", str0, str1);
    }

    @Test
    public void testGetSetCurrency() {
        DecimalFormat df = new DecimalFormat("¤#", DecimalFormatSymbols.getInstance(ULocale.US));
        assertEquals("Currency should start out as the locale default", Currency.getInstance("USD"), df.getCurrency());
        Currency curr = Currency.getInstance("EUR");
        df.setCurrency(curr);
        assertEquals("Currency should equal EUR after set", curr, df.getCurrency());
        String result = df.format(123);
        assertEquals("Currency should format as expected in EUR", "€123.00", result);
    }

    @Test
    public void testRoundingModeSetters() {
        DecimalFormat df1 = new DecimalFormat();
        DecimalFormat df2 = new DecimalFormat();

        df1.setRoundingMode(java.math.BigDecimal.ROUND_CEILING);
        assertNotEquals("Rounding mode was set to a non-default", df1, df2);
        df2.setRoundingMode(com.ibm.icu.math.BigDecimal.ROUND_CEILING);
        assertEquals("Rounding mode from icu.math and java.math should be the same", df1, df2);
        df2.setRoundingMode(java.math.RoundingMode.CEILING.ordinal());
        assertEquals("Rounding mode ordinal from java.math.RoundingMode should be the same", df1, df2);
    }

    @Test
    public void testCurrencySignificantDigits() {
        ULocale locale = new ULocale("en-US");
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
        df.setMaximumSignificantDigits(2);
        String result = df.format(1234);
        assertEquals("Currency rounding should obey significant digits", "$1,200", result);
    }

    @Test
    public void testParseStrictScientific() {
        // See ticket #13057
        DecimalFormat df = (DecimalFormat) NumberFormat.getScientificInstance();
        df.setParseStrict(true);
        ParsePosition ppos = new ParsePosition(0);
        Number result0 = df.parse("123E4", ppos);
        assertEquals("Should accept number with exponent", 1230000L, result0);
        assertEquals("Should consume the whole number", 5, ppos.getIndex());
        ppos.setIndex(0);
        result0 = df.parse("123", ppos);
        // #13737: For backwards compatibility, do NOT require the exponent.
        assertEquals("Should NOT reject number without exponent", 123L, result0);
        ppos.setIndex(0);
        CurrencyAmount result1 = df.parseCurrency("USD123", ppos);
        assertEquals("Should NOT reject currency without exponent",
                new CurrencyAmount(123L, Currency.getInstance("USD")),
                result1);
    }

    @Test
    public void testParseLenientScientific() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getScientificInstance();
        ParsePosition ppos = new ParsePosition(0);
        Number result0 = df.parse("123E", ppos);
        assertEquals("Should parse the number in lenient mode", 123L, result0);
        assertEquals("Should stop before the E", 3, ppos.getIndex());
        DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
        dfs.setExponentSeparator("EE");
        df.setDecimalFormatSymbols(dfs);
        ppos.setIndex(0);
        result0 = df.parse("123EE", ppos);
        assertEquals("Should parse the number in lenient mode", 123L, result0);
        assertEquals("Should stop before the EE", 3, ppos.getIndex());
    }

    @Test
    public void testParseAcceptAsciiPercentPermilleFallback() {
        ULocale loc = new ULocale("ar");
        DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance(loc);
        ParsePosition ppos = new ParsePosition(0);
        Number result = df.parse("42%", ppos);
        assertEquals("Should parse as 0.42 even in ar", new BigDecimal("0.42"), result);
        assertEquals("Should consume the entire string even in ar", 3, ppos.getIndex());
        // TODO: Is there a better way to make a localized permille formatter?
        df.applyPattern(df.toPattern().replace("%", "‰"));
        ppos.setIndex(0);
        result = df.parse("42‰", ppos);
        assertEquals("Should parse as 0.042 even in ar", new BigDecimal("0.042"), result);
        assertEquals("Should consume the entire string even in ar", 3, ppos.getIndex());
    }

    @Test
    public void testParseSubtraction() {
        // TODO: Is this a case we need to support? It prevents us from automatically parsing
        // minus signs that appear after the number, like  in "12-" vs "-12".
        DecimalFormat df = new DecimalFormat();
        String str = "12 - 5";
        ParsePosition ppos = new ParsePosition(0);
        Number n1 = df.parse(str, ppos);
        Number n2 = df.parse(str, ppos);
        assertEquals("Should parse 12 and -5", 12, n1.intValue());
        assertEquals("Should parse 12 and -5", -5, n2.intValue());
    }

    @Test
    public void testSetPrefixDefaultSuffix() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
        df.setPositivePrefix("+");
        assertEquals("Should have manual plus sign and auto percent sign", "+100%", df.format(1));
    }

    @Test
    public void testMultiCodePointPaddingInPattern() {
        DecimalFormat df = new DecimalFormat("a*'நி'###0b");
        String result = df.format(12);
        assertEquals("Multi-codepoint padding should not be split", "aநிநி12b", result);
        df = new DecimalFormat("a*😁###0b");
        result = df.format(12);
        assertEquals("Single-codepoint padding should not be split", "a😁😁12b", result);
        df = new DecimalFormat("a*''###0b");
        result = df.format(12);
        assertEquals("Quote should be escapable in padding syntax", "a''12b", result);
    }

    @Test
    public void Test13737_ParseScientificStrict() {
        NumberFormat df = NumberFormat.getScientificInstance(ULocale.ENGLISH);
        df.setParseStrict(true);
        // Parse Test: exponent is not required, even in strict mode
        expect(df, "1.2", 1.2);
    }

    // TODO: Investigate this test and re-enable if appropriate.
    @Test
    @Ignore
    public void testParseAmbiguousAffixes() {
        BigDecimal positive = new BigDecimal("0.0567");
        BigDecimal negative = new BigDecimal("-0.0567");
        DecimalFormat df = new DecimalFormat();
        df.setParseBigDecimal(true);

        String[] patterns = { "+0.00%;-0.00%", "+0.00%;0.00%", "0.00%;-0.00%" };
        String[] inputs = { "+5.67%", "-5.67%", "5.67%" };
        boolean[][] expectedPositive = {
                { true, false, true },
                { true, false, false },
                { true, false, true }
        };

        for (int i=0; i<patterns.length; i++) {
            String pattern = patterns[i];
            df.applyPattern(pattern);
            for (int j=0; j<inputs.length; j++) {
                String input = inputs[j];
                ParsePosition ppos = new ParsePosition(0);
                Number actual = df.parse(input, ppos);
                BigDecimal expected = expectedPositive[i][j] ? positive : negative;
                String message = "Pattern " + pattern + " with input " + input;
                assertEquals(message, expected, actual);
                assertEquals(message, input.length(), ppos.getIndex());
            }
        }
    }

    @Test
    public void testParseIgnorables() {
        // Also see the test case "test parse ignorables" in numberformattestspecification.txt
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance();
        dfs.setPercentString("\u200E%\u200E");
        DecimalFormat df = new DecimalFormat("0 %;-0a", dfs);
        ParsePosition ppos = new ParsePosition(0);
        Number result = df.parse("42\u200E%\u200E ", ppos);
        assertEquals("Should parse as percentage", new BigDecimal("0.42"), result);
        assertEquals("Should consume the trailing bidi since it is in the symbol", 5, ppos.getIndex());
        ppos.setIndex(0);
        result = df.parse("-42a\u200E ", ppos);
        assertEquals("Should parse as percent", -0.42, result.doubleValue());
        assertEquals("Should not consume the trailing bidi or whitespace", 4, ppos.getIndex());

        // A few more cases based on the docstring:
        expect(df, "42%", 0.42);
        expect(df, "42 %", 0.42);
        expect(df, "42   %", 0.42);
        expect(df, "42\u00A0%", 0.42);
    }

    @Test
    public void testCustomCurrencyUsageOverridesPattern() {
        DecimalFormat df = new DecimalFormat("#,##0.###");
        expect2(df, 1234, "1,234");
        df.setCurrencyUsage(CurrencyUsage.STANDARD);
        expect2(df, 1234, "1,234.00");
        df.setCurrencyUsage(null);
        expect2(df, 1234, "1,234");
    }

    @Test
    public void testCurrencyUsageFractionOverrides() {
        NumberFormat df = DecimalFormat.getCurrencyInstance(ULocale.US);
        expect2(df, 35.0, "$35.00");
        df.setMinimumFractionDigits(3);
        expect2(df, 35.0, "$35.000");
        df.setMaximumFractionDigits(3);
        expect2(df, 35.0, "$35.000");
        df.setMinimumFractionDigits(-1);
        expect2(df, 35.0, "$35.00");
        df.setMaximumFractionDigits(-1);
        expect2(df, 35.0, "$35.00");
    }

    @Test
    public void testParseVeryVeryLargeExponent() {
        DecimalFormat df = new DecimalFormat();
        ParsePosition ppos = new ParsePosition(0);

        Object[][] cases = {
                {"1.2E+1234567890", Double.POSITIVE_INFINITY},
                {"1.2E+999999999", new com.ibm.icu.math.BigDecimal("1.2E+999999999")},
                {"1.2E+1000000000", Double.POSITIVE_INFINITY},
                {"-1.2E+999999999", new com.ibm.icu.math.BigDecimal("-1.2E+999999999")},
                {"-1.2E+1000000000", Double.NEGATIVE_INFINITY},
                {"1.2E-999999999", new com.ibm.icu.math.BigDecimal("1.2E-999999999")},
                {"1.2E-1000000000", 0.0},
                {"-1.2E-999999999", new com.ibm.icu.math.BigDecimal("-1.2E-999999999")},
                {"-1.2E-1000000000", -0.0},

        };

        for (Object[] cas : cases) {
            ppos.setIndex(0);
            String input = (String) cas[0];
            Number expected = (Number) cas[1];
            Number actual = df.parse(input, ppos);
            assertEquals(input, expected, actual);
        }
    }

    @Test
    public void testStringMethodsNPE() {
        String[] npeMethods = {
                "applyLocalizedPattern",
                "applyPattern",
                "setNegativePrefix",
                "setNegativeSuffix",
                "setPositivePrefix",
                "setPositiveSuffix"
        };
        for (String npeMethod : npeMethods) {
            DecimalFormat df = new DecimalFormat();
            try {
                DecimalFormat.class.getDeclaredMethod(npeMethod, String.class).invoke(df, (String) null);
                fail("NullPointerException not thrown in method " + npeMethod);
            } catch (InvocationTargetException e) {
                assertTrue("Exception should be NullPointerException in method " + npeMethod,
                        e.getCause() instanceof NullPointerException);
            } catch (Exception e) {
                // Other reflection exceptions
                throw new AssertionError("Reflection error in method " + npeMethod + ": " + e.getMessage());
            }
        }

        // Also test the constructors
        try {
            new DecimalFormat(null);
            fail("NullPointerException not thrown in 1-parameter constructor");
        } catch (NullPointerException e) {
            // Expected
        }
        try {
            new DecimalFormat(null, new DecimalFormatSymbols());
            fail("NullPointerException not thrown in 2-parameter constructor");
        } catch (NullPointerException e) {
            // Expected
        }
        try {
            new DecimalFormat(null, new DecimalFormatSymbols(), CurrencyPluralInfo.getInstance(), 0);
            fail("NullPointerException not thrown in 4-parameter constructor");
        } catch (NullPointerException e) {
            // Expected
        }
    }

    @Test
    public void testParseNoExponent() throws ParseException {
        DecimalFormat df = new DecimalFormat();
        assertEquals("Parse no exponent has wrong default", false, df.isParseNoExponent());
        Number result1 = df.parse("123E4");
        df.setParseNoExponent(true);
        assertEquals("Parse no exponent getter is broken", true, df.isParseNoExponent());
        Number result2 = df.parse("123E4");
        assertEquals("Exponent did not parse before setParseNoExponent", result1, new Long(1230000));
        assertEquals("Exponent parsed after setParseNoExponent", result2, new Long(123));
    }

    @Test
    public void testMinimumGroupingDigits() {
        String[][] allExpected = {
                {"123", "123"},
                {"1,230", "1230"},
                {"12,300", "12,300"},
                {"1,23,000", "1,23,000"}
        };

        DecimalFormat df = new DecimalFormat("#,##,##0");
        assertEquals("Minimum grouping digits has wrong default", 1, df.getMinimumGroupingDigits());

        for (int l = 123, i=0; l <= 123000; l *= 10, i++) {
            df.setMinimumGroupingDigits(1);
            assertEquals("Minimum grouping digits getter is broken", 1, df.getMinimumGroupingDigits());
            String actual = df.format(l);
            assertEquals("Output is wrong for 1, "+i, allExpected[i][0], actual);
            df.setMinimumGroupingDigits(2);
            assertEquals("Minimum grouping digits getter is broken", 2, df.getMinimumGroupingDigits());
            actual = df.format(l);
            assertEquals("Output is wrong for 2, "+i, allExpected[i][1], actual);
        }
    }

    @Test
    public void testParseCaseSensitive() {
        String[] patterns = {"a#b", "A#B"};
        String[] inputs = {"a500b", "A500b", "a500B", "a500e10b", "a500E10b"};
        int[][] expectedParsePositions = {
                {5, 5, 5, 8, 8}, // case insensitive, pattern 0
                {5, 0, 4, 4, 8}, // case sensitive, pattern 0
                {5, 5, 5, 8, 8}, // case insensitive, pattern 1
                {0, 4, 0, 0, 0}, // case sensitive, pattern 1
        };

        for (int p = 0; p < patterns.length; p++) {
            String pat = patterns[p];
            DecimalFormat df = new DecimalFormat(pat);
            assertEquals("parseCaseSensitive default is wrong", false, df.isParseCaseSensitive());
            for (int i = 0; i < inputs.length; i++) {
                String inp = inputs[i];
                df.setParseCaseSensitive(false);
                assertEquals("parseCaseSensitive getter is broken", false, df.isParseCaseSensitive());
                ParsePosition actualInsensitive = new ParsePosition(0);
                df.parse(inp, actualInsensitive);
                assertEquals("Insensitive, pattern "+p+", input "+i,
                        expectedParsePositions[p*2][i], actualInsensitive.getIndex());
                df.setParseCaseSensitive(true);
                assertEquals("parseCaseSensitive getter is broken", true, df.isParseCaseSensitive());
                ParsePosition actualSensitive = new ParsePosition(0);
                df.parse(inp, actualSensitive);
                assertEquals("Sensitive, pattern "+p+", input "+i,
                        expectedParsePositions[p*2+1][i], actualSensitive.getIndex());
            }
        }
    }

    @Test
    public void testPlusSignAlwaysShown() throws ParseException {
        double[] numbers = {0.012, 5.78, 0, -0.012, -5.78};
        ULocale[] locs = {new ULocale("en-US"), new ULocale("ar-EG"), new ULocale("es-CL")};
        String[][][] expecteds = {
                // en-US
                {
                    // decimal
                    { "+0.012", "+5.78", "+0", "-0.012", "-5.78" },
                    // currency
                    { "+$0.01", "+$5.78", "+$0.00", "-$0.01", "-$5.78" }
                },
                // ar-EG (interesting because the plus sign string starts with \u061C)
                {
                    // decimal
                    {
                        "\u061C+\u0660\u066B\u0660\u0661\u0662", // "؜+٠٫٠١٢"
                        "\u061C+\u0665\u066B\u0667\u0668", // "؜+٥٫٧٨"
                        "\u061C+\u0660", // "؜+٠"
                        "\u061C-\u0660\u066B\u0660\u0661\u0662", // "؜-٠٫٠١٢"
                        "\u061C-\u0665\u066B\u0667\u0668", // "؜-٥٫٧٨"
                    },
                    // currency (\062C.\0645.\200F is the currency sign in ar for EGP)
                    {
                        "\u061C+\u0660\u066B\u0660\u0661\u00A0\u062C.\u0645.\u200F",
                        "\u061C+\u0665\u066B\u0667\u0668\u00A0\u062C.\u0645.\u200F",
                        "\u061C+\u0660\u066B\u0660\u0660\u00A0\u062C.\u0645.\u200F",
                        "\u061C-\u0660\u066B\u0660\u0661\u00A0\u062C.\u0645.\u200F",
                        "\u061C-\u0665\u066B\u0667\u0668\u00A0\u062C.\u0645.\u200F"
                    }
                },
                // es-CL (interesting because of position of sign in currency)
                {
                    // decimal
                    { "+0,012", "+5,78", "+0", "-0,012", "-5,78" },
                    // currency (note: rounding for es-CL's currency, CLP, is 0 fraction digits)
                    { "$+0", "$+6", "$+0", "$-0", "$-6" }
                }
        };

        for (int i=0; i<locs.length; i++) {
            ULocale loc = locs[i];
            DecimalFormat df1 = (DecimalFormat) NumberFormat.getNumberInstance(loc);
            assertFalse("Default should be false", df1.isSignAlwaysShown());
            df1.setSignAlwaysShown(true);
            assertTrue("Getter should now return true", df1.isSignAlwaysShown());
            DecimalFormat df2 = (DecimalFormat) NumberFormat.getCurrencyInstance(loc);
            assertFalse("Default should be false", df2.isSignAlwaysShown());
            df2.setSignAlwaysShown(true);
            assertTrue("Getter should now return true", df2.isSignAlwaysShown());
            for (int j=0; j<2; j++) {
                DecimalFormat df = (j == 0) ? df1 : df2;
                for (int k=0; k<numbers.length; k++) {
                    double d = numbers[k];
                    String exp = expecteds[i][j][k];
                    String act = df.format(d);
                    assertEquals("Locale " + loc + ", type " + j + ", " + d, exp, act);
                    BigDecimal parsedExp = BigDecimal.valueOf(d);
                    if (j == 1) {
                        // Currency-round expected parse output
                        int scale = (i == 2) ? 0 : 2;
                        parsedExp = parsedExp.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
                    }
                    Number parsedNum = df.parse(exp);
                    BigDecimal parsedAct = (parsedNum.getClass() == BigDecimal.class)
                            ? (BigDecimal) parsedNum
                            : BigDecimal.valueOf(parsedNum.doubleValue());
                    assertEquals(
                            "Locale " + loc + ", type " + j + ", " + d + ", " + parsedExp + " => " + parsedAct,
                            0, parsedExp.compareTo(parsedAct));
                }
            }
        }
    }

    @Test
    public void Test20073_StrictPercentParseErrorIndex() {
        ParsePosition parsePosition = new ParsePosition(0);
        DecimalFormat df = new DecimalFormat("0%", DecimalFormatSymbols.getInstance(Locale.US));
        df.setParseStrict(true);
        Number number = df.parse("%2%", parsePosition);
        assertNull("", number);
        assertEquals("", 0, parsePosition.getIndex());
        assertEquals("", 0, parsePosition.getErrorIndex());
    }

    @Test
    public void Test11626_CustomizeCurrencyPluralInfo() throws ParseException {
        // Use locale sr because it has interesting plural rules.
        ULocale locale = ULocale.forLanguageTag("sr");
        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
        CurrencyPluralInfo info = CurrencyPluralInfo.getInstance(locale);
        info.setCurrencyPluralPattern("one", "0 qwerty");
        info.setCurrencyPluralPattern("few", "0 dvorak");
        DecimalFormat df = new DecimalFormat("#", symbols, info, NumberFormat.CURRENCYSTYLE);
        df.setCurrency(Currency.getInstance("USD"));
        df.setMaximumFractionDigits(0);

        assertEquals("Plural one", "1 qwerty", df.format(1));
        assertEquals("Plural few", "3 dvorak", df.format(3));
        assertEquals("Plural other", "99 америчких долара", df.format(99));

        info.setPluralRules("few: n is 1; one: n in 2..4");
        df.setCurrencyPluralInfo(info);
        assertEquals("Plural one", "1 dvorak", df.format(1));
        assertEquals("Plural few", "3 qwerty", df.format(3));
        assertEquals("Plural other", "99 америчких долара", df.format(99));
    }

    @Test
    public void TestNarrowCurrencySymbols() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.CANADA);
        df.setCurrency(Currency.getInstance("USD"));
        expect2(df, 123.45, "US$123.45");
        String pattern = df.toPattern();
        pattern = pattern.replace("¤", "¤¤¤¤¤");
        df.applyPattern(pattern);
        // Note: Narrow currency is not parseable because of ambiguity.
        assertEquals("Narrow currency symbol for USD in en_CA is $",
                "$123.45", df.format(123.45));
    }

    @Test
    public void TestAffixOverrideBehavior() {
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
        expect2(df, 100, "100");
        expect2(df, -100, "-100");
        // This is not the right way to set an override plus sign, but we need to support it for compatibility.
        df.setPositivePrefix("+");
        expect2(df, 100, "+100");
        expect2(df, -100, "-100"); // note: the positive prefix does not affect the negative prefix
        df.applyPattern("a0");
        expect2(df, 100, "a100");
        expect2(df, -100, "-a100");
    }

    @Test
    public void TestCurrencyRoundingMinWithoutMax() {
        NumberFormat currencyFormat = DecimalFormat.getCurrencyInstance(Locale.US);
        currencyFormat.setCurrency(Currency.getInstance("AUD"));
        currencyFormat.setMinimumFractionDigits(0);
        expect(currencyFormat, 0.001, "A$0");

        // NOTE: The size of the increment takes precedent over minFrac since ICU 59.
        // CAD-Cash uses nickel rounding.
        currencyFormat = DecimalFormat.getCurrencyInstance(Locale.US);
        currencyFormat.setCurrency(Currency.getInstance("CAD"));
        ((DecimalFormat)currencyFormat).setCurrencyUsage(CurrencyUsage.CASH);
        currencyFormat.setMinimumFractionDigits(0);
        // expect(currencyFormat, 0.08, "CA$0.1");  // ICU 58 and down
        expect(currencyFormat, 0.08, "CA$0.10");  // ICU 59 and up
    }

    @Test
    public void testParsePositionIncrease() {
        String input = "123\n456\n$789";
        ParsePosition ppos = new ParsePosition(0);
        DecimalFormat df = new DecimalFormat();
        df.parse(input, ppos);
        assertEquals("Should stop after first entry", 3, ppos.getIndex());
        ppos.setIndex(ppos.getIndex() + 1);
        df.parse(input, ppos);
        assertEquals("Should stop after second entry", 7, ppos.getIndex());
        ppos.setIndex(ppos.getIndex() + 1);
        df.parseCurrency(input, ppos); // test parseCurrency API as well
        assertEquals("Should stop after third entry", 12, ppos.getIndex());
    }

    @Test
    public void testTrailingMinusSign() {
        String input = "52-";
        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(ULocale.ENGLISH);
        ParsePosition ppos = new ParsePosition(0);
        Number result = df.parse(input, ppos);
        assertEquals("Trailing sign should NOT be accepted after the number in English by default",
                52.0,
                result.doubleValue(),
                0.0);
        df.applyPattern("#;#-");
        ppos.setIndex(0);
        result = df.parse(input, ppos);
        assertEquals("Trailing sign SHOULD be accepted if there is one in the pattern",
                -52.0,
                result.doubleValue(),
                0.0);
    }

    @Test
    public void testScientificCustomSign() {
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
        dfs.setMinusSignString("nnn");
        dfs.setPlusSignString("ppp");
        DecimalFormat df = new DecimalFormat("0E0", dfs);
        df.setExponentSignAlwaysShown(true);
        expect2(df, 0.5, "5Ennn1");
        expect2(df, 50, "5Eppp1");
    }

    @Test
    public void testParsePercentInPattern() {
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
        DecimalFormat df = new DecimalFormat("0x%", dfs);
        df.setParseStrict(true);
        expect2(df, 0.5, "50x%");
    }

    @Test
    public void testParseIsoStrict() {
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
        DecimalFormat df = new DecimalFormat("¤¤0;-0¤¤", dfs);
        df.setCurrency(Currency.getInstance("USD"));
        df.setParseStrict(true);
        expect2(df, 45, "USD 45.00");
        expect2(df, -45, "-45.00 USD");
    }

    @Test
    public void test13684_FrenchPercentParsing() {
        NumberFormat numberFormat = NumberFormat.getPercentInstance(ULocale.FRENCH);
        numberFormat.setParseStrict(true);
        ParsePosition ppos = new ParsePosition(0);
        Number percentage = numberFormat.parse("8\u00A0%", ppos);
        assertEquals("Should parse successfully", 0.08, percentage.doubleValue());
        assertEquals("Should consume whole string", 3, ppos.getIndex());
    }

    @Test
    public void testStrictParseCurrencyLongNames() {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(ULocale.ENGLISH, DecimalFormat.PLURALCURRENCYSTYLE);
        df.setParseStrict(true);
        df.setCurrency(Currency.getInstance("USD"));
        double input = 514.23;
        String formatted = df.format(input);
        String expected = "514.23 US dollars";
        assertEquals("Should format as expected", expected, formatted);
        ParsePosition ppos = new ParsePosition(0);
        CurrencyAmount ca = df.parseCurrency(formatted, ppos);
        assertEquals("Should consume whole number", ppos.getIndex(), 17);
        assertEquals("Number should round-trip", ca.getNumber().doubleValue(), input);
        assertEquals("Should get correct currency", ca.getCurrency().getCurrencyCode(), "USD");
        // Should also round-trip in non-currency parsing
        expect2(df, input, expected);
    }

    @Test
    public void testStrictParseCurrencySpacing() {
        DecimalFormat df = new DecimalFormat("¤ 0", DecimalFormatSymbols.getInstance(ULocale.ROOT));
        df.setCurrency(Currency.getInstance("USD"));
        df.setParseStrict(true);
        expect2(df, -51.42, "-US$ 51.42");
    }

    @Test
    public void testCaseSensitiveCustomIsoCurrency() {
        DecimalFormat df = new DecimalFormat("¤¤0", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
        df.setCurrency(Currency.getInstance("ICU"));
        ParsePosition ppos = new ParsePosition(0);
        df.parseCurrency("icu123", ppos);
        assertEquals("Should succeed", 6, ppos.getIndex());
        assertEquals("Should succeed", -1, ppos.getErrorIndex());
    }

    @Test
    public void testCurrencyPluralAffixOverrides() {
        // The affix setters should override CurrencyPluralInfo, used in the plural currency constructor.
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.PLURALCURRENCYSTYLE);
        assertEquals("Defaults to unknown currency", " (unknown currency)", df.getPositiveSuffix());
        df.setCurrency(Currency.getInstance("USD"));
        assertEquals("Should resolve to CurrencyPluralInfo", " US dollars", df.getPositiveSuffix());
        df.setPositiveSuffix("lala");
        assertEquals("Custom suffix should round-trip", "lala", df.getPositiveSuffix());
        assertEquals("Custom suffix should be used in formatting", "123.00lala", df.format(123));
    }

    @Test
    public void testParseDoubleMinus() {
        DecimalFormat df = new DecimalFormat("-0", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
        expect2(df, -5, "--5");
    }

    @Test
    public void testParsePercentRegression() {
        DecimalFormat df1 = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
        DecimalFormat df2 = (DecimalFormat) NumberFormat.getPercentInstance(ULocale.ENGLISH);
        df1.setParseStrict(false);
        df2.setParseStrict(false);

        {
            ParsePosition ppos = new ParsePosition(0);
            Number result = df1.parse("50%", ppos);
            assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex());
            assertEquals("df1 should return the number as 50", 50.0, result.doubleValue());
        }
        {
            ParsePosition ppos = new ParsePosition(0);
            Number result = df2.parse("50%", ppos);
            assertEquals("df2 should accept the percent sign", 3, ppos.getIndex());
            assertEquals("df2 should return the number as 0.5", 0.5, result.doubleValue());
        }
        {
            ParsePosition ppos = new ParsePosition(0);
            Number result = df2.parse("50", ppos);
            assertEquals("df2 should return the number as 0.5 even though the percent sign is missing",
                    0.5,
                    result.doubleValue());
        }
    }

    @Test
    public void test13148_GroupingSeparatorOverride() throws Exception {
        DecimalFormat fmt = (DecimalFormat)NumberFormat.getInstance(new ULocale("en", "ZA"));
        DecimalFormatSymbols symbols = fmt.getDecimalFormatSymbols();
        symbols.setDecimalSeparator('.');
        symbols.setGroupingSeparator(',');
        fmt.setDecimalFormatSymbols(symbols);
        Number number = fmt.parse("300,000");
        assertEquals("Should use custom symbols and not monetary symbols", 300000L, number);
    }

    @Test
    public void test11897_LocalizedPatternSeparator() {
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
        dfs.setPatternSeparator('!');
        DecimalFormat df = new DecimalFormat("0", dfs);
        df.applyPattern("a0;b0"); // should not throw
        assertEquals("should apply the normal pattern", df.getNegativePrefix(), "b");
        df.applyLocalizedPattern("c0!d0"); // should not throw
        assertEquals("should apply the localized pattern", df.getNegativePrefix(), "d");
    }

    @Test
    public void test13777_ParseLongNameNonCurrencyMode() {
        // Currency long name should round-trip even when non-currency parsing is used.
        NumberFormat df = NumberFormat.getInstance(ULocale.US, NumberFormat.PLURALCURRENCYSTYLE);
        expect2(df, 1.5, "1.50 US dollars");
    }

    @Test
    public void test13804_EmptyStringsWhenParsing() {
        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
        dfs.setCurrencySymbol("");
        dfs.setDecimalSeparatorString("");
        dfs.setDigitStrings(new String[] { "", "", "", "", "", "", "", "", "", "" });
        dfs.setExponentMultiplicationSign("");
        dfs.setExponentSeparator("");
        dfs.setGroupingSeparatorString("");
        dfs.setInfinity("");
        dfs.setInternationalCurrencySymbol("");
        dfs.setMinusSignString("");
        dfs.setMonetaryDecimalSeparatorString("");
        dfs.setMonetaryGroupingSeparatorString("");
        dfs.setNaN("");
        dfs.setPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT, false, "");
        dfs.setPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT, true, "");
        dfs.setPercentString("");
        dfs.setPerMillString("");
        dfs.setPlusSignString("");

        DecimalFormat df = new DecimalFormat("0", dfs);
        df.setGroupingUsed(true);
        df.setScientificNotation(true);
        df.setParseStrict(false); // enable all matchers
        df.format(0); // should not throw or hit infinite loop
        String[] samples = new String[] {
                "",
                "123",
                "$123",
                "-",
                "+",
                "44%",
                "1E+2.3"
        };
        for (String sample : samples) {
            logln("Attempting parse on: " + sample);
            // We don't care about the results, only that we don't throw and don't loop.
            ParsePosition ppos = new ParsePosition(0);
            df.parse(sample, ppos);
            ppos = new ParsePosition(0);
            df.parseCurrency(sample, ppos);
        }

        // Test with a nonempty exponent separator symbol to cover more code
        dfs.setExponentSeparator("E");
        df.setDecimalFormatSymbols(dfs);
        {
            ParsePosition ppos = new ParsePosition(0);
            df.parse("1E+2.3", ppos);
        }
    }

    @Test
    public void Test20037_ScientificIntegerOverflow() throws ParseException {
        NumberFormat nf = NumberFormat.getInstance(ULocale.US);

        // Test overflow of exponent
        Number result = nf.parse("1E-2147483648");
        assertEquals("Should snap to zero",
                "0", result.toString());

        // Test edge case overflow of exponent
        // Note: the behavior is different from C++; this is probably due to the
        // intermediate BigDecimal form, which has its own restrictions
        result = nf.parse("1E-2147483647E-1");
        assertEquals("Should not overflow and should parse only the first exponent",
                "0.0", result.toString());

        // For Java, we should get *pretty close* to 2^31.
        result = nf.parse("1E-547483647");
        assertEquals("Should *not* snap to zero",
                "1E-547483647", result.toString());

        // Test edge case overflow of exponent
        result = nf.parse(".0003e-2147483644");
        assertEquals("Should not overflow",
                "0", result.toString());

        // Test largest parseable exponent
        // This is limited by ICU's BigDecimal implementation
        result = nf.parse("1e999999999");
        assertEquals("Should not overflow",
                "1E+999999999", result.toString());

        // Test max value as well
        String[] infinityInputs = {
                "9876e1000000000",
                "9876e2147483640",
                "9876e2147483641",
                "9876e2147483642",
                "9876e2147483643",
                "9876e2147483644",
                "9876e2147483645",
                "9876e2147483646",
                "9876e2147483647",
                "9876e2147483648",
                "9876e2147483649",
        };
        for (String input : infinityInputs) {
            result = nf.parse(input);
            assertEquals("Should become Infinity: " + input,
                    "Infinity", result.toString());
        }
    }

    @Test
    public void test13840_ParseLongStringCrash() throws ParseException {
        NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH);
        String bigString =
            "111111111111111111111111111111111111111111111111111111111111111111111" +
            "111111111111111111111111111111111111111111111111111111111111111111111" +
            "111111111111111111111111111111111111111111111111111111111111111111111" +
            "111111111111111111111111111111111111111111111111111111111111111111111" +
            "111111111111111111111111111111111111111111111111111111111111111111111" +
            "111111111111111111111111111111111111111111111111111111111111111111111";
        Number result = nf.parse(bigString);

        // Normalize the input string:
        BigDecimal expectedBigDecimal = new BigDecimal(bigString);
        String expectedUString = expectedBigDecimal.toString();

        // Get the output string:
        BigDecimal actualBigDecimal = (BigDecimal) result;
        String actualUString = actualBigDecimal.toString();

        assertEquals("Should round-trip without crashing", expectedUString, actualUString);
    }

    @Test
    public void test20348_CurrencyPrefixOverride() {
        DecimalFormat fmt = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.ENGLISH);
        assertEquals("Initial pattern",
            "¤#,##0.00", fmt.toPattern());
        assertEquals("Initial prefix",
            "¤", fmt.getPositivePrefix());
        assertEquals("Initial suffix",
            "-¤", fmt.getNegativePrefix());
        assertEquals("Initial format",
            "\u00A4100.00", fmt.format(100));

        fmt.setPositivePrefix("$");
        assertEquals("Set positive prefix pattern",
            "$#,##0.00;-\u00A4#,##0.00", fmt.toPattern());
        assertEquals("Set positive prefix prefix",
            "$", fmt.getPositivePrefix());
        assertEquals("Set positive prefix suffix",
            "-¤", fmt.getNegativePrefix());
        assertEquals("Set positive prefix format",
            "$100.00", fmt.format(100));

        fmt.setNegativePrefix("-$");
        assertEquals("Set negative prefix pattern",
            "$#,##0.00;'-'$#,##0.00", fmt.toPattern());
        assertEquals("Set negative prefix prefix",
            "$", fmt.getPositivePrefix());
        assertEquals("Set negative prefix suffix",
            "-$", fmt.getNegativePrefix());
        assertEquals("Set negative prefix format",
            "$100.00", fmt.format(100));
    }

    @Test
    public void test20358_GroupingInPattern() {
        DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
        assertEquals("Initial pattern",
            "#,##0.###", fmt.toPattern());
        assertTrue("Initial grouping",
            fmt.isGroupingUsed());
        assertEquals("Initial format",
            "54,321", fmt.format(54321));

        fmt.setGroupingUsed(false);
        assertEquals("Set grouping false",
            "0.###", fmt.toPattern());
        assertFalse("Set grouping false grouping",
            fmt.isGroupingUsed());
        assertEquals("Set grouping false format",
            "54321", fmt.format(54321));

        fmt.setGroupingUsed(true);
        assertEquals("Set grouping true",
            "#,##0.###", fmt.toPattern());
        assertTrue("Set grouping true grouping",
            fmt.isGroupingUsed());
        assertEquals("Set grouping true format",
            "54,321", fmt.format(54321));
    }

    @Test
    public void test13731_DefaultCurrency() {
        {
            NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.CURRENCYSTYLE);
            assertEquals("symbol", "¤1.10", nf.format(1.1));
            assertEquals("currency", "XXX", nf.getCurrency().getCurrencyCode());
        }
        {
            NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.ISOCURRENCYSTYLE);
            assertEquals("iso_code", "XXX 1.10", nf.format(1.1));
            assertEquals("currency", "XXX", nf.getCurrency().getCurrencyCode());
        }
        {
            NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH, NumberFormat.PLURALCURRENCYSTYLE);
            assertEquals("plural", "1.10 (unknown currency)", nf.format(1.1));
            assertEquals("currency", "XXX", nf.getCurrency().getCurrencyCode());
        }
    }

    @Test
    public void test20499_CurrencyVisibleDigitsPlural() {
        ULocale locale = new ULocale("ro-RO");
        NumberFormat nf = NumberFormat.getInstance(locale, NumberFormat.PLURALCURRENCYSTYLE);
        String expected = "24,00 lei românești";
        for (int i=0; i<5; i++) {
            String actual = nf.format(24);
            assertEquals("iteration " + i, expected, actual);
        }
    }
}
