/*
 * Copyright (c) 2023, 2024, 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 8302976
 * @summary Verify conversion cons between float and the binary16 format
 * @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch=="aarch64"
 *           | (os.arch == "riscv64" & vm.cpu.features ~= ".*zfh.*")
 *           | ((os.arch == "ppc64" | os.arch == "ppc64le") & vm.cpu.features ~= ".*darn.*")
 * @requires vm.compiler1.enabled & vm.compiler2.enabled
 * @requires vm.compMode != "Xcomp"
 * @comment default run:
 * @run main TestConstFloat16ToFloat
 * @comment C1 JIT compilation only:
 * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestConstFloat16ToFloat::test* -XX:TieredStopAtLevel=1 TestConstFloat16ToFloat
 * @comment C2 JIT compilation only:
 * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestConstFloat16ToFloat::test* -XX:-TieredCompilation TestConstFloat16ToFloat
 */

public class TestConstFloat16ToFloat {

    public static class Binary16 {
        public static final short POSITIVE_INFINITY = (short)0x7c00;
        public static final short MAX_VALUE         = 0x7bff;
        public static final short ONE               = 0x3c00;
        public static final short MIN_NORMAL        = 0x0400;
        public static final short MAX_SUBNORMAL     = 0x03ff;
        public static final short MIN_VALUE         = 0x0001;
        public static final short POSITIVE_ZERO     = 0x0000;
    }

    static final short[] sCon = {
        Short.MIN_VALUE,
        Short.MIN_VALUE + 1,
        -1,
        0,
        +1,
        Short.MAX_VALUE - 1,
        Short.MAX_VALUE,
        Binary16.MIN_VALUE,
        Binary16.MIN_NORMAL,
        Binary16.POSITIVE_ZERO,
        Binary16.ONE,
        Binary16.MAX_VALUE,
        Binary16.MAX_SUBNORMAL,
        Binary16.POSITIVE_INFINITY
    };

    public final static class BinaryF16 {
        public static final float POSITIVE_INFINITY = Float.POSITIVE_INFINITY;
        public static final float MAX_VALUE         = 65504.0f;
        public static final float ONE               = 1.0f;
        public static final float MIN_NORMAL        = 0x1.0p-14f;
        public static final float MAX_SUBNORMAL     = 0x1.ff8p-15f;
        public static final float MIN_VALUE         = 0x1.0p-24f;
        public static final float POSITIVE_ZERO     = +0x0f;
    }

    static float[] fCon = {
        0.0f - BinaryF16.POSITIVE_INFINITY,
        0.0f - BinaryF16.MAX_VALUE,
        0.0f - BinaryF16.MAX_SUBNORMAL,
        0.0f - BinaryF16.MIN_VALUE,
        0.0f - BinaryF16.MIN_NORMAL,
        -1.0f,
        -0.0f,
        BinaryF16.MIN_VALUE,
        BinaryF16.MIN_NORMAL,
        BinaryF16.POSITIVE_ZERO,
        BinaryF16.ONE,
        BinaryF16.MAX_VALUE,
        BinaryF16.MAX_SUBNORMAL,
        BinaryF16.POSITIVE_INFINITY
    };

    // Testing some constant values (optimized by C2).
    public static void testFloat16Const(float[] fRes) {
        fRes[ 0] = Float.float16ToFloat(Short.MIN_VALUE);
        fRes[ 1] = Float.float16ToFloat((short)(Short.MIN_VALUE + 1));
        fRes[ 2] = Float.float16ToFloat((short)-1);
        fRes[ 3] = Float.float16ToFloat((short)0);
        fRes[ 4] = Float.float16ToFloat((short)+1);
        fRes[ 5] = Float.float16ToFloat((short)(Short.MAX_VALUE - 1));
        fRes[ 6] = Float.float16ToFloat(Short.MAX_VALUE);
        fRes[ 7] = Float.float16ToFloat(Binary16.MIN_VALUE);
        fRes[ 8] = Float.float16ToFloat(Binary16.MIN_NORMAL);
        fRes[ 9] = Float.float16ToFloat(Binary16.POSITIVE_ZERO);
        fRes[10] = Float.float16ToFloat(Binary16.ONE);
        fRes[11] = Float.float16ToFloat(Binary16.MAX_VALUE);
        fRes[12] = Float.float16ToFloat(Binary16.MAX_SUBNORMAL);
        fRes[13] = Float.float16ToFloat(Binary16.POSITIVE_INFINITY);
    }

    public static void testFloatConst(short[] sRes) {
        sRes[ 0] = Float.floatToFloat16(0.0f - BinaryF16.POSITIVE_INFINITY);
        sRes[ 1] = Float.floatToFloat16(0.0f - BinaryF16.MAX_VALUE);
        sRes[ 2] = Float.floatToFloat16(0.0f - BinaryF16.MAX_SUBNORMAL);
        sRes[ 3] = Float.floatToFloat16(0.0f - BinaryF16.MIN_VALUE);
        sRes[ 4] = Float.floatToFloat16(0.0f - BinaryF16.MIN_NORMAL);
        sRes[ 5] = Float.floatToFloat16(-1.0f);
        sRes[ 6] = Float.floatToFloat16(-0.0f);
        sRes[ 7] = Float.floatToFloat16(BinaryF16.MIN_VALUE);
        sRes[ 8] = Float.floatToFloat16(BinaryF16.MIN_NORMAL);
        sRes[ 9] = Float.floatToFloat16(BinaryF16.POSITIVE_ZERO);
        sRes[10] = Float.floatToFloat16(BinaryF16.ONE);
        sRes[11] = Float.floatToFloat16(BinaryF16.MAX_VALUE);
        sRes[12] = Float.floatToFloat16(BinaryF16.MAX_SUBNORMAL);
        sRes[13] = Float.floatToFloat16(BinaryF16.POSITIVE_INFINITY);
    }

    public static int run() {
        int errors = 0;
        short s = Float.floatToFloat16(0.0f); // Load Float class
        // Testing constant float16 values.
        float[] fRes = new float[sCon.length];
        testFloat16Const(fRes);
        for (int i = 0; i < sCon.length; i++) {
            float fVal = Float.float16ToFloat(sCon[i]);
            if (Float.floatToRawIntBits(fRes[i]) != Float.floatToRawIntBits(fVal)) {
                errors++;
                String cVal_hex = Integer.toHexString(sCon[i] & 0xffff);
                String fRes_hex = Integer.toHexString(Float.floatToRawIntBits(fRes[i]));
                String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal));
                System.out.println("Inconsistent result for Float.float16ToFloat(" + cVal_hex + "): " +
                                    fRes[i] + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex);
            }
        }

        // Testing constant float values.
        short[] sRes = new short[fCon.length];
        testFloatConst(sRes);
        for (int i = 0; i < fCon.length; i++) {
            short sVal = Float.floatToFloat16(fCon[i]);
            if (sRes[i] != sVal) {
                errors++;
                String cVal_hex = Integer.toHexString(Float.floatToRawIntBits(fCon[i]));
                String sRes_hex = Integer.toHexString(sRes[i] & 0xffff);
                String sVal_hex = Integer.toHexString(sVal & 0xffff);
                System.out.println("Inconsistent result for Float.floatToFloat16(" + fCon[i] + "/" + cVal_hex + "): " +
                                    sRes_hex + "(" + sRes + ")" + " != " + sVal_hex + "(" + sVal + ")");
            }
        }
        return errors;

    }

    public static void main(String[] args) {
        int errors = 0;
        // Run twice to trigger compilation
        for (int i = 0; i < 2; i++) {
            errors += run();
        }
        if (errors > 0) {
            throw new RuntimeException(errors + " errors");
        }
    }
}
