/*
 * Copyright (c) 2003, 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.
 */

/*
 * Shared static test methods for numerical tests.  Sharing these
 * helper test methods avoids repeated functions in the various test
 * programs.  The test methods return 1 for a test failure and 0 for
 * success.  The order of arguments to the test methods is generally
 * the test name, followed by the test arguments, the computed result,
 * and finally the expected result.
 */

import sun.misc.FpUtils;

public class Tests {
    private Tests(){}; // do not instantiate

    public static String toHexString(float f) {
        if (!Float.isNaN(f))
            return Float.toHexString(f);
        else
            return "NaN(0x" + Integer.toHexString(Float.floatToRawIntBits(f)) + ")";
    }

    public static String toHexString(double d) {
        if (!Double.isNaN(d))
            return Double.toHexString(d);
        else
            return "NaN(0x" + Long.toHexString(Double.doubleToRawLongBits(d)) + ")";
    }

    /**
     * Return the floating-point value next larger in magnitude.
     */
    public static double nextOut(double d) {
        if (d > 0.0)
            return Math.nextUp(d);
        else
            return -Math.nextUp(-d);
    }

    public static int test(String testName, float input,
                           boolean result, boolean expected) {
        if (expected != result) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\n"  +
                               "\tgot       " + result   + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName, double input,
                           boolean result, boolean expected) {
        if (expected != result) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\n"  +
                               "\tgot       " + result   + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName, float input1, float input2,
                           boolean result, boolean expected) {
        if (expected != result) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\t(" + toHexString(input2) + ")\n" +
                               "\texpected  "  + expected + "\n"  +
                               "\tgot       "  + result   + ").");
            return 1;
        }
        return 0;
    }

    public static int test(String testName, double input1, double input2,
                           boolean result, boolean expected) {
        if (expected != result) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\t(" + toHexString(input2) + ")\n" +
                               "\texpected  "  + expected + "\n"  +
                               "\tgot       "  + result   + ").");
            return 1;
        }
        return 0;
    }

    public static int test(String testName, float input,
                           int result, int expected) {
        if (expected != result) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\n" +
                               "\tgot       " + result    + ").");
            return 1;
        }
        return 0;
    }

    public  static int test(String testName, double input,
                            int result, int expected) {
        if (expected != result) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\n"  +
                               "\tgot       " + result   + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName, float input,
                           float result, float expected) {
        if (Float.compare(expected, result) != 0 ) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       " + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }


    public static int test(String testName, double input,
                           double result, double expected) {
        if (Double.compare(expected, result ) != 0) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       " + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName,
                           float input1, double input2,
                           float result, float expected) {
        if (Float.compare(expected, result ) != 0) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\t(" + toHexString(input2) + ")\n" +
                               "\texpected  "  + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       "  + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName,
                           double input1, double input2,
                           double result, double expected) {
        if (Double.compare(expected, result ) != 0) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\t(" + toHexString(input2) + ")\n" +
                               "\texpected  "  + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       "  + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName,
                           float input1, int input2,
                           float result, float expected) {
        if (Float.compare(expected, result ) != 0) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\n"  +
                               "\texpected  "  + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       "  + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }

    public static int test(String testName,
                           double input1, int input2,
                           double result, double expected) {
        if (Double.compare(expected, result ) != 0) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\n"  +
                               "\texpected  "  + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       "  + result   + "\t(" + toHexString(result) + ").");
            return 1;
        }
        else
            return 0;
    }

    static int testUlpCore(double result, double expected, double ulps) {
        // We assume we won't be unlucky and have an inexact expected
        // be nextDown(2^i) when 2^i would be the correctly rounded
        // answer.  This would cause the ulp size to be half as large
        // as it should be, doubling the measured error).

        if (Double.compare(expected, result) == 0) {
            return 0;   // result and expected are equivalent
        } else {
            if( ulps == 0.0) {
                // Equivalent results required but not found
                return 1;
            } else {
                double difference = expected - result;
                if (FpUtils.isUnordered(expected, result) ||
                    Double.isNaN(difference) ||
                    // fail if greater than or unordered
                    !(Math.abs( difference/Math.ulp(expected) ) <= Math.abs(ulps)) ) {
                    return 1;
                }
                else
                    return 0;
            }
        }
    }

    // One input argument.
    public static int testUlpDiff(String testName, double input,
                                  double result, double expected, double ulps) {
        int code = testUlpCore(result, expected, ulps);
        if (code == 1) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       " + result   + "\t(" + toHexString(result) + ");\n" +
                               "\tdifference greater than ulp tolerance " + ulps);
        }
        return code;
    }

    // Two input arguments.
    public static int testUlpDiff(String testName, double input1, double input2,
                                  double result, double expected, double ulps) {
        int code = testUlpCore(result, expected, ulps);
        if (code == 1) {
            System.err.println("Failure for "  + testName + ":\n" +
                               "\tFor inputs " + input1   + "\t(" + toHexString(input1) + ") and "
                                               + input2   + "\t(" + toHexString(input2) + ")\n" +
                               "\texpected  "  + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       "  + result   + "\t(" + toHexString(result) + ");\n" +
                               "\tdifference greater than ulp tolerance " + ulps);
        }
        return code;
    }

    // For a successful test, the result must be within the ulp bound of
    // expected AND the result must have absolute value less than or
    // equal to absBound.
    public static int testUlpDiffWithAbsBound(String testName, double input,
                                              double result, double expected,
                                              double ulps, double absBound) {
        int code = 0;   // return code value

        if (!(StrictMath.abs(result) <= StrictMath.abs(absBound)) &&
            !Double.isNaN(expected)) {
            code = 1;
        } else
            code = testUlpCore(result, expected, ulps);

        if (code == 1) {
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
                               "\tgot       " + result   + "\t(" + toHexString(result) + ");\n" +
                               "\tdifference greater than ulp tolerance " + ulps +
                               " or the result has larger magnitude than " + absBound);
        }
        return code;
    }

    // For a successful test, the result must be within the ulp bound of
    // expected AND the result must have absolute value greater than
    // or equal to the lowerBound.
    public static int testUlpDiffWithLowerBound(String testName, double input,
                                                double result, double expected,
                                                double ulps, double lowerBound) {
        int code = 0;   // return code value

        if (!(result >= lowerBound) && !Double.isNaN(expected)) {
            code = 1;
        } else
            code = testUlpCore(result, expected, ulps);

        if (code == 1) {
            System.err.println("Failure for " + testName +
                               ":\n" +
                               "\tFor input "   + input    + "\t(" + toHexString(input) + ")" +
                               "\n\texpected  " + expected + "\t(" + toHexString(expected) + ")" +
                               "\n\tgot       " + result   + "\t(" + toHexString(result) + ");" +
                               "\ndifference greater than ulp tolerance " + ulps +
                               " or result not greater than or equal to the bound " + lowerBound);
        }
        return code;
    }

    public static int testTolerance(String testName, double input,
                                    double result, double expected, double tolerance) {
        if (Double.compare(expected, result ) != 0) {
            double difference = expected - result;
            if (FpUtils.isUnordered(expected, result) ||
                Double.isNaN(difference) ||
                // fail if greater than or unordered
                !(Math.abs((difference)/expected) <= StrictMath.pow(10, -tolerance)) ) {
                System.err.println("Failure for " + testName + ":\n" +
                                   "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                                   "\texpected  " + expected + "\t(" + toHexString(expected) + ")\n" +
                                   "\tgot       " + result   + "\t(" + toHexString(result) + ");\n" +
                                   "\tdifference greater than tolerance 10^-" + tolerance);
                return 1;
            }
            return 0;
        }
        else
            return 0;
    }

    // For a successful test, the result must be within the upper and
    // lower bounds.
    public static int testBounds(String testName, double input, double result,
                                 double bound1, double bound2) {
        if ((result >= bound1 && result <= bound2) ||
            (result <= bound1 && result >= bound2))
            return 0;
        else {
            double lowerBound = Math.min(bound1, bound2);
            double upperBound = Math.max(bound1, bound2);
            System.err.println("Failure for " + testName + ":\n" +
                               "\tFor input " + input    + "\t(" + toHexString(input) + ")\n" +
                               "\tgot       " + result   + "\t(" + toHexString(result) + ");\n" +
                               "\toutside of range\n" +
                               "\t[" + lowerBound    + "\t(" + toHexString(lowerBound) + "), " +
                               upperBound    + "\t(" + toHexString(upperBound) + ")]");
            return 1;
        }
    }
}
