Emulated Math.min/max(float/double) did not match JVM behavior

- JVM considers negative zero smaller than positive zero
- JVM returns NaN if either argument is NaN

Native JS Math.min/max provides the same behavior so we use it now in
favor of the previous implementation.

Change-Id: I293722a7bf704e7d15fcd3dfd25479ff8813a782
Bug-Link: https://github.com/gwtproject/gwt/issues/9281
diff --git a/user/super/com/google/gwt/emul/java/lang/Math.java b/user/super/com/google/gwt/emul/java/lang/Math.java
index fc59cdc..f3b4f23 100644
--- a/user/super/com/google/gwt/emul/java/lang/Math.java
+++ b/user/super/com/google/gwt/emul/java/lang/Math.java
@@ -146,11 +146,11 @@
   }
 
   public static double max(double x, double y) {
-    return x > y ? x : y;
+    return NativeMath.max(x, y);
   }
 
   public static float max(float x, float y) {
-    return x > y ? x : y;
+    return (float) NativeMath.max(x, y);
   }
 
   public static int max(int x, int y) {
@@ -162,11 +162,11 @@
   }
 
   public static double min(double x, double y) {
-    return x < y ? x : y;
+    return NativeMath.min(x, y);
   }
 
   public static float min(float x, float y) {
-    return x < y ? x : y;
+    return (float) NativeMath.min(x, y);
   }
 
   public static int min(int x, int y) {
@@ -301,6 +301,8 @@
     public static native double exp(double x);
     public static native double floor(double x);
     public static native double log(double x);
+    public static native double max(double x, double y);
+    public static native double min(double x, double y);
     public static native double pow(double x, double exp);
     public static native double random();
     public static native double round(double x);
diff --git a/user/test/com/google/gwt/emultest/java/lang/MathTest.java b/user/test/com/google/gwt/emultest/java/lang/MathTest.java
index 56ae5e1..55d295a 100644
--- a/user/test/com/google/gwt/emultest/java/lang/MathTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/MathTest.java
@@ -97,6 +97,70 @@
     assertEquals(Double.POSITIVE_INFINITY, v);
   }
 
+  public void testMax() {
+    assertEquals(2d, Math.max(1d, 2d));
+    assertEquals(2d, Math.max(2d, 1d));
+    assertEquals(0d, Math.max(-0d, 0d));
+    assertEquals(0d, Math.max(0d, -0d));
+    assertEquals(1d, Math.max(-1d, 1d));
+    assertEquals(1d, Math.max(1d, -1d));
+    assertEquals(-1d, Math.max(-1d, -2d));
+    assertEquals(-1d, Math.max(-2d, -1d));
+    assertTrue(Double.isNaN(Math.max(Double.NaN, 1d)));
+    assertTrue(Double.isNaN(Math.max(1d, Double.NaN)));
+    assertTrue(Double.isNaN(Math.max(Double.NaN, Double.POSITIVE_INFINITY)));
+    assertTrue(Double.isNaN(Math.max(Double.POSITIVE_INFINITY, Double.NaN)));
+    assertTrue(Double.isNaN(Math.max(Double.NaN, Double.NEGATIVE_INFINITY)));
+    assertTrue(Double.isNaN(Math.max(Double.NEGATIVE_INFINITY, Double.NaN)));
+
+    assertEquals(2f, Math.max(1f, 2f));
+    assertEquals(2f, Math.max(2f, 1f));
+    assertEquals(0f, Math.max(-0f, 0f));
+    assertEquals(0f, Math.max(0f, -0f));
+    assertEquals(1f, Math.max(-1f, 1f));
+    assertEquals(1f, Math.max(1f, -1f));
+    assertEquals(-1f, Math.max(-1f, -2f));
+    assertEquals(-1f, Math.max(-2f, -1f));
+    assertTrue(Float.isNaN(Math.max(Float.NaN, 1f)));
+    assertTrue(Float.isNaN(Math.max(1f, Float.NaN)));
+    assertTrue(Float.isNaN(Math.max(Float.NaN, Float.POSITIVE_INFINITY)));
+    assertTrue(Float.isNaN(Math.max(Float.POSITIVE_INFINITY, Float.NaN)));
+    assertTrue(Float.isNaN(Math.max(Float.NaN, Float.NEGATIVE_INFINITY)));
+    assertTrue(Float.isNaN(Math.max(Float.NEGATIVE_INFINITY, Float.NaN)));
+  }
+
+  public void testMin() {
+    assertEquals(1d, Math.min(1d, 2d));
+    assertEquals(1d, Math.min(2d, 1d));
+    assertEquals(-0d, Math.min(-0d, 0d));
+    assertEquals(-0d, Math.min(0d, -0d));
+    assertEquals(-1d, Math.min(-1d, 1d));
+    assertEquals(-1d, Math.min(1d, -1d));
+    assertEquals(-2d, Math.min(-1d, -2d));
+    assertEquals(-2d, Math.min(-2d, -1d));
+    assertTrue(Double.isNaN(Math.min(Double.NaN, 1d)));
+    assertTrue(Double.isNaN(Math.min(1d, Double.NaN)));
+    assertTrue(Double.isNaN(Math.min(Double.NaN, Double.POSITIVE_INFINITY)));
+    assertTrue(Double.isNaN(Math.min(Double.POSITIVE_INFINITY, Double.NaN)));
+    assertTrue(Double.isNaN(Math.min(Double.NaN, Double.NEGATIVE_INFINITY)));
+    assertTrue(Double.isNaN(Math.min(Double.NEGATIVE_INFINITY, Double.NaN)));
+
+    assertEquals(1f, Math.min(1f, 2f));
+    assertEquals(1f, Math.min(2f, 1f));
+    assertEquals(-0f, Math.min(-0f, 0f));
+    assertEquals(-0f, Math.min(0f, -0f));
+    assertEquals(-1f, Math.min(-1f, 1f));
+    assertEquals(-1f, Math.min(1f, -1f));
+    assertEquals(-2f, Math.min(-1f, -2f));
+    assertEquals(-2f, Math.min(-2f, -1f));
+    assertTrue(Float.isNaN(Math.min(Float.NaN, 1f)));
+    assertTrue(Float.isNaN(Math.min(1f, Float.NaN)));
+    assertTrue(Float.isNaN(Math.min(Float.NaN, Float.POSITIVE_INFINITY)));
+    assertTrue(Float.isNaN(Math.min(Float.POSITIVE_INFINITY, Float.NaN)));
+    assertTrue(Float.isNaN(Math.min(Float.NaN, Float.NEGATIVE_INFINITY)));
+    assertTrue(Float.isNaN(Math.min(Float.NEGATIVE_INFINITY, Float.NaN)));
+  }
+
   public void testLog() {
     double v = Math.log(Math.E);
     assertEquals(1.0, v, 1e-15);