Implement java.util.Random based on Harmony and gwt-java-math.
Reimplement core functionality to avoid 'long' arithmetic where possible.

Review by: scottb



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7414 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/util/Random.java b/user/super/com/google/gwt/emul/java/util/Random.java
new file mode 100644
index 0000000..0ded4c2
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/Random.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ * 
+ * INCLUDES MODIFICATIONS BY RICHARD ZSCHECH AS WELL AS GOOGLE.
+ */
+package java.util;
+
+import java.io.Serializable;
+
+/**
+ * This class provides methods that generates pseudo-random numbers of different
+ * types, such as {@code int}, {@code long}, {@code double}, and {@code float}.
+ * It follows the algorithms specified in the JRE javadoc.
+ */
+public class Random implements Serializable {
+
+  private static final double multiplierHi = 0x5de;
+  private static final double multiplierLo = 0xece66d;
+  private static final double twoToThe24 = 16777216.0;
+  private static final double twoToThe31 = 2147483648.0;
+  private static final double twoToThe32 = 4294967296.0;
+  private static final double twoToTheMinus24 =
+    5.9604644775390625e-8;
+  private static final double twoToTheMinus26 =
+    1.490116119384765625e-8;
+  private static final double twoToTheMinus31 =
+    4.656612873077392578125e-10;
+  private static final double twoToTheMinus53 =
+    1.1102230246251565404236316680908203125e-16;
+  private static final double[] twoToTheXMinus24 = new double[25];
+  private static final double[] twoToTheXMinus48 = new double[33];
+
+  /**
+   * A value used to avoid two random number generators produced at the same
+   * time having the same seed.
+   */
+  private static int uniqueSeed = 0;
+
+  // Initialize power-of-two tables
+  static {
+    double twoToTheXMinus48Tmp = 1.52587890625e-5; // 1.0 / (1 << (48 - 32));
+    for (int i = 32; i >= 0; i--) {
+      twoToTheXMinus48[i] = twoToTheXMinus48Tmp;
+      twoToTheXMinus48Tmp *= 0.5;
+    }
+    
+    double twoToTheXMinus24Tmp = 1.0;
+    for (int i = 24; i >= 0; i--) {
+      twoToTheXMinus24[i] = twoToTheXMinus24Tmp;
+      twoToTheXMinus24Tmp *= 0.5;
+    }
+  }
+
+  /**
+   * Return the number of milliseconds since 1/1/1970 as a double
+   * in order to avoid the use os LongLib.
+   */
+  private static native double currentTimeMillis() /*-{
+    return (new Date()).getTime();
+  }-*/;
+
+  /**
+   * The boolean value indicating if the second Gaussian number is available.
+   * 
+   * @serial
+   */
+  private boolean haveNextNextGaussian = false;
+
+  /**
+   * The second Gaussian generated number.
+   * 
+   * @serial
+   */
+  private double nextNextGaussian;
+  
+  /**
+   * The high 24 bits of the 48=bit seed value.
+   * 
+   * @serial It is associated with the internal state of this generator.
+   */
+  private double seedhi;
+
+  /**
+   * The low 24 bits of the 48=bit seed value.
+   * 
+   * @serial It is associated with the internal state of this generator.
+   */
+  private double seedlo;
+  
+  /**
+   * Construct a random generator with the current time of day in milliseconds
+   * plus a unique counter value as the initial state.
+   * 
+   * @see #setSeed
+   */
+  public Random() {
+    double seed = uniqueSeed++ + currentTimeMillis();
+    int hi = (int) Math.floor(seed * twoToTheMinus24) & 0xffffff;
+    int lo = (int) (seed - (hi * twoToThe24));
+    setSeed(hi, lo);
+  }
+
+  /**
+   * Construct a random generator with the given {@code seed} as the initial
+   * state.
+   * 
+   * @param seed the seed that will determine the initial state of this random
+   *          number generator.
+   * @see #setSeed
+   */
+  public Random(long seed) {
+    setSeed(seed);
+  }
+
+  /**
+   * Returns the next pseudo-random, uniformly distributed {@code boolean} value
+   * generated by this generator.
+   * 
+   * @return a pseudo-random, uniformly distributed boolean value.
+   */
+  public boolean nextBoolean() {
+    return nextInternal(1) != 0;
+  }
+
+  /**
+   * Modifies the {@code byte} array by a random sequence of {@code byte}s
+   * generated by this random number generator.
+   * 
+   * @param buf non-null array to contain the new random {@code byte}s.
+   * @see #next
+   */
+  public void nextBytes(byte[] buf) {
+    if (buf == null) {
+      throw new NullPointerException();
+    }
+
+    int rand = 0, count = 0, loop = 0;
+    while (count < buf.length) {
+      if (loop == 0) {
+        rand = (int) nextInternal(32);
+        loop = 3;
+      } else {
+        loop--;
+      }
+      buf[count++] = (byte) rand;
+      rand >>= 8;
+    }
+  }
+
+  /**
+   * Generates a normally distributed random {@code double} number between 0.0
+   * inclusively and 1.0 exclusively.
+   * 
+   * @return a random {@code double} in the range [0.0 - 1.0)
+   * @see #nextFloat
+   */
+  public double nextDouble() {
+    return nextInternal(26) * twoToTheMinus26 +
+      nextInternal(27) * twoToTheMinus53;
+  }
+
+  /**
+   * Generates a normally distributed random {@code float} number between 0.0
+   * inclusively and 1.0 exclusively.
+   * 
+   * @return float a random {@code float} number between [0.0 and 1.0)
+   * @see #nextDouble
+   */
+  public float nextFloat() {
+    return (float) (nextInternal(24) * twoToTheMinus24);
+  }
+
+  /**
+   * Pseudo-randomly generates (approximately) a normally distributed {@code
+   * double} value with mean 0.0 and a standard deviation value of {@code 1.0}
+   * using the <i>polar method<i> of G. E. P. Box, M. E. Muller, and G.
+   * Marsaglia, as described by Donald E. Knuth in <i>The Art of Computer
+   * Programming, Volume 2: Seminumerical Algorithms</i>, section 3.4.1,
+   * subsection C, algorithm P.
+   * 
+   * @return a random {@code double}
+   * @see #nextDouble
+   */
+  public synchronized double nextGaussian() {
+    if (haveNextNextGaussian) {
+      // if X1 has been returned, return the second Gaussian
+      haveNextNextGaussian = false;
+      return nextNextGaussian;
+    }
+    
+    double v1, v2, s;
+    do {
+      // Generates two independent random variables U1, U2
+      v1 = 2 * nextDouble() - 1;
+      v2 = 2 * nextDouble() - 1;
+      s = v1 * v1 + v2 * v2;
+    } while (s >= 1);
+
+    // See errata for TAOCP vol. 2, 3rd ed. for proper handling of s == 0 case
+    // (page 5 of http://www-cs-faculty.stanford.edu/~uno/err2.ps.gz)
+    double norm = (s == 0) ? 0.0 : Math.sqrt(-2.0 * Math.log(s) / s);
+    nextNextGaussian = v2 * norm;
+    haveNextNextGaussian = true;
+    return v1 * norm;
+  }
+
+  /**
+   * Generates a uniformly distributed 32-bit {@code int} value from the random
+   * number sequence.
+   * 
+   * @return a uniformly distributed {@code int} value.
+   * @see java.lang.Integer#MAX_VALUE
+   * @see java.lang.Integer#MIN_VALUE
+   * @see #next
+   * @see #nextLong
+   */
+  public int nextInt() {
+    return (int) nextInternal(32);
+  }
+
+  /**
+   * Returns a new pseudo-random {@code int} value which is uniformly
+   * distributed between 0 (inclusively) and the value of {@code n}
+   * (exclusively).
+   * 
+   * @param n the exclusive upper border of the range [0 - n).
+   * @return a random {@code int}.
+   */
+  public int nextInt(int n) {
+    if (n > 0) {
+      if ((n & -n) == n) {
+        return (int) ((n * nextInternal(31)) * twoToTheMinus31);
+      }
+      double bits, val;
+      do {
+        bits = nextInternal(31);
+        val = bits % n;
+      } while (bits - val + (n - 1) < 0);
+      return (int) val;
+    }
+    
+    throw new IllegalArgumentException();
+  }
+  
+  /**
+   * Generates a uniformly distributed 64-bit integer value from the random
+   * number sequence.
+   * 
+   * @return 64-bit random integer.
+   * @see java.lang.Integer#MAX_VALUE
+   * @see java.lang.Integer#MIN_VALUE
+   * @see #next
+   * @see #nextInt()
+   * @see #nextInt(int)
+   */
+  public long nextLong() {
+    return ((long) nextInternal(32) << 32) + (long) nextInternal(32);
+  }
+
+  /**
+   * Modifies the seed a using linear congruential formula presented in <i>The
+   * Art of Computer Programming, Volume 2</i>, Section 3.2.1.
+   * 
+   * @param seed the seed that alters the state of the random number generator.
+   * @see #next
+   * @see #Random()
+   * @see #Random(long)
+   */
+  public synchronized void setSeed(long seed) {
+    setSeed((int) ((seed >> 24) & 0xffffff), (int) (seed & 0xffffff));
+  }
+  
+  /**
+   * Returns a pseudo-random uniformly distributed {@code int} value of the
+   * number of bits specified by the argument {@code bits} as described by
+   * Donald E. Knuth in <i>The Art of Computer Programming, Volume 2:
+   * Seminumerical Algorithms</i>, section 3.2.1.
+   * 
+   * @param bits number of bits of the returned value.
+   * @return a pseudo-random generated int number.
+   * @see #nextBytes
+   * @see #nextDouble
+   * @see #nextFloat
+   * @see #nextInt()
+   * @see #nextInt(int)
+   * @see #nextGaussian
+   * @see #nextLong
+   */
+  protected synchronized int next(int bits) {
+    return (int) nextInternal(bits);
+  }
+  
+  private synchronized double nextInternal(int bits) {
+    double hi = seedhi * multiplierLo + seedlo * multiplierHi;
+    double lo = seedlo * multiplierLo + 0xb;
+    double carry = Math.floor(lo * twoToTheMinus24);
+    hi += carry;
+    lo -= carry * twoToThe24;
+    hi %= twoToThe24;
+    
+    seedhi = hi;
+    seedlo = lo;
+    
+    if (bits <= 24) {
+      // High bits of seedhi, shifted right
+      return Math.floor(seedhi * twoToTheXMinus24[bits]);
+    } else {
+      // All bits of seedhi, shifted left
+      double h = seedhi * (1 << (bits - 24));
+      // High bits of seedlo, shifted right
+      double l = Math.floor(seedlo * twoToTheXMinus48[bits]);
+      // Sum of high and low bits
+      double dval = h + l;
+      // Force sign based on bit 31
+      if (dval >= twoToThe31) {
+        dval -= twoToThe32;
+      }
+      return dval;
+    }
+  }
+  
+  // seedhi and seedlo should be integers from 0 to 2^24 - 1
+  private synchronized void setSeed(int seedhi, int seedlo) {
+    this.seedhi = seedhi ^ 0x5de;
+    this.seedlo = seedlo ^ 0xece66d;
+    haveNextNextGaussian = false;
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 34266e4..6e29683 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -46,6 +46,7 @@
 import com.google.gwt.emultest.java.util.LinkedHashMapTest;
 import com.google.gwt.emultest.java.util.LinkedListTest;
 import com.google.gwt.emultest.java.util.PriorityQueueTest;
+import com.google.gwt.emultest.java.util.RandomTest;
 import com.google.gwt.emultest.java.util.StackTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
 
@@ -90,6 +91,7 @@
     suite.addTestSuite(LinkedHashMapTest.class);
     suite.addTestSuite(LinkedListTest.class);
     suite.addTestSuite(PriorityQueueTest.class);
+    suite.addTestSuite(RandomTest.class);
     suite.addTestSuite(StackTest.class);
     suite.addTestSuite(SqlDateTest.class);
     suite.addTestSuite(SqlTimeTest.class);
diff --git a/user/test/com/google/gwt/emultest/java/util/RandomTest.java b/user/test/com/google/gwt/emultest/java/util/RandomTest.java
new file mode 100644
index 0000000..3b09580
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/RandomTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.Random;
+
+/**
+ * Tests for GWT's emulation of the JRE Random class.  The JRE specifies the
+ * exact algorithm used to generate the pseudorandom output.
+ */
+public class RandomTest extends GWTTestCase {
+
+  /**
+   * Sets module name so that javascript compiler can operate.
+   */
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+  
+  public void testNextBytes() {
+    Random r = new Random(1);
+    byte[] b = new byte[5];
+    r.nextBytes(b);
+    assertEquals((byte) 115, b[0]);
+    assertEquals((byte) -43, b[1]);
+    assertEquals((byte) 26, b[2]);
+    assertEquals((byte) -69, b[3]);
+    assertEquals((byte) -40, b[4]);
+    
+    try {
+      r.nextBytes(null);
+      fail("Expected NullPointerException");
+    } catch (NullPointerException e) {
+    }
+  }
+  
+  public void testNextDouble() {
+    Random r = new Random(1);
+    assertEquals(0.7308781907032909, r.nextDouble());
+    assertEquals(0.41008081149220166, r.nextDouble());
+    assertEquals(0.20771484130971707, r.nextDouble());
+    assertEquals(0.3327170559595112, r.nextDouble());
+    assertEquals(0.9677559094241207, r.nextDouble());
+  }
+  
+  public void testNextFloat() {
+    Random r = new Random(1);
+    assertEquals(0.7308782f, r.nextFloat());
+    assertEquals(0.100473166f, r.nextFloat());
+    assertEquals(0.4100808f, r.nextFloat());
+    assertEquals(0.40743977f, r.nextFloat());
+    assertEquals(0.2077148f, r.nextFloat());
+  }
+  
+  public void testNextGaussian() {
+    Random r = new Random(1);
+    assertEquals(1.561581040188955, r.nextGaussian());
+    assertEquals(-0.6081826070068602, r.nextGaussian());
+    assertEquals(-1.0912278829447088, r.nextGaussian());
+    assertEquals(-0.6245401364066232, r.nextGaussian());
+    assertEquals(-1.1182832102556484, r.nextGaussian());
+  }
+  
+  public void testNextInt() {
+    Random r = new Random(1);
+    assertEquals(-1155869325, r.nextInt());
+    assertEquals(431529176, r.nextInt());
+    assertEquals(1761283695, r.nextInt());
+    assertEquals(1749940626, r.nextInt());
+    assertEquals(892128508, r.nextInt());
+    
+    try {
+      r.nextInt(0);
+      fail("Expected IlledgalArgumentException");
+    } catch (IllegalArgumentException e) {
+    }
+    
+    try {
+      r.nextInt(-1);
+      fail("Expected IlledgalArgumentException");
+    } catch (IllegalArgumentException e) {
+    }
+  }
+  
+  public void testNextInt100() {
+    Random r = new Random(1);
+    assertEquals(85, r.nextInt(100));
+    assertEquals(88, r.nextInt(100));
+    assertEquals(47, r.nextInt(100));
+    assertEquals(13, r.nextInt(100));
+    assertEquals(54, r.nextInt(100));
+  }  
+  
+  public void testNextInt128() {
+    Random r = new Random(1);
+    assertEquals(93, r.nextInt(128));
+    assertEquals(12, r.nextInt(128));
+    assertEquals(52, r.nextInt(128));
+    assertEquals(52, r.nextInt(128));
+    assertEquals(26, r.nextInt(128));
+  }
+  
+  public void testNextLong() {
+    Random r = new Random(1);
+    assertEquals(-4964420948893066024L, r.nextLong());
+    assertEquals(7564655870752979346L, r.nextLong());
+    assertEquals(3831662765844904176L, r.nextLong());
+    assertEquals(6137546356583794141L, r.nextLong());
+    assertEquals(-594798593157429144L, r.nextLong());
+  }
+  
+  public void testSetSeed() {
+    Random r = new Random();
+    
+    r.setSeed(1);
+    byte[] b = new byte[1];
+    r.nextBytes(b);
+    assertEquals((byte) 115, b[0]);
+    
+    r.setSeed(1);
+    assertEquals(0.7308781907032909, r.nextDouble());
+    
+    r.setSeed(1);
+    assertEquals(0.7308782f, r.nextFloat());
+    
+    r.setSeed(1);
+    assertEquals(1.561581040188955, r.nextGaussian());
+    
+    r.setSeed(1);
+    assertEquals(-1155869325, r.nextInt());
+    
+    r.setSeed(1);
+    assertEquals(85, r.nextInt(100));
+    
+    r.setSeed(1);
+    assertEquals(93, r.nextInt(128));
+    
+    r.setSeed(1);
+    assertEquals(-4964420948893066024L, r.nextLong());
+  }
+}