/*
 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 4160406 4705734 4707389 4826774 4895911 4421494 7021568 7039369
 * @summary Test for Double.parseDouble method and acceptance regex
 */

import java.util.regex.*;
import java.math.BigDecimal;

public class ParseDouble {

    private static void check(String val, double expected) {
        double n = Double.parseDouble(val);
        if (n != expected)
            throw new RuntimeException("Double.parseDouble failed. String:" +
                                                val + " Result:" + n);
    }

    private static void rudimentaryTest() {
        check(new String(""+Double.MIN_VALUE), Double.MIN_VALUE);
        check(new String(""+Double.MAX_VALUE), Double.MAX_VALUE);

        check("10",     (double)  10.0);
        check("10.0",   (double)  10.0);
        check("10.01",  (double)  10.01);

        check("-10",    (double) -10.0);
        check("-10.00", (double) -10.0);
        check("-10.01", (double) -10.01);
    }


    static  String badStrings[] = {
        "",
        "+",
        "-",
        "+e",
        "-e",
        "+e170",
        "-e170",

        // Make sure intermediate white space is not deleted.
        "1234   e10",
        "-1234   e10",

        // Control characters in the interior of a string are not legal
        "1\u0007e1",
        "1e\u00071",

        // NaN and infinity can't have trailing type suffices or exponents
        "NaNf",
        "NaNF",
        "NaNd",
        "NaND",
        "-NaNf",
        "-NaNF",
        "-NaNd",
        "-NaND",
        "+NaNf",
        "+NaNF",
        "+NaNd",
        "+NaND",
        "Infinityf",
        "InfinityF",
        "Infinityd",
        "InfinityD",
        "-Infinityf",
        "-InfinityF",
        "-Infinityd",
        "-InfinityD",
        "+Infinityf",
        "+InfinityF",
        "+Infinityd",
        "+InfinityD",

        "NaNe10",
        "-NaNe10",
        "+NaNe10",
        "Infinitye10",
        "-Infinitye10",
        "+Infinitye10",

        // Non-ASCII digits are not recognized
        "\u0661e\u0661", // 1e1 in Arabic-Indic digits
        "\u06F1e\u06F1", // 1e1 in Extended Arabic-Indic digits
        "\u0967e\u0967", // 1e1 in Devanagari digits

        // JCK test lex03592m3
        ".",

        // JCK test lex03592m4
        "e42",

        // JCK test lex03592m5
        ".e42",

        // JCK test lex03592m6
        "d",

        // JCK test lex03592m7
        ".d",

        // JCK test lex03592m8
        "e42d",

        // JCK test lex03592m9
        ".e42d",

        // JCK test lex03593m10
        "1A01.01125e-10d",

        // JCK test lex03593m11
        "2;3.01125e-10d",

        // JCK test lex03593m12
        "1_34.01125e-10d",

        // JCK test lex03593m14
        "202..01125e-10d",

        // JCK test lex03593m15
        "202,01125e-10d",

        // JCK test lex03593m16
        "202.03b4e-10d",

        // JCK test lex03593m18
        "202.06_3e-10d",

        // JCK test lex03593m20
        "202.01125e-f0d",

        // JCK test lex03593m21
        "202.01125e_3d",

        // JCK test lex03593m22
        "202.01125e -5d",

        // JCK test lex03593m24
        "202.01125e-10r",

        // JCK test lex03593m25
        "202.01125e-10ff",

        // JCK test lex03593m26
        "1234L.01",

        // JCK test lex03593m27
        "12ee-2",

        // JCK test lex03593m28
        "12e-2.2.2",

        // JCK test lex03593m29
        "12.01e+",

        // JCK test lex03593m30
        "12.01E",

        // Bad hexadecimal-style strings

        // Two leading zeros
        "00x1.0p1",

        // Must have hex specifier
        "1.0p1",
        "00010p1",
        "deadbeefp1",

        // Need an explicit fully-formed exponent
        "0x1.0p",
        "0x1.0",

        // Exponent must be in decimal
        "0x1.0pa",
        "0x1.0pf",

        // Exponent separated by "p"
        "0x1.0e22",
        "0x1.0e22",

        // Need a signifcand
        "0xp22"
    };

    static String goodStrings[] = {
        "NaN",
        "+NaN",
        "-NaN",
        "Infinity",
        "+Infinity",
        "-Infinity",
        "1.1e-23f",
        ".1e-23f",
        "1e-23",
        "1f",
        "0",
        "-0",
        "+0",
        "00",
        "00",
        "-00",
        "+00",
        "0000000000",
        "-0000000000",
        "+0000000000",
        "1",
        "2",
        "1234",
        "-1234",
        "+1234",
        "2147483647",   // Integer.MAX_VALUE
        "2147483648",
        "-2147483648",  // Integer.MIN_VALUE
        "-2147483649",

        "16777215",
        "16777216",     // 2^24
        "16777217",

        "-16777215",
        "-16777216",    // -2^24
        "-16777217",

        "9007199254740991",
        "9007199254740992",     // 2^53
        "9007199254740993",

        "-9007199254740991",
        "-9007199254740992",    // -2^53
        "-9007199254740993",

        "9223372036854775807",
        "9223372036854775808",  // Long.MAX_VALUE
        "9223372036854775809",

        "-9223372036854775808",
        "-9223372036854775809", // Long.MIN_VALUE
        "-9223372036854775810",

        // Culled from JCK test lex03591m1
        "54.07140d",
        "7.01e-324d",
        "2147483647.01d",
        "1.2147483647f",
        "000000000000000000000000001.F",
        "1.00000000000000000000000000e-2F",

        // Culled from JCK test lex03592m2
        "2.",
        ".0909",
        "122112217090.0",
        "7090e-5",
        "2.E-20",
        ".0909e42",
        "122112217090.0E+100",
        "7090f",
        "2.F",
        ".0909d",
        "122112217090.0D",
        "7090e-5f",
        "2.E-20F",
        ".0909e42d",
        "122112217090.0E+100D",

        // Culled from JCK test lex03594m31 -- unicode escapes
        "\u0035\u0031\u0034\u0039\u0032\u0033\u0036\u0037\u0038\u0030.1102E-209D",
        "1290873\u002E12301e100",
        "1.1E-10\u0066",

        // Culled from JCK test lex03595m1
        "0.0E-10",
        "1E10",

        // Culled from JCK test lex03691m1
        "0.f",
        "1f",
        "0.F",
        "1F",
        "0.12d",
        "1e-0d",
        "12.e+1D",
        "0e-0D",
        "12.e+01",
        "1e-01",

        // Good hex strings
        // Vary capitalization of separators.

        "0x1p1",
        "0X1p1",
        "0x1P1",
        "0X1P1",
        "0x1p1f",
        "0X1p1f",
        "0x1P1f",
        "0X1P1f",
        "0x1p1F",
        "0X1p1F",
        "0x1P1F",
        "0X1P1F",
        "0x1p1d",
        "0X1p1d",
        "0x1P1d",
        "0X1P1d",
        "0x1p1D",
        "0X1p1D",
        "0x1P1D",
        "0X1P1D",

        "-0x1p1",
        "-0X1p1",
        "-0x1P1",
        "-0X1P1",
        "-0x1p1f",
        "-0X1p1f",
        "-0x1P1f",
        "-0X1P1f",
        "-0x1p1F",
        "-0X1p1F",
        "-0x1P1F",
        "-0X1P1F",
        "-0x1p1d",
        "-0X1p1d",
        "-0x1P1d",
        "-0X1P1d",
        "-0x1p1D",
        "-0X1p1D",
        "-0x1P1D",
        "-0X1P1D",

        "0x1p-1",
        "0X1p-1",
        "0x1P-1",
        "0X1P-1",
        "0x1p-1f",
        "0X1p-1f",
        "0x1P-1f",
        "0X1P-1f",
        "0x1p-1F",
        "0X1p-1F",
        "0x1P-1F",
        "0X1P-1F",
        "0x1p-1d",
        "0X1p-1d",
        "0x1P-1d",
        "0X1P-1d",
        "0x1p-1D",
        "0X1p-1D",
        "0x1P-1D",
        "0X1P-1D",

        "-0x1p-1",
        "-0X1p-1",
        "-0x1P-1",
        "-0X1P-1",
        "-0x1p-1f",
        "-0X1p-1f",
        "-0x1P-1f",
        "-0X1P-1f",
        "-0x1p-1F",
        "-0X1p-1F",
        "-0x1P-1F",
        "-0X1P-1F",
        "-0x1p-1d",
        "-0X1p-1d",
        "-0x1P-1d",
        "-0X1P-1d",
        "-0x1p-1D",
        "-0X1p-1D",
        "-0x1P-1D",
        "-0X1P-1D",


        // Try different significand combinations
        "0xap1",
        "0xbp1",
        "0xcp1",
        "0xdp1",
        "0xep1",
        "0xfp1",

        "0x1p1",
        "0x.1p1",
        "0x1.1p1",

        "0x001p23",
        "0x00.1p1",
        "0x001.1p1",

        "0x100p1",
        "0x.100p1",
        "0x1.100p1",

        "0x00100p1",
        "0x00.100p1",
        "0x001.100p1",

        // Limits

        "1.7976931348623157E308",     // Double.MAX_VALUE
        "4.9e-324",                   // Double.MIN_VALUE
        "2.2250738585072014e-308",    // Double.MIN_NORMAL

        "2.2250738585072012e-308",    // near Double.MIN_NORMAL
    };

    static String paddedBadStrings[];
    static String paddedGoodStrings[];
    static {
        String pad = " \t\n\r\f\u0001\u000b\u001f";
        paddedBadStrings = new String[badStrings.length];
        for(int i = 0 ; i <  badStrings.length; i++)
            paddedBadStrings[i] = pad + badStrings[i] + pad;

        paddedGoodStrings = new String[goodStrings.length];
        for(int i = 0 ; i <  goodStrings.length; i++)
            paddedGoodStrings[i] = pad + goodStrings[i] + pad;

    }


    /*
     * Throws an exception if <code>Input</code> is
     * <code>exceptionalInput</code> and {@link Double.parseDouble
     * parseDouble} does <em>not</em> throw an exception or if
     * <code>Input</code> is not <code>exceptionalInput</code> and
     * <code>parseDouble</code> throws an exception.  This method does
     * not attempt to test whether the string is converted to the
     * proper value; just whether the input is accepted appropriately
     * or not.
     */
    private static void testParsing(String [] input,
                                    boolean exceptionalInput) {
        for(int i = 0; i < input.length; i++) {
            double d;

            try {
                d = Double.parseDouble(input[i]);
            }
            catch (NumberFormatException e) {
                if (! exceptionalInput) {
                    throw new RuntimeException("Double.parseDouble rejected " +
                                               "good string `" + input[i] +
                                               "'.");
                }
                break;
            }
            if (exceptionalInput) {
                throw new RuntimeException("Double.parseDouble accepted " +
                                           "bad string `" + input[i] +
                                           "'.");
            }
        }
    }

    /*
     * Throws an exception if <code>Input</code> is
     * <code>exceptionalInput</code> and the regular expression
     * matches one of the strings or if <code>Input</code> is not
     * <code>exceptionalInput</code> and the regular expression fails
     * to match an input string.
     */
    private static void testRegex(String [] input, boolean exceptionalInput) {
        /*
         * The regex below is taken from the JavaDoc for
         * Double.valueOf.
         */

        final String Digits     = "(\\p{Digit}+)";
        final String HexDigits  = "(\\p{XDigit}+)";
        // an exponent is 'e' or 'E' followed by an optionally
        // signed decimal integer.
        final String Exp        = "[eE][+-]?"+Digits;
        final String fpRegex    =
            ("[\\x00-\\x20]*"+  // Optional leading "whitespace"
             "[+-]?(" + // Optional sign character
             "NaN|" +           // "NaN" string
             "Infinity|" +      // "Infinity" string

             // A floating-point string representing a finite positive
             // number without a leading sign has at most five basic pieces:
             // Digits . Digits ExponentPart FloatTypeSuffix
             //
             // Since this method allows integer-only strings as input
             // in addition to strings of floating-point literals, the
             // two sub-patterns below are simplifications of the grammar
             // productions from the Java Language Specification, 2nd
             // edition, section 3.10.2.


             // A decimal floating-point string representing a finite positive
             // number without a leading sign has at most five basic pieces:
             // Digits . Digits ExponentPart FloatTypeSuffix
             //
             // Since this method allows integer-only strings as input
             // in addition to strings of floating-point literals, the
             // two sub-patterns below are simplifications of the grammar
             // productions from the Java Language Specification, 2nd
             // edition, section 3.10.2.

             // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
             "(((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+

             // . Digits ExponentPart_opt FloatTypeSuffix_opt
             "(\\.("+Digits+")("+Exp+")?))|"+

            // Hexadecimal strings
            "((" +
             // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
             "(0[xX]" + HexDigits + "(\\.)?)|" +

             // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
             "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +

             ")[pP][+-]?" + Digits + "))" +
             "[fFdD]?))" +
             "[\\x00-\\x20]*");// Optional trailing "whitespace"
        Pattern fpPattern = Pattern.compile(fpRegex);

        for(int i = 0; i < input.length; i++) {
             Matcher m = fpPattern.matcher(input[i]);
             if (m.matches() != ! exceptionalInput) {
                 throw new RuntimeException("Regular expression " +
                                            (exceptionalInput?
                                             "accepted bad":
                                             "rejected good") +
                                            " string `" +
                                            input[i] + "'.");
             }
        }

    }

    /**
     * For each subnormal power of two, test at boundaries of
     * region that should convert to that value.
     */
    private static void testSubnormalPowers() {
        BigDecimal TWO = BigDecimal.valueOf(2);
        // An ulp is the same for all subnormal values
        BigDecimal ulp_BD = new BigDecimal(Double.MIN_VALUE);

        // Test subnormal powers of two
        for(int i = -1074; i <= -1022; i++) {
            double d = Math.scalb(1.0, i);

            /*
             * The region [d - ulp/2, d + ulp/2] should round to d.
             */
            BigDecimal d_BD = new BigDecimal(d);

            BigDecimal lowerBound = d_BD.subtract(ulp_BD.divide(TWO));
            BigDecimal upperBound = d_BD.add(ulp_BD.divide(TWO));

            double convertedLowerBound = Double.parseDouble(lowerBound.toString());
            double convertedUpperBound = Double.parseDouble(upperBound.toString());
        }
    }


    private static void testStrictness() {
        final double expected = 0x0.0000008000001p-1022;
        boolean failed = false;
        double conversion = 0.0;
        double sum = 0.0; // Prevent conversion from being optimized away

        //2^-1047 + 2^-1075
        String decimal = "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";

        for(int i = 0; i <= 12_000; i++) {
            conversion = Double.parseDouble(decimal);
            sum += conversion;
            if (conversion != expected) {
                failed = true;
                System.out.printf("Iteration %d converts as %a%n",
                                  i, conversion);
            }
        }

        System.out.println("Sum = "  + sum);
        if (failed)
            throw new RuntimeException("Inconsistent conversion");
    }

    public static void main(String[] args) throws Exception {
        rudimentaryTest();

        testParsing(goodStrings, false);
        testParsing(paddedGoodStrings, false);
        testParsing(badStrings, true);
        testParsing(paddedBadStrings, true);

        testRegex(goodStrings, false);
        testRegex(paddedGoodStrings, false);
        testRegex(badStrings, true);
        testRegex(paddedBadStrings, true);

        testSubnormalPowers();
        testStrictness();
    }
}
