Faster version of LongLib

Review at http://gwt-code-reviews.appspot.com/572801

Review by: jat@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8235 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
index 0e4a148..f848152 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
 import com.google.gwt.dev.jjs.ast.JCharLiteral;
@@ -25,8 +26,11 @@
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.js.ast.JsArrayLiteral;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.lang.LongLib;
 
@@ -40,10 +44,6 @@
  */
 public class GenerateJavaScriptLiterals extends JVisitor {
 
-  static {
-    LongLib.RUN_IN_JVM = true;
-  }
-
   private final JsProgram program;
   private final Stack<JsVisitable<?>> nodeStack = new Stack<JsVisitable<?>>();
 
@@ -78,13 +78,20 @@
 
   @Override
   public void endVisit(JLongLiteral x, Context ctx) {
-    JsArrayLiteral arrayLit = new JsArrayLiteral(x.getSourceInfo());
-    double[] doubleArray = LongLib.typeChange(x.getValue());
-    arrayLit.getExpressions().add(
-        program.getNumberLiteral(x.getSourceInfo(), doubleArray[0]));
-    arrayLit.getExpressions().add(
-        program.getNumberLiteral(x.getSourceInfo(), doubleArray[1]));
-    push(arrayLit);
+    SourceInfo sourceInfo = x.getSourceInfo();
+    int[] intArray = LongLib.getAsIntArray(x.getValue());
+    JsObjectLiteral objectLit = new JsObjectLiteral(sourceInfo);
+    List<JsPropertyInitializer> inits = objectLit.getPropertyInitializers();
+    JsExpression label0 = new JsNameRef(sourceInfo, "l");
+    JsExpression label1 = new JsNameRef(sourceInfo, "m");
+    JsExpression label2 = new JsNameRef(sourceInfo, "h");
+    JsExpression value0 = program.getNumberLiteral(sourceInfo, intArray[0]);
+    JsExpression value1 = program.getNumberLiteral(sourceInfo, intArray[1]);
+    JsExpression value2 = program.getNumberLiteral(sourceInfo, intArray[2]);
+    inits.add(new JsPropertyInitializer(sourceInfo, label0, value0));
+    inits.add(new JsPropertyInitializer(sourceInfo, label1, value1));
+    inits.add(new JsPropertyInitializer(sourceInfo, label2, value2));
+    push(objectLit);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
index 59a624f..1d30097 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
@@ -44,6 +44,8 @@
  * Detects when same literal is passed as parameter value, and uses literal
  * instead of parameter. The unused parameter will be removed by other analyses.
  */
+// TODO: this optimization can mistakenly act on methods such as LongLib.fromInt
+// since only one call is seen in LongLib itself.
 public class SameParameterValueOptimizer {
   /**
    * Fill parameterValues map.
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 4e907dc..02ddedd 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -209,7 +209,8 @@
   /**
    * Creates a primitive JSON array of a given seedType.
    * 
-   * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
+   * @param seedType the primitive type of the array; 0: null; 1: zero;
+   *     2: false; 3: (long) 0
    * @param length the requested length
    * @see #NULL_SEED_TYPE
    * @see #ZERO_SEED_TYPE
@@ -218,8 +219,15 @@
    */
   private static native Array createFromSeed(int seedType, int length) /*-{
     var array = new Array(length);
-    if (seedType > 0) {
-      var value = [null, 0, false, [0, 0]][seedType];
+    if (seedType == 3) {
+      // Fill array with the type used by LongLib
+      for (var i = 0; i < length; ++i) {
+        var value = new Object();
+        value.l = value.m = value.h = 0;
+        array[i] = value;
+      }
+    } else if (seedType > 0) {
+      var value = [null, 0, false][seedType];
       for (var i = 0; i < length; ++i) {
         array[i] = value;
       }
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
index 9226113..1afcfa8 100644
--- a/dev/core/super/com/google/gwt/lang/LongLib.java
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -15,687 +15,439 @@
  */
 package com.google.gwt.lang;
 
-import static com.google.gwt.lang.LongLib.Const.LN_2;
-import static com.google.gwt.lang.LongLib.Const.MAX_VALUE;
-import static com.google.gwt.lang.LongLib.Const.MIN_VALUE;
-import static com.google.gwt.lang.LongLib.Const.NEG_ONE;
-import static com.google.gwt.lang.LongLib.Const.ONE;
-import static com.google.gwt.lang.LongLib.Const.TWO;
-import static com.google.gwt.lang.LongLib.Const.TWO_PWR_24;
-import static com.google.gwt.lang.LongLib.Const.ZERO;
-
-import com.google.gwt.core.client.UnsafeNativeLong;
-
 /**
  * Implements a Java <code>long</code> in a way that can be translated to
  * JavaScript.
  */
-public class LongLib {
-  /*
-   * Implementation: An array of two doubles, low and high, such that high+low
-   * is mathematically equivalent to the original integer. Since a JavaScript
-   * Number does not hold enough bits to precisely calculate high+low, all
-   * operations must be implemented carefully. "low" is always between 0 and
-   * 2^32-1 inclusive. "high" is always between -2^63 and 2^63-2^32 inclusive
-   * and is a multiple of 2^32. The sign of the number is determined entirely by
-   * "high". Since low is always positive, small negative numbers are encoded
-   * with high=-2^32. For example, -1 is encoded as { high=-2^32, low=2^32-1 }.
-   * 
-   * Note that this class must be careful using type "long". Being the
-   * implementation of the long type for web mode, any place it uses a long is
-   * not usable in web mode. There are currently two such methods:
-   * {@link #toLong(double[])} and {@link #make(long)}.
-   * 
-   * The GWT RPC serialization code is dependent on the internal format of the
-   * long type; any changes made to this class should be reflected in the
-   * implementations of SerializationStreamReader and Writer.
-   */
-
-  /**
-   * Use nested class to avoid clinit on outer.
-   */
-  static class CachedInts {
-    // Between -128 and 127.
-    static double[][] boxedValues = new double[256][];
-  }
+public class LongLib extends LongLibBase {
 
   static class Const {
-    static final double LN_2 = Math.log(2);
-    static final double[] MAX_VALUE = typeChange(Long.MAX_VALUE);
-    static final double[] MIN_VALUE = typeChange(Long.MIN_VALUE);
-    static final double[] NEG_ONE = fromInt(-1);
-    static final double[] ONE = fromInt(1);
-    static final double[] TWO = fromInt(2);
+    static final LongEmul MAX_VALUE = create(MASK, MASK, MASK_2 >> 1);
+    static final LongEmul MIN_VALUE = create(0, 0, SIGN_BIT_VALUE);
+    static final LongEmul ONE = fromInt(1);
+    static final LongEmul TWO = fromInt(2);
+    static final LongEmul ZERO = fromInt(0);
+  }
+  
+  private static LongEmul[] boxedValues;
+  
+  public static LongEmul add(LongEmul a, LongEmul b) {
+    int sum0 = getL(a) + getL(b);
+    int sum1 = getM(a) + getM(b) + (sum0 >> BITS);
+    int sum2 = getH(a) + getH(b) + (sum1 >> BITS);
 
-    /**
-     * Half of the number of bits we expect to be precise.
-     * 
-     * @see LongLib#PRECISION_BITS
-     */
-    static final double[] TWO_PWR_24 = typeChange(0x1000000L);
+    return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
+  }
 
-    static final double[] ZERO = fromInt(0);
+  public static LongEmul and(LongEmul a, LongEmul b) {
+    return create(getL(a) & getL(b), getM(a) & getM(b), getH(a) & getH(b));
   }
 
   /**
-   * Set this to false before calling any methods when using this class outside
-   * of GWT!
-   */
-  public static boolean RUN_IN_JVM = false;
-
-  /**
-   * Number of bits we expect to be accurate for a double representing a large
-   * integer.
-   */
-  private static final int PRECISION_BITS = 48;
-
-  /**
-   * Index of the high bits in a 2-double array.
-   */
-  private static final int HIGH = 1;
-  private static final double HIGH_MAX = 9223372032559808512d;
-  private static final double HIGH_MIN = -9223372036854775808d;
-
-  /**
-   * Index of the low bits in a 2-double array.
-   */
-  private static final int LOW = 0;
-  private static final double LOW_MAX = 4294967295d;
-  private static final double LOW_MIN = 0;
-
-  private static final double TWO_PWR_15_DBL = 0x8000;
-  private static final double TWO_PWR_16_DBL = 0x10000;
-  private static final double TWO_PWR_31_DBL = TWO_PWR_16_DBL * TWO_PWR_15_DBL;
-  private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
-  private static final double TWO_PWR_48_DBL = TWO_PWR_32_DBL * TWO_PWR_16_DBL;
-  private static final double TWO_PWR_63_DBL = TWO_PWR_32_DBL * TWO_PWR_31_DBL;
-  private static final double TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
-
-  public static double[] add(double[] a, double[] b) {
-    double newHigh = a[HIGH] + b[HIGH];
-    double newLow = a[LOW] + b[LOW];
-    return create(newLow, newHigh);
-  }
-
-  public static double[] and(double[] a, double[] b) {
-    return makeFromBits(highBits(a) & highBits(b), lowBits(a) & lowBits(b));
-  }
-
-  /**
-   * Compare the receiver to the argument.
+   * Compare the receiver a to the argument b.
    * 
-   * @return 0 if they are the same, 1 if the receiver is greater, -1 if the
-   *         argument is greater.
+   * @return 0 if they are the same, a positive value if the receiver is
+   * greater, or a negative value if the argument is greater.
    */
-  public static int compare(double[] a, double[] b) {
-    if (eq(a, b)) {
-      return 0;
+  public static int compare(LongEmul a, LongEmul b) {
+    int signA = sign(a);
+    int signB = sign(b);
+    if (signA != signB) {
+      return signB - signA;
     }
 
-    boolean nega = isNegative(a);
-    boolean negb = isNegative(b);
-    if (nega && !negb) {
-      return -1;
-    }
-    if (!nega && negb) {
-      return 1;
+    int a2 = getH(a);
+    int b2 = getH(b);
+    if (a2 != b2) {
+      return a2 - b2;
     }
 
-    // at this point, the signs are the same, so subtraction will not overflow
-    assert (nega == negb);
-    if (isNegative(sub(a, b))) {
-      return -1;
-    } else {
-      return 1;
+    int a1 = getM(a);
+    int b1 = getM(b);
+    if (a1 != b1) {
+      return a1 - b1;
     }
+
+    int a0 = getL(a);
+    int b0 = getL(b);
+    return a0 - b0;
   }
 
-  public static double[] div(double[] a, double[] b) {
-    if (isZero(b)) {
-      throw new ArithmeticException("/ by zero");
+  public static LongEmul div(LongEmul a, LongEmul b) {
+    return divMod(a, b, false);
+  }
+  
+  public static boolean eq(LongEmul a, LongEmul b) {
+    return getL(a) == getL(b) && getM(a) == getM(b) && getH(a) == getH(b);
+  }
+
+  public static LongEmul fromDouble(double value) {
+    if (Double.isNaN(value)) {
+      return Const.ZERO;
     }
-    if (isZero(a)) {
-      return ZERO;
+    if (value < -TWO_PWR_63_DBL) {
+      return Const.MIN_VALUE;
+    }
+    if (value >= TWO_PWR_63_DBL) {
+      return Const.MAX_VALUE;
     }
 
-    if (eq(a, MIN_VALUE)) {
-      // handle a==MIN_VALUE carefully because of overflow issues
-      if (eq(b, ONE) || eq(b, NEG_ONE)) {
-        // this strange exception is described in JLS3 17.17.2
-        return MIN_VALUE;
-      }
-      // at this point, abs(b) >= 2, so |a/b| < -MIN_VALUE
-      double[] halfa = shr(a, 1);
-      double[] approx = shl(div(halfa, b), 1);
-      double[] rem = sub(a, mul(b, approx));
-      assert gt(rem, MIN_VALUE);
-      return add(approx, div(rem, b));
+    boolean negative = false;
+    if (value < 0) {
+      negative = true;
+      value = -value;
     }
-
-    if (eq(b, MIN_VALUE)) {
-      assert !eq(a, MIN_VALUE);
-      return ZERO;
+    int a2 = 0;
+    if (value >= TWO_PWR_44_DBL) {
+      a2 = (int) (value / TWO_PWR_44_DBL);
+      value -= a2 * TWO_PWR_44_DBL;
     }
-
-    // To keep the implementation compact, make a and be
-    // both be positive and swap the sign of the result
-    // if necessary.
-    if (isNegative(a)) {
-      if (isNegative(b)) {
-        return div(neg(a), neg(b));
-      } else {
-        return neg(div(neg(a), b));
-      }
+    int a1 = 0;
+    if (value >= TWO_PWR_22_DBL) {
+      a1 = (int) (value / TWO_PWR_22_DBL);
+      value -= a1 * TWO_PWR_22_DBL;
     }
-    assert (!isNegative(a));
-    if (isNegative(b)) {
-      return neg(div(a, neg(b)));
+    int a0 = (int) value;
+    LongEmul result = create(a0, a1, a2);
+    if (negative) {
+      negate(result);
     }
-    assert (!isNegative(b));
-
-    // Use float division to approximate the answer.
-    // Repeat until the remainder is less than b.
-    double[] result = ZERO;
-    double[] rem = a;
-    while (gte(rem, b)) {
-      // approximate using float division
-      double[] deltaResult = fromDouble(Math.floor(toDoubleRoundDown(rem)
-          / toDoubleRoundUp(b)));
-      if (isZero(deltaResult)) {
-        deltaResult = Const.ONE;
-      }
-      double[] deltaRem = mul(deltaResult, b);
-
-      assert gte(deltaResult, ONE);
-      assert lte(deltaRem, rem);
-      result = add(result, deltaResult);
-      rem = sub(rem, deltaRem);
-    }
-
     return result;
   }
 
-  public static boolean eq(double[] a, double[] b) {
-    return ((a[LOW] == b[LOW]) && (a[HIGH] == b[HIGH]));
-  }
-
-  public static double[] fromDouble(double value) {
-    if (Double.isNaN(value)) {
-      return ZERO;
-    }
-    if (value < -TWO_PWR_63_DBL) {
-      return MIN_VALUE;
-    }
-    if (value >= TWO_PWR_63_DBL) {
-      return MAX_VALUE;
-    }
-    if (value > 0) {
-      return create(Math.floor(value), 0.0);
-    } else {
-      return create(Math.ceil(value), 0.0);
-    }
-  }
-
-  public static double[] fromInt(int value) {
+  public static LongEmul fromInt(int value) {
     if (value > -129 && value < 128) {
       int rebase = value + 128;
-      double[] result = CachedInts.boxedValues[rebase];
+      if (boxedValues == null) {
+        boxedValues = new LongEmul[256];
+      }
+      LongEmul result = boxedValues[rebase];
       if (result == null) {
-        result = CachedInts.boxedValues[rebase] = internalFromInt(value);
+        result = boxedValues[rebase] = create(value);
       }
       return result;
     }
-    return internalFromInt(value);
+
+    return create(value);
   }
-
-  public static boolean gt(double[] a, double[] b) {
-    return compare(a, b) > 0;
+  
+  /**
+   * Return a triple of ints { low, middle, high } that concatenate bitwise to
+   * the given number.
+   */
+  public static int[] getAsIntArray(long l) {
+    int[] a = new int[3];
+    a[0] = (int) (l & MASK);
+    a[1] = (int) ((l >> BITS) & MASK);
+    a[2] = (int) ((l >> BITS01) & MASK_2);
+    return a;
   }
-
-  public static boolean gte(double[] a, double[] b) {
-    return compare(a, b) >= 0;
-  }
-
-  public static boolean lt(double[] a, double[] b) {
-    return compare(a, b) < 0;
-  }
-
-  public static boolean lte(double[] a, double[] b) {
-    return compare(a, b) <= 0;
-  }
-
-  public static double[] mod(double[] a, double[] b) {
-    return sub(a, mul(div(a, b), b));
-  }
-
-  public static double[] mul(double[] a, double[] b) {
-    if (isZero(a)) {
-      return ZERO;
-    }
-    if (isZero(b)) {
-      return ZERO;
-    }
-
-    // handle MIN_VALUE carefully, because neg(MIN_VALUE)==MIN_VALUE
-    if (eq(a, MIN_VALUE)) {
-      return multByMinValue(b);
-    }
-    if (eq(b, MIN_VALUE)) {
-      return multByMinValue(a);
-    }
-
-    // If either argument is negative, change it to positive, multiply,
-    // and then negate the result.
-    if (isNegative(a)) {
-      if (isNegative(b)) {
-        return mul(neg(a), neg(b));
+  
+  public static boolean gt(LongEmul a, LongEmul b) {
+    int signa = getH(a) >> (BITS2 - 1);
+    int signb = getH(b) >> (BITS2 - 1);
+    if (signa == 0) {
+      if (signb != 0 || getH(a) > getH(b)
+          || (getH(a) == getH(b) && getM(a) > getM(b))
+          || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) > getL(b))) {
+        return true;
       } else {
-        return neg(mul(neg(a), b));
+        return false;
+      }
+    } else {
+      if (signb == 0 || getH(a) < getH(b)
+          || (getH(a) == getH(b) && getM(a) < getM(b))
+          || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) <= getL(b))) {
+        return false;
+      } else {
+        return true;
       }
     }
-    assert (!isNegative(a));
-    if (isNegative(b)) {
-      return neg(mul(a, neg(b)));
-    }
-    assert (!isNegative(b));
-
-    // If both numbers are small, use float multiplication
-    if (lt(a, TWO_PWR_24) && lt(b, TWO_PWR_24)) {
-      return create(toDouble(a) * toDouble(b), 0.0);
-    }
-
-    // Divide each number into 4 chunks of 16 bits, and then add
-    // up 4x4 multiplies. Skip the six multiplies where the result
-    // mod 2^64 would be 0.
-    double a3 = a[HIGH] % TWO_PWR_48_DBL;
-    double a4 = a[HIGH] - a3;
-    double a1 = a[LOW] % TWO_PWR_16_DBL;
-    double a2 = a[LOW] - a1;
-
-    double b3 = b[HIGH] % TWO_PWR_48_DBL;
-    double b4 = b[HIGH] - b3;
-    double b1 = b[LOW] % TWO_PWR_16_DBL;
-    double b2 = b[LOW] - b1;
-
-    double[] res = ZERO;
-
-    res = addTimes(res, a4, b1);
-    res = addTimes(res, a3, b2);
-    res = addTimes(res, a3, b1);
-    res = addTimes(res, a2, b3);
-    res = addTimes(res, a2, b2);
-    res = addTimes(res, a2, b1);
-    res = addTimes(res, a1, b4);
-    res = addTimes(res, a1, b3);
-    res = addTimes(res, a1, b2);
-    res = addTimes(res, a1, b1);
-
-    return res;
   }
 
-  public static double[] neg(double[] a) {
-    if (eq(a, MIN_VALUE)) {
-      return MIN_VALUE;
-    }
-    double newHigh = -a[HIGH];
-    double newLow = -a[LOW];
-    if (newLow > LOW_MAX) {
-      newLow -= TWO_PWR_32_DBL;
-      newHigh += TWO_PWR_32_DBL;
-    }
-    if (newLow < LOW_MIN) {
-      newLow += TWO_PWR_32_DBL;
-      newHigh -= TWO_PWR_32_DBL;
-    }
-    return createNormalized(newLow, newHigh);
-  }
-
-  public static boolean neq(double[] a, double[] b) {
-    return ((a[LOW] != b[LOW]) || (a[HIGH] != b[HIGH]));
-  }
-
-  public static double[] not(double[] a) {
-    return makeFromBits(~highBits(a), ~lowBits(a));
-  }
-
-  public static double[] or(double[] a, double[] b) {
-    return makeFromBits(highBits(a) | highBits(b), lowBits(a) | lowBits(b));
-  }
-
-  public static double[] shl(double[] a, int n) {
-    n &= 63;
-
-    if (eq(a, MIN_VALUE)) {
-      if (n == 0) {
-        return a;
+  public static boolean gte(LongEmul a, LongEmul b) {
+    int signa = getH(a) >> (BITS2 - 1);
+    int signb = getH(b) >> (BITS2 - 1);
+    if (signa == 0) {
+      if (signb != 0 || getH(a) > getH(b)
+          || (getH(a) == getH(b) && getM(a) > getM(b))
+          || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) >= getL(b))) {
+        return true;
       } else {
-        return ZERO;
+        return false;
+      }
+    } else {
+      if (signb == 0 || getH(a) < getH(b)
+          || (getH(a) == getH(b) && getM(a) < getM(b))
+          || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) < getL(b))) {
+        return false;
+      } else {
+        return true;
       }
     }
-
-    if (isNegative(a)) {
-      return neg(shl(neg(a), n));
-    }
-
-    final double twoToN = pwrAsDouble(n);
-
-    double newHigh = a[HIGH] * twoToN % TWO_PWR_64_DBL;
-    double newLow = a[LOW] * twoToN;
-    double diff = newLow - (newLow % TWO_PWR_32_DBL);
-    newHigh += diff;
-    newLow -= diff;
-    if (newHigh >= TWO_PWR_63_DBL) {
-      newHigh -= TWO_PWR_64_DBL;
-    }
-
-    return createNormalized(newLow, newHigh);
   }
 
-  public static double[] shr(double[] a, int n) {
+  public static boolean lt(LongEmul a, LongEmul b) {
+    return !gte(a, b);
+  }
+
+  public static boolean lte(LongEmul a, LongEmul b) {
+    return !gt(a, b);
+  }
+
+  public static LongEmul mod(LongEmul a, LongEmul b) {
+    divMod(a, b, true);
+    return remainder;
+  }
+
+  // Assumes BITS == 22
+  public static LongEmul mul(LongEmul a, LongEmul b) {
+    // Grab 13-bit chunks
+    int a0 = getL(a) & 0x1fff;
+    int a1 = (getL(a) >> 13) | ((getM(a) & 0xf) << 9);
+    int a2 = (getM(a) >> 4) & 0x1fff;
+    int a3 = (getM(a) >> 17) | ((getH(a) & 0xff) << 5);
+    int a4 = (getH(a) & 0xfff00) >> 8;
+
+    int b0 = getL(b) & 0x1fff;
+    int b1 = (getL(b) >> 13) | ((getM(b) & 0xf) << 9);
+    int b2 = (getM(b) >> 4) & 0x1fff;
+    int b3 = (getM(b) >> 17) | ((getH(b) & 0xff) << 5);
+    int b4 = (getH(b) & 0xfff00) >> 8;
+
+    // Compute partial products
+    // Optimization: if b is small, avoid multiplying by parts that are 0
+    int p0 = a0 * b0; // << 0
+    int p1 = a1 * b0; // << 13
+    int p2 = a2 * b0; // << 26
+    int p3 = a3 * b0; // << 39
+    int p4 = a4 * b0; // << 52
+    
+    if (b1 != 0) {
+      p1 += a0 * b1;
+      p2 += a1 * b1;
+      p3 += a2 * b1;
+      p4 += a3 * b1;
+    }
+    if (b2 != 0) {
+      p2 += a0 * b2;
+      p3 += a1 * b2;
+      p4 += a2 * b2;
+    }
+    if (b3 != 0) {
+      p3 += a0 * b3;
+      p4 += a1 * b3;
+    }
+    if (b4 != 0) {
+      p4 += a0 * b4;
+    }
+
+    // Accumulate into 22-bit chunks:
+    // .........................................c10|...................c00|
+    // |....................|..................xxxx|xxxxxxxxxxxxxxxxxxxxxx| p0
+    // |....................|......................|......................|
+    // |....................|...................c11|......c01.............|
+    // |....................|....xxxxxxxxxxxxxxxxxx|xxxxxxxxx.............| p1
+    // |....................|......................|......................|
+    // |.................c22|...............c12....|......................|
+    // |..........xxxxxxxxxx|xxxxxxxxxxxxxxxxxx....|......................| p2
+    // |....................|......................|......................|
+    // |.................c23|..c13.................|......................|
+    // |xxxxxxxxxxxxxxxxxxxx|xxxxx.................|......................| p3
+    // |....................|......................|......................|
+    // |.........c24........|......................|......................|
+    // |xxxxxxxxxxxx........|......................|......................| p4
+
+    int c00 = p0 & 0x3fffff;
+    int c01 = (p1 & 0x1ff) << 13;
+    int c0 = c00 + c01;
+
+    int c10 = p0 >> 22;
+    int c11 = p1 >> 9;
+    int c12 = (p2 & 0x3ffff) << 4;
+    int c13 = (p3 & 0x1f) << 17;
+    int c1 = c10 + c11 + c12 + c13;
+
+    int c22 = p2 >> 18;
+    int c23 = p3 >> 5;
+    int c24 = (p4 & 0xfff) << 8;
+    int c2 = c22 + c23 + c24;
+
+    // Propagate high bits from c0 -> c1, c1 -> c2
+    c1 += c0 >> BITS;
+    c0 &= MASK;
+    c2 += c1 >> BITS;
+    c1 &= MASK;
+    c2 &= MASK_2;
+
+    return create(c0, c1, c2);
+  }
+
+  public static LongEmul neg(LongEmul a) {
+    int neg0 = (~getL(a) + 1) & MASK;
+    int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK;
+    int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2;
+
+    return create(neg0, neg1, neg2);
+  }
+
+  public static boolean neq(LongEmul a, LongEmul b) {
+    return getL(a) != getL(b) || getM(a) != getM(b) || getH(a) != getH(b);
+  }
+
+  public static LongEmul not(LongEmul a) {
+    return create((~getL(a)) & MASK, (~getM(a)) & MASK, (~getH(a)) & MASK_2);
+  }
+
+  public static LongEmul or(LongEmul a, LongEmul b) {
+    return create(getL(a) | getL(b), getM(a) | getM(b), getH(a) | getH(b));
+  }
+
+  public static LongEmul shl(LongEmul a, int n) {
     n &= 63;
-    double shiftFact = pwrAsDouble(n);
-    double newHigh = Math.floor(a[HIGH] / shiftFact);
-    double newLow = Math.floor(a[LOW] / shiftFact);
 
-    /*
-     * Doing the above floors separately on each component is safe. If n<32,
-     * a[HIGH]/shiftFact is guaranteed to be an integer already. For n>32,
-     * a[HIGH]/shiftFact will have fractional bits, but we need to discard them
-     * as they shift away. We will end up discarding all of a[LOW] in this case,
-     * as it divides out to entirely fractional.
-     */
+    int res0, res1, res2;
+    if (n < BITS) {
+      res0 = getL(a) << n;
+      res1 = (getM(a) << n) | (getL(a) >> (BITS - n));
+      res2 = (getH(a) << n) | (getM(a) >> (BITS - n));
+    } else if (n < BITS01) {
+      res0 = 0;
+      res1 = getL(a) << (n - BITS);
+      res2 = (getM(a) << (n - BITS)) | (getL(a) >> (BITS01 - n));
+    } else {
+      res0 = 0;
+      res1 = 0;
+      res2 = getL(a) << (n - BITS01);
+    }
 
-    return create(newLow, newHigh);
+    return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
+  }
+
+  public static LongEmul shr(LongEmul a, int n) {
+    n &= 63;
+
+    int res0, res1, res2;
+
+    // Sign extend h(a)
+    int a2 = getH(a);
+    boolean negative = (a2 & SIGN_BIT_VALUE) != 0;
+    if (negative) {
+      a2 |= ~MASK_2;
+    }
+
+    if (n < BITS) {
+      res2 = a2 >> n;
+      res1 = (getM(a) >> n) | (a2 << (BITS - n));
+      res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
+    } else if (n < BITS01) {
+      res2 = negative ? MASK_2 : 0;
+      res1 = a2 >> (n - BITS);
+      res0 = (getM(a) >> (n - BITS)) | (a2 << (BITS01 - n));
+    } else {
+      res2 = negative ? MASK_2 : 0;
+      res1 = negative ? MASK : 0;
+      res0 = a2 >> (n - BITS01);
+    }
+
+    return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
   }
 
   /**
    * Logical right shift. It does not preserve the sign of the input.
    */
-  public static double[] shru(double[] a, int n) {
+  public static LongEmul shru(LongEmul a, int n) {
     n &= 63;
-    double[] sr = shr(a, n);
-    if (isNegative(a)) {
-      // the following changes the high bits to 0, using
-      // a formula from JLS3 section 15.19
-      sr = add(sr, shl(TWO, 63 - n));
+
+    int res0, res1, res2;
+    int a2 = getH(a) & MASK_2;
+    if (n < BITS) {
+      res2 = a2 >>> n;
+      res1 = (getM(a) >> n) | (a2 << (BITS - n));
+      res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
+    } else if (n < BITS01) {
+      res2 = 0;
+      res1 = a2 >>> (n - BITS);
+      res0 = (getM(a) >> (n - BITS)) | (getH(a) << (BITS01 - n));
+    } else {
+      res2 = 0;
+      res1 = 0;
+      res0 = a2 >>> (n - BITS01);
     }
 
-    return sr;
+    return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
   }
 
-  public static double[] sub(double[] a, double[] b) {
-    double newHigh = a[HIGH] - b[HIGH];
-    double newLow = a[LOW] - b[LOW];
-    return create(newLow, newHigh);
+  public static LongEmul sub(LongEmul a, LongEmul b) {
+    int sum0 = getL(a) - getL(b);
+    int sum1 = getM(a) - getM(b) + (sum0 >> BITS);
+    int sum2 = getH(a) - getH(b) + (sum1 >> BITS);
+
+    return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
   }
 
-  /**
-   * Cast from long to double or float.
-   */
-  public static double toDouble(double[] a) {
-    return a[HIGH] + a[LOW];
+  public static double toDouble(LongEmul a) {
+    if (LongLib.eq(a, Const.MIN_VALUE)) {
+      return -9223372036854775808.0;
+    }
+    if (LongLib.lt(a, Const.ZERO)) {
+      return -toDoubleHelper(LongLib.neg(a));
+    }
+    return toDoubleHelper(a);
   }
-
-  /**
-   * Cast from long to int.
-   */
-  public static int toInt(double[] a) {
-    return lowBits(a);
+  
+  // Assumes Integer.MIN_VALUE <= a <= Integer.MAX_VALUE
+  public static int toInt(LongEmul a) {
+    return getL(a) | (getM(a) << BITS);
   }
-
-  /**
-   * Implicit conversion from long to String.
-   */
-  public static String toString(double[] a) {
-    if (isZero(a)) {
+  
+  public static String toString(LongEmul a) {
+    if (LongLibBase.isZero(a)) {
       return "0";
     }
-
-    if (eq(a, MIN_VALUE)) {
-      // Special-case MIN_VALUE because neg(MIN_VALUE)==MIN_VALUE
+  
+    if (LongLibBase.isMinValue(a)) {
+      // Special-case MIN_VALUE because neg(MIN_VALUE) == MIN_VALUE
       return "-9223372036854775808";
     }
-
-    if (isNegative(a)) {
+  
+    if (LongLibBase.isNegative(a)) {
       return "-" + toString(neg(a));
     }
-
-    double[] rem = a;
+  
+    LongEmul rem = a;
     String res = "";
-
-    while (!isZero(rem)) {
+  
+    while (!LongLibBase.isZero(rem)) {
       // Do several digits each time through the loop, so as to
       // minimize the calls to the very expensive emulated div.
       final int tenPowerZeroes = 9;
       final int tenPower = 1000000000;
-      double[] tenPowerLong = fromInt(tenPower);
-
-      final double[] remDivTenPower = div(rem, tenPowerLong);
-      String digits = "" + toInt(sub(rem, mul(remDivTenPower, tenPowerLong)));
-      rem = remDivTenPower;
-
-      if (!isZero(rem)) {
+      LongEmul tenPowerLong = fromInt(tenPower);
+  
+      rem = LongLibBase.divMod(rem, tenPowerLong, true);
+      String digits = "" + toInt(LongLibBase.remainder);
+  
+      if (!LongLibBase.isZero(rem)) {
         int zeroesNeeded = tenPowerZeroes - digits.length();
         for (; zeroesNeeded > 0; zeroesNeeded--) {
           digits = "0" + digits;
         }
       }
-
+  
       res = digits + res;
     }
-
+  
     return res;
   }
-
-  public static double[] typeChange(long value) {
-    if (RUN_IN_JVM) {
-      return makeFromBits((int) (value >> 32), (int) value);
-    } else {
-      return typeChange0(value);
-    }
+  
+  public static LongEmul xor(LongEmul a, LongEmul b) {
+    return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b));
   }
-
-  public static double[] xor(double[] a, double[] b) {
-    return makeFromBits(highBits(a) ^ highBits(b), lowBits(a) ^ lowBits(b));
-  }
-
-  static long toLong(double[] a) {
-    return (long) a[HIGH] + (long) a[LOW];
-  }
-
-  private static double[] addTimes(double[] accum, double a, double b) {
-    if (a == 0.0) {
-      return accum;
-    }
-    if (b == 0.0) {
-      return accum;
-    }
-    return add(accum, create(a * b, 0.0));
-  }
-
-  /*
-   * Make a new instance equal to valueLow+valueHigh. The arguments do not need
-   * to be normalized, though by convention valueHigh and valueLow will hold the
-   * high and low bits, respectively.
-   */
-  private static double[] create(double valueLow, double valueHigh) {
-    assert (!Double.isNaN(valueHigh));
-    assert (!Double.isNaN(valueLow));
-    assert (!Double.isInfinite(valueHigh));
-    assert (!Double.isInfinite(valueLow));
-    assert (Math.floor(valueHigh) == valueHigh);
-    assert (Math.floor(valueLow) == valueLow);
-
-    // remove overly high bits
-    valueHigh %= TWO_PWR_64_DBL;
-    valueLow %= TWO_PWR_64_DBL;
-
-    // segregate high and low bits between high and low
-    {
-      double diffHigh = valueHigh % TWO_PWR_32_DBL;
-      double diffLow = Math.floor(valueLow / TWO_PWR_32_DBL) * TWO_PWR_32_DBL;
-
-      valueHigh = (valueHigh - diffHigh) + diffLow;
-      valueLow = (valueLow - diffLow) + diffHigh;
-    }
-
-    // Most or all of the while's in this implementation could probably be if's,
-    // but they are left as while's for now pending a careful review.
-
-    // make valueLow be positive
-    while (valueLow < LOW_MIN) {
-      valueLow += TWO_PWR_32_DBL;
-      valueHigh -= TWO_PWR_32_DBL;
-    }
-
-    // make valueLow not too large
-    while (valueLow > LOW_MAX) {
-      valueLow -= TWO_PWR_32_DBL;
-      valueHigh += TWO_PWR_32_DBL;
-    }
-
-    // make valueHigh within range
-    valueHigh = valueHigh % TWO_PWR_64_DBL;
-    while (valueHigh > HIGH_MAX) {
-      valueHigh -= TWO_PWR_64_DBL;
-    }
-    while (valueHigh < HIGH_MIN) {
-      valueHigh += TWO_PWR_64_DBL;
-    }
-
-    return createNormalized(valueLow, valueHigh);
-  }
-
-  /**
-   * Create an instance. The high and low parts must be normalized. Normal
-   * callers should use the factory method {@link #make(double, double) make}.
-   */
-  private static double[] createNormalized(double valueLow, double valueHigh) {
-    assert (valueHigh <= HIGH_MAX);
-    assert (valueHigh >= HIGH_MIN);
-    assert (valueLow >= 0);
-    assert (valueLow <= LOW_MAX);
-    assert (valueHigh % TWO_PWR_32_DBL == 0);
-    assert (Math.floor(valueLow) == valueLow); // no fractional bits allowed
-
-    if (RUN_IN_JVM) {
-      return new double[] {valueLow, valueHigh};
-    } else {
-      return newLong0(valueLow, valueHigh);
-    }
-  }
-
-  private static int highBits(double[] a) {
-    return (int) (a[HIGH] / TWO_PWR_32_DBL);
-  }
-
-  private static double[] internalFromInt(int value) {
-    if (value >= 0) {
-      return createNormalized(value, 0.0);
-    } else {
-      return createNormalized(value + TWO_PWR_32_DBL, -TWO_PWR_32_DBL);
-    }
-  }
-
-  private static boolean isNegative(double[] a) {
-    return a[HIGH] < 0;
-  }
-
-  private static boolean isOdd(double[] a) {
-    return (lowBits(a) & 1) == 1;
-  }
-
-  private static boolean isZero(double[] a) {
-    return a[LOW] == 0.0 && a[HIGH] == 0.0;
-  }
-
-  private static int lowBits(double[] a) {
-    if (a[LOW] >= TWO_PWR_31_DBL) {
-      return (int) (a[LOW] - TWO_PWR_32_DBL);
-    } else {
-      return (int) a[LOW];
-    }
-  }
-
-  /**
-   * Make an instance equivalent to stringing highBits next to lowBits, where
-   * highBits and lowBits are assumed to be in 32-bit twos-complement notation.
-   * As a result, for any double[] l, the following identity holds:
-   * 
-   * <blockquote> <code>l == makeFromBits(l.highBits(), l.lowBits())</code>
-   * </blockquote>
-   */
-  private static double[] makeFromBits(int highBits, int lowBits) {
-    double high = highBits * TWO_PWR_32_DBL;
-    double low = lowBits;
-    if (lowBits < 0) {
-      low += TWO_PWR_32_DBL;
-    }
-    return createNormalized(low, high);
-  }
-
-  private static double[] multByMinValue(double[] a) {
-    if (isOdd(a)) {
-      return MIN_VALUE;
-    } else {
-      return ZERO;
-    }
-  }
-
-  /**
-   * Faster web mode implementation doesn't need full type semantics.
-   */
-  private static native double[] newLong0(double valueLow, double valueHigh) /*-{
-    return [valueLow, valueHigh];
-  }-*/;
-
-  /**
-   * Return a power of two as a double.
-   * 
-   * @return 2 raised to the <code>n</code>
-   */
-  private static double pwrAsDouble(int n) {
-    if (n <= 30) {
-      return (1 << n);
-    } else {
-      return pwrAsDouble(30) * pwrAsDouble(n - 30);
-    }
-  }
-
-  private static double toDoubleRoundDown(double[] a) {
-    int magnitute = (int) (Math.log(a[HIGH]) / LN_2);
-    if (magnitute <= PRECISION_BITS) {
-      return toDouble(a);
-    } else {
-      int diff = magnitute - PRECISION_BITS;
-      int toSubtract = (1 << diff) - 1;
-      return a[HIGH] + (a[LOW] - toSubtract);
-    }
-  }
-
-  private static double toDoubleRoundUp(double[] a) {
-    int magnitute = (int) (Math.log(a[HIGH]) / LN_2);
-    if (magnitute <= PRECISION_BITS) {
-      return toDouble(a);
-    } else {
-      int diff = magnitute - PRECISION_BITS;
-      int toAdd = (1 << diff) - 1;
-      return a[HIGH] + (a[LOW] + toAdd);
-    }
-  }
-
-  /**
-   * Web mode implementation; the long is already the right object.
-   */
-  @UnsafeNativeLong
-  private static native double[] typeChange0(long value) /*-{
-    return value;
-  }-*/;
-
+  
   /**
    * Not instantiable.
    */
   private LongLib() {
   }
-
 }
diff --git a/dev/core/super/com/google/gwt/lang/LongLibBase.java b/dev/core/super/com/google/gwt/lang/LongLibBase.java
new file mode 100644
index 0000000..f293f9c
--- /dev/null
+++ b/dev/core/super/com/google/gwt/lang/LongLibBase.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2010 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.lang;
+
+import com.google.gwt.core.client.UnsafeNativeLong;
+
+final class LongEmul {
+  int l, m, h; // Used only when RUN_IN_JVM is true
+  
+  public static LongEmul getInstance() {
+    return new LongEmul();
+  }
+}
+
+/**
+ * Implements a Java <code>long</code> in a way that can be translated to
+ * JavaScript.  Methods that are meant to be called from outside this package
+ * are located in {@link LongLib}.
+ */
+class LongLibBase {
+  // Force the class to exist
+  public static LongEmul instance = new LongEmul();
+
+  /*
+   * Implementation: A LongEmul containing three values {l, m, h} (low, middle,
+   * high) such that (x.l + ((long) x.m << 22) + ((long) x.h << 44)) is equal to
+   * the original long integer.  The constant 22 is chosen since some browsers
+   * are faster when operating on integers of 24 bits or less.
+   * 
+   * By convention, we expect and maintain that the upper bits of each word
+   * be zeroed.
+   * 
+   * Note that this class must be careful using type "long". Being the
+   * implementation of the long type for web mode, any place it uses a long is
+   * not usable in web mode. There is currently one such method: {@link
+   * LongLib#getAsIntArray}.
+   */
+
+  // Note that the 'mul' method implicitly depends on the specific value
+  // BITS == 22
+  protected static final int BITS = 22;
+  protected static final int BITS01 = 2 * BITS;
+  protected static final int BITS2 = 64 - BITS01;
+  protected static final int MASK = (1 << BITS) - 1;
+  protected static final int MASK_2 = (1 << BITS2) - 1;
+  protected static LongEmul remainder;
+  
+  /**
+   * Allow a standalone Java test such as LongLibJreTest to run this code.
+   */
+  protected static boolean RUN_IN_JVM = false;
+  
+  protected static final int SIGN_BIT = BITS2 - 1;
+  protected static final int SIGN_BIT_VALUE = 1 << SIGN_BIT;
+  protected static final double TWO_PWR_15_DBL = 0x8000;
+  protected static final double TWO_PWR_16_DBL = 0x10000;
+  protected static final double TWO_PWR_22_DBL = 0x400000;
+  protected static final double TWO_PWR_31_DBL = TWO_PWR_16_DBL * TWO_PWR_15_DBL;
+  protected static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
+  protected static final double TWO_PWR_44_DBL = TWO_PWR_22_DBL * TWO_PWR_22_DBL;
+  protected static final double TWO_PWR_63_DBL = TWO_PWR_32_DBL * TWO_PWR_31_DBL;
+
+  /**
+   * Web mode implementation; the int array is already the right object.
+   */
+  @UnsafeNativeLong
+  protected static native long asLong(LongEmul value) /*-{
+    return value;
+  }-*/;
+  
+  protected static LongEmul create(int value) {
+    int a0 = value & MASK;
+    int a1 = (value >> BITS) & MASK;
+    int a2 = (value < 0) ? MASK_2 : 0;
+
+    if (RUN_IN_JVM ) {
+      LongEmul a = new LongEmul();
+      a.l = a0;
+      a.m = a1;
+      a.h = a2;
+      return a;
+    }
+    return create0(a0, a1, a2);
+  }
+  
+  protected static LongEmul create(int a0, int a1, int a2) {
+    if (RUN_IN_JVM) {
+      LongEmul a = new LongEmul();
+      a.l = a0;
+      a.m = a1;
+      a.h = a2;
+      return a;
+    }
+    return create0(a0, a1, a2);
+  }
+  
+  protected static LongEmul divMod(LongEmul a, LongEmul b, boolean computeRemainder) {
+    if (isZero(b)) {
+      throw new ArithmeticException("divide by zero");
+    }
+    if (isZero(a)) {
+      if (computeRemainder) {
+        remainder = create(); // zero
+      }
+      return create(); // zero
+    }
+
+    // MIN_VALUE / MIN_VALUE = 1, anything other a / MIN_VALUE is 0
+    if (isMinValue(b)) {
+      return divModByMinValue(a, computeRemainder);
+    }
+
+    // Normalize b to abs(b), keeping track of the parity in 'negative'.
+    // We can do this because we have already ensured that b != MIN_VALUE.
+    boolean negative = false;
+    if (isNegative(b)) {
+      b = LongLib.neg(b);
+      negative = !negative;
+    }
+
+    // If b == 2^n, bpower will be n, otherwise it will be -1
+    int bpower = powerOfTwo(b);
+
+    // True if the original value of a is negative
+    boolean aIsNegative = false;
+    // True if the original value of a is Long.MIN_VALUE
+    boolean aIsMinValue = false;
+
+    /*
+     * Normalize a to a positive value, keeping track of the sign change
+     * in 'negative' (which tracks the sign of both a and b and is used to
+     * determine the sign of the quotient) and 'aIsNegative' (which is used
+     * to determine the sign of the remainder).
+     * 
+     * For all values of a except MIN_VALUE, we can just negate a and
+     * modify negative and aIsNegative appropriately.  When a == MIN_VALUE,
+     * negation is not possible without overflowing 64 bits, so instead
+     * of computing abs(MIN_VALUE) / abs(b) we compute
+     * (abs(MIN_VALUE) - 1) / abs(b).  The only circumstance under which
+     * these quotients differ is when b is a power of two, which will
+     * divide abs(MIN_VALUE) == 2^64 exactly.  In this case, we can get
+     * the proper result by shifting MIN_VALUE in unsigned fashion.
+     * 
+     * We make a single copy of a before the first operation that needs to
+     * modify its value.
+     */
+    boolean aIsCopy = false;
+    if (isMinValue(a)) {
+      aIsMinValue = true;
+      aIsNegative = true;
+      // If b is not a power of two, treat -a as MAX_VALUE (instead of the
+      // actual value (MAX_VALUE + 1)).
+      if (bpower == -1) {
+        a = create(LongLib.Const.MAX_VALUE);
+        aIsCopy = true;
+        negative = !negative;
+      } else {
+        // Signed shift of MIN_VALUE produces the right answer
+        LongEmul c = LongLib.shr(a, bpower);
+        if (negative) {
+          negate(c);
+        }
+        if (computeRemainder) {
+          remainder = create(); // zero
+        }
+        return c;
+      }
+    } else if (isNegative(a)) {
+      aIsNegative = true;
+      a = LongLib.neg(a);
+      aIsCopy = true;
+      negative = !negative;
+    }
+
+    // Now both a and b are non-negative
+    
+    // If b is a power of two, just shift
+    if (bpower != -1) {
+      return divModByShift(a, bpower, negative, aIsNegative,
+          computeRemainder);
+    }
+
+    // if a < b, the quotient is 0 and the remainder is a
+    if (LongLib.lt(a, b)) {
+      if (computeRemainder) {
+        if (aIsNegative) {
+          remainder = LongLib.neg(a);
+        } else {
+          remainder = create(a);
+        }
+      }
+      return create(); // zero
+    }
+    
+    // Generate the quotient using bit-at-a-time long division
+    return divModHelper(aIsCopy ? a : create(a), b, negative,
+        aIsNegative, aIsMinValue, computeRemainder);
+  }
+
+  protected static int getH(LongEmul a) {
+    if (RUN_IN_JVM) {
+      return a.h;
+    }
+    return getHNative(a);
+  }
+
+  protected static int getL(LongEmul a) {
+    if (RUN_IN_JVM) {
+      return a.l;
+    }
+    return getLNative(a);
+  }
+
+  protected static int getM(LongEmul a) {
+    if (RUN_IN_JVM) {
+      return a.m;
+    }
+    return getMNative(a);
+  }
+
+  protected static boolean isMinValue(LongEmul a) {
+    return getH(a) == SIGN_BIT_VALUE && getM(a) == 0 && getL(a) == 0;
+  }
+
+  protected static boolean isNegative(LongEmul a) {
+    return sign(a) != 0;
+  }
+
+  protected static boolean isZero(LongEmul a) {
+    return getL(a) == 0 && getM(a) == 0 && getH(a) == 0;
+  }
+
+  /**
+   * a = -a
+   */
+  protected static void negate(LongEmul a) {
+    int neg0 = (~getL(a) + 1) & MASK;
+    int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK;
+    int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2;
+
+    if (RUN_IN_JVM) {
+      a.l = neg0;
+      a.m = neg1;
+      a.h = neg2;
+    } else {
+      setL(a, neg0);
+      setM(a, neg1);
+      setH(a, neg2);
+    }
+  }
+
+  /**
+   * @return 0 if a is >= 0, 1 if a < 0.
+   */
+  protected static int sign(LongEmul a) {
+    return getH(a) >> (BITS2 - 1);
+  }
+
+  // Assumes a is non-negative
+  protected static double toDoubleHelper(LongEmul a) {
+    return getL(a) + (getM(a) * TWO_PWR_22_DBL) + (getH(a) * TWO_PWR_44_DBL);
+  }
+
+  /**
+   * Creates a long instance equal to 0.
+   */
+  private static LongEmul create() {
+    if (RUN_IN_JVM) {
+      return new LongEmul();
+    }
+    return create0(0, 0, 0);
+  }
+
+  /**
+   * Creates a long instance equal to a given long.
+   */
+  private static LongEmul create(LongEmul a) {
+    if (RUN_IN_JVM) {
+      LongEmul b = new LongEmul();
+      b.l = getL(a);
+      b.m = getM(a);
+      b.h = getH(a);
+      return b;
+    }
+    return create0(getL(a), getM(a), getH(a));
+  }
+
+  private static native LongEmul create0(int l, int m, int h) /*-{
+    return (a = @com.google.gwt.lang.LongEmul::getInstance()(), a.l = l, a.m = m, a.h = h, a);
+  }-*/;
+
+  private static LongEmul divModByMinValue(LongEmul a, boolean computeRemainder) {
+    // MIN_VALUE / MIN_VALUE == 1, remainder = 0
+    // (a != MIN_VALUE) / MIN_VALUE == 0, remainder == a
+    if (isMinValue(a)) {
+      if (computeRemainder) {
+        remainder = create(); // zero
+      }
+      return create(LongLib.Const.ONE);
+    }
+    if (computeRemainder) {
+      remainder = create(a);
+    }
+    return create(); // zero
+  }
+  
+  private static LongEmul divModByShift(LongEmul a, int bpower,
+      boolean negative, boolean aIsNegative, boolean computeRemainder) {
+    LongEmul c = LongLib.shr(a, bpower);
+    if (negative) {
+      negate(c);
+    }
+
+    if (computeRemainder) {
+      a = maskRight(a, bpower);
+      if (aIsNegative) {
+        remainder = LongLib.neg(a);
+      } else {
+        remainder = create(a);
+      }
+    }
+    return c;
+  }
+
+  private static LongEmul divModHelper(LongEmul a, LongEmul b, boolean negative,
+      boolean aIsNegative, boolean aIsMinValue, boolean computeRemainder) {
+    // Align the leading one bits of a and b by shifting b left
+    int shift = numberOfLeadingZeros(b) - numberOfLeadingZeros(a);
+    LongEmul bshift = LongLib.shl(b, shift);
+
+    LongEmul quotient = create();
+    while (shift >= 0) {
+      boolean gte = trialSubtract(a, bshift);
+      if (gte) {
+        setBit(quotient, shift);
+        if (isZero(a)) {
+          break;
+        }
+      }
+
+      toShru1(bshift);
+      shift--;
+    }
+
+    if (negative) {
+      negate(quotient);
+    }
+
+    if (computeRemainder) {
+      if (aIsNegative) {
+        remainder = LongLib.neg(a);
+        if (aIsMinValue) {
+          remainder = LongLib.sub(remainder, LongLib.Const.ONE);
+        }
+      } else {
+        remainder = create(a);
+      }
+    }
+
+    return quotient;
+  }
+
+  private static native int getHNative(LongEmul a) /*-{
+    return a.h;
+  }-*/;
+
+  private static native int getLNative(LongEmul a) /*-{
+    return a.l;
+  }-*/;
+
+  private static native int getMNative(LongEmul a) /*-{
+    return a.m;
+  }-*/;
+
+  /**
+   * a &= ((1L << bits) - 1)
+   */
+  private static LongEmul maskRight(LongEmul a, int bits) {
+    int b0, b1, b2;
+    if (bits <= BITS) {
+      b0 = getL(a) & ((1 << bits) - 1);
+      b1 = b2 = 0;
+    } else if (bits <= BITS01) {
+      b0 = getL(a);
+      b1 = getM(a) & ((1 << (bits - BITS)) - 1);
+      b2 = 0;
+    } else {
+      b0 = getL(a);
+      b1 = getM(a);
+      b2 = getH(a) & ((1 << (bits - BITS01)) - 1);
+    }
+
+    return create(b0, b1, b2);
+  }
+
+  /**
+   * Return the number of leading zeros of a long value.
+   */
+  private static int numberOfLeadingZeros(LongEmul a) {
+    int b2 = Integer.numberOfLeadingZeros(getH(a));
+    if (b2 == 32) {
+      int b1 = Integer.numberOfLeadingZeros(getM(a));
+      if (b1 == 32) {
+        return Integer.numberOfLeadingZeros(getL(a)) + BITS2 + 2 * BITS - 32;
+      } else {
+        return b1 + BITS2 - (32 - BITS);
+      }
+    } else {
+      return b2 - (32 - BITS2);
+    }
+  }
+
+  /**
+   * Return the exact log base 2 of a, or -1 if a is not a power of two: 
+   * 
+   * <pre>
+   * if (x == 2^n) {
+   *   return n;
+   * } else {
+   *   return -1;
+   * }
+   * </pre>
+   */
+  private static int powerOfTwo(LongEmul a) {
+    // Power of two or 0
+    int l = getL(a);
+    if ((l & (l - 1)) != 0) {
+      return -1;
+    }
+    int m = getM(a);
+    if ((m & (m - 1)) != 0) {
+      return -1;
+    }
+    int h = getH(a);
+    if ((h & (h - 1)) != 0) {
+      return -1;
+    }
+    if (h == 0 && m == 0 && l == 0) {
+      return -1;
+    }
+    if (h == 0 && m == 0 && l != 0) {
+      return Integer.numberOfTrailingZeros(l);
+    }
+    if (h == 0 && m != 0 && l == 0) {
+      return Integer.numberOfTrailingZeros(m) + BITS;
+    }
+    if (h != 0 && m == 0 && l == 0) {
+      return Integer.numberOfTrailingZeros(h) + BITS01;
+    }
+
+    return -1;
+  }
+
+  private static void setBit(LongEmul a, int bit) {
+    if (RUN_IN_JVM) {
+      if (bit < BITS) {
+        a.l |= 0x1 << bit;
+      } else if (bit < BITS01) {
+        a.m |= 0x1 << (bit - BITS);
+      } else {
+        a.h |= 0x1 << (bit - BITS01);
+      }
+    } else {
+      if (bit < BITS) {
+        setBitL(a, bit);
+      } else if (bit < BITS01) {
+        setBitM(a, bit - BITS);
+      } else {
+        setBitH(a, bit - BITS01);
+      }
+    }
+  }
+  
+  private static native void setBitH(LongEmul a, int bit) /*-{
+    a.h |= 1 << bit;
+  }-*/;
+  
+  private static native void setBitL(LongEmul a, int bit) /*-{
+    a.l |= 1 << bit;
+  }-*/;
+  
+  private static native void setBitM(LongEmul a, int bit) /*-{
+    a.m |= 1 << bit;
+  }-*/;
+
+  private static native void setH(LongEmul a, int x) /*-{
+    a.h = x;
+  }-*/;
+
+  private static native void setL(LongEmul a, int x) /*-{
+    a.l = x;
+  }-*/;
+
+  private static native void setM(LongEmul a, int x) /*-{
+    a.m = x;
+  }-*/;
+
+  /**
+   * a >>= 1.  Assumes a >= 0.
+   */
+  private static void toShru1(LongEmul a) {
+    int a1 = getM(a);
+    int a2 = getH(a);
+    int a0 = getL(a);
+
+    if (RUN_IN_JVM) {
+      a.h = a2 >>> 1;
+      a.m = (a1 >>> 1) | ((a2 & 0x1) << (BITS - 1));
+      a.l = (a0 >>> 1) | ((a1 & 0x1) << (BITS - 1));
+    } else {
+      setH(a, a2 >>> 1);
+      setM(a, (a1 >>> 1) | ((a2 & 0x1) << (BITS - 1)));
+      setL(a, (a0 >>> 1) | ((a1 & 0x1) << (BITS - 1)));
+    }
+  }
+
+  /**
+   * Attempt to subtract b from a if a >= b:
+   * 
+   * <pre>
+   * if (a >= b) {
+   *   a -= b;
+   *   return true;
+   * } else {
+   *   return false;
+   * }
+   * </pre>
+   */
+  private static boolean trialSubtract(LongEmul a, LongEmul b) {
+    // Early exit
+    int sum2 = getH(a) - getH(b);
+    if (sum2 < 0) {
+      return false;
+    }
+
+    int sum0 = getL(a) - getL(b);
+    int sum1 = getM(a) - getM(b) + (sum0 >> BITS);
+    sum2 += (sum1 >> BITS);
+
+    if (sum2 < 0) {
+      return false;
+    }
+
+    if (RUN_IN_JVM) {
+      a.l = sum0 & MASK;
+      a.m = sum1 & MASK;
+      a.h = sum2 & MASK_2;
+    } else {
+      setL(a, sum0 & MASK);
+      setM(a, sum1 & MASK);
+      setH(a, sum2 & MASK_2);
+    }
+
+    return true;
+  }
+
+  /**
+   * Not instantiable outside this package.
+   */
+  LongLibBase() {
+  }
+}
diff --git a/dev/core/test/com/google/gwt/lang/LongLibJreTest.java b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
index d8b5c85..559bdab 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
@@ -18,14 +18,14 @@
 import junit.framework.TestCase;
 
 /**
- * Test the LongLib class as a GWTTestCase.
+ * Test the LongLib class as a non-GWT TestCase.
  */
 public class LongLibJreTest extends TestCase {
-
+  
   static {
-    LongLib.RUN_IN_JVM = true;
+    LongLibBase.RUN_IN_JVM = true;
   }
-
+  
   private LongLibTestBase impl = new LongLibTestBase();
 
   public void testAAAA() {
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
index bd0f38d..9f2052a 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
@@ -25,36 +25,36 @@
  */
 public class LongLibTestBase extends TestCase {
 
-  static void assertEquals(double[] expected, double[] actual) {
+  static void assertEquals(LongEmul expected, LongEmul actual) {
     assertTrue("expected=" + LongLib.toString(expected) + "  actual="
         + LongLib.toString(actual), LongLib.eq(expected, actual));
   }
 
   public void testAdditive() {
     {
-      final double[] n1 = LongLib.fromInt(1234);
-      final double[] n2 = LongLib.fromInt(9876);
+      final LongEmul n1 = LongLib.fromInt(1234);
+      final LongEmul n2 = LongLib.fromInt(9876);
       assertEquals(LongLib.fromInt(11110), LongLib.add(n1, n2));
       assertEquals(LongLib.fromInt(-8642), LongLib.sub(n1, n2));
     }
 
     {
-      final double[] n1 = LongLib.fromInt(-1234);
-      final double[] n2 = LongLib.fromInt(9876);
+      final LongEmul n1 = LongLib.fromInt(-1234);
+      final LongEmul n2 = LongLib.fromInt(9876);
       assertEquals(LongLib.fromInt(8642), LongLib.add(n1, n2));
       assertEquals(LongLib.fromInt(-11110), LongLib.sub(n1, n2));
     }
 
     {
-      final double[] n1 = LongLib.fromInt(-1234);
-      final double[] n2 = LongLib.fromInt(-9876);
+      final LongEmul n1 = LongLib.fromInt(-1234);
+      final LongEmul n2 = LongLib.fromInt(-9876);
       assertEquals(LongLib.fromInt(-11110), LongLib.add(n1, n2));
       assertEquals(LongLib.fromInt(8642), LongLib.sub(n1, n2));
     }
 
     {
-      final double[] n1 = longFromBits(0x12345678, 0xabcdabcd);
-      final double[] n2 = longFromBits(0x77773333, 0x22224444);
+      final LongEmul n1 = longFromBits(0x12345678, 0xabcdabcd);
+      final LongEmul n2 = longFromBits(0x77773333, 0x22224444);
       assertEquals(longFromBits(0x89ab89ab, 0xcdeff011), LongLib.add(n1, n2));
       assertEquals(longFromBits(0x9abd2345, 0x89ab6789), LongLib.sub(n1, n2));
     }
@@ -62,8 +62,8 @@
 
   public void testBitOps() {
     {
-      final double[] n1 = LongLib.fromInt(1234);
-      final double[] n2 = LongLib.fromInt(9876);
+      final LongEmul n1 = LongLib.fromInt(1234);
+      final LongEmul n2 = LongLib.fromInt(9876);
 
       assertEquals(LongLib.fromInt(1168), LongLib.and(n1, n2));
       assertEquals(LongLib.fromInt(9942), LongLib.or(n1, n2));
@@ -73,8 +73,8 @@
     }
 
     {
-      final double[] n1 = LongLib.fromInt(-1234);
-      final double[] n2 = LongLib.fromInt(9876);
+      final LongEmul n1 = LongLib.fromInt(-1234);
+      final LongEmul n2 = LongLib.fromInt(9876);
       assertEquals(LongLib.fromInt(8708), LongLib.and(n1, n2));
       assertEquals(LongLib.fromInt(-66), LongLib.or(n1, n2));
       assertEquals(LongLib.fromInt(-8774), LongLib.xor(n1, n2));
@@ -83,8 +83,8 @@
     }
 
     {
-      final double[] n1 = LongLib.shl(LongLib.fromInt(0x1234), 32);
-      final double[] n2 = LongLib.shl(LongLib.fromInt(0x9876), 32);
+      final LongEmul n1 = LongLib.shl(LongLib.fromInt(0x1234), 32);
+      final LongEmul n2 = LongLib.shl(LongLib.fromInt(0x9876), 32);
       assertEquals(LongLib.shl(LongLib.fromInt(0x1034), 32),
           LongLib.and(n1, n2));
       assertEquals(LongLib.shl(LongLib.fromInt(0x9a76), 32), LongLib.or(n1, n2));
@@ -113,17 +113,28 @@
     assertTrue(!LongLib.eq(LongLib.fromInt(12), LongLib.fromInt(11)));
     assertTrue(LongLib.gte(LongLib.fromInt(12), LongLib.fromInt(11)));
     assertTrue(LongLib.gt(LongLib.fromInt(12), LongLib.fromInt(11)));
+    
+    assertTrue(LongLib.gt(LongLib.fromInt(-10), LongLib.fromInt(-11)));
+    assertTrue(LongLib.gt(LongLib.fromInt(10), LongLib.fromInt(-11)));
+    assertTrue(!LongLib.gt(LongLib.fromInt(-10), LongLib.fromInt(11)));
+    assertTrue(LongLib.gte(LongLib.fromInt(-10), LongLib.fromInt(-11)));
+    assertTrue(LongLib.gte(LongLib.fromInt(-10), LongLib.fromInt(-10)));
+    assertTrue(!LongLib.lt(LongLib.fromInt(-10), LongLib.fromInt(-11)));
+    assertTrue(!LongLib.lte(LongLib.fromInt(-10), LongLib.fromInt(-11)));
+    assertTrue(LongLib.lte(LongLib.fromInt(-10), LongLib.fromInt(-10)));
+    assertTrue(LongLib.eq(LongLib.fromInt(-10), LongLib.fromInt(-10)));
+    assertTrue(!LongLib.neq(LongLib.fromInt(-10), LongLib.fromInt(-10)));
 
     // the following three comparisons cannot be implemented by
     // subtracting the arguments, because the subtraction causes an overflow
-    final double[] largeNeg = longFromBits(0x82341234, 0x0);
-    final double[] largePos = longFromBits(0x12341234, 0x0);
+    final LongEmul largeNeg = longFromBits(0x82341234, 0x0);
+    final LongEmul largePos = longFromBits(0x12341234, 0x0);
     assertTrue(LongLib.lt(largeNeg, largePos));
 
     assertTrue(LongLib.lt(Const.MIN_VALUE, LongLib.fromInt(0)));
     assertTrue(LongLib.gt(LongLib.fromInt(0), Const.MIN_VALUE));
 
-    final double[] largePosPlusOne = LongLib.add(largePos, LongLib.fromInt(1));
+    final LongEmul largePosPlusOne = LongLib.add(largePos, LongLib.fromInt(1));
 
     assertTrue(LongLib.lt(largePos, largePosPlusOne));
     assertTrue(LongLib.lte(largePos, largePosPlusOne));
@@ -152,22 +163,64 @@
   }
 
   public void testDiv() {
-    double[] deadBeef = LongLib.typeChange(0xdeadbeefdeadbeefL);
-    double[] ten = LongLib.fromInt(10);
-    assertEquals(LongLib.typeChange(-240105308887621659L), LongLib.div(
+    LongEmul deadBeef = longFromBits(0xdeadbeef, 0xdeadbeef);
+    LongEmul ten = LongLib.fromInt(10);
+    assertEquals(longFromBits(0xfcaaf97e, 0x63115fe5), LongLib.div(
         deadBeef, ten));
     assertEquals(Const.ZERO, LongLib.div(Const.ONE, Const.TWO));
-    assertEquals(LongLib.typeChange(4611686018427387903L), LongLib.div(
+    assertEquals(longFromBits(0x3fffffff, 0xffffffff), LongLib.div(
         Const.MAX_VALUE, Const.TWO));
+    
+    assertEquals(Const.ZERO, LongLib.div(Const.ZERO, LongLib.fromInt(1000)));
+    assertEquals(Const.ONE, LongLib.div(Const.MIN_VALUE, Const.MIN_VALUE));
+    assertEquals(Const.ZERO, LongLib.div(LongLib.fromInt(1000), Const.MIN_VALUE));
+    assertEquals("-1125899906842624", LongLib.toString(LongLib.div(Const.MIN_VALUE, LongLib.fromInt(8192))));
+    assertEquals("-1125762484664320", LongLib.toString(LongLib.div(Const.MIN_VALUE, LongLib.fromInt(8193))));
+    assertEquals(Const.ZERO, LongLib.div(LongLib.fromInt(-1000), LongLib.fromInt(8192)));
+    assertEquals(Const.ZERO, LongLib.div(LongLib.fromInt(-1000), LongLib.fromInt(8193)));
+    assertEquals(LongLib.fromInt(-122070), LongLib.div(LongLib.fromInt(-1000000000), LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(-122055), LongLib.div(LongLib.fromInt(-1000000000), LongLib.fromInt(8193)));
+    assertEquals(LongLib.fromInt(122070), LongLib.div(LongLib.fromInt(1000000000), LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(122055), LongLib.div(LongLib.fromInt(1000000000), LongLib.fromInt(8193)));
+    
+    assertEquals(longFromBits(0x1fffff, 0xffffffff), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00000400)));
+    assertEquals(longFromBits(0x1fff, 0xffffffff), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00040000)));
+    assertEquals(longFromBits(0x1f, 0xffffffff), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x04000000)));
+    assertEquals(LongLib.fromInt(536870911), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000004, 0x00000000)));
+    assertEquals(LongLib.fromInt(2097151), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000400, 0x00000000)));
+    assertEquals(LongLib.fromInt(8191), LongLib.div(Const.MAX_VALUE, longFromBits(0x00040000, 0x00000000)));
+    assertEquals(LongLib.fromInt(31), LongLib.div(Const.MAX_VALUE, longFromBits(0x04000000, 0x00000000)));
+    
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00000300));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x30000000));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x00000000));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x00000300));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x30000000));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x30000300));
+    LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x30000300));
   }
 
   public void testFactorial() {
-    double[] fact18 = fact(LongLib.fromInt(18));
-    double[] fact17 = fact(LongLib.fromInt(17));
+    LongEmul fact18 = fact(LongLib.fromInt(18));
+    LongEmul fact17 = fact(LongLib.fromInt(17));
     assertEquals(LongLib.fromInt(18), LongLib.div(fact18, fact17));
   }
 
   public void testFromDouble() {
+    assertEquals("4611686018427387904", LongLib.toString(LongLib.fromDouble(Math.pow(2, 62))));
+    assertEquals("35184372088832", LongLib.toString(LongLib.fromDouble(Math.pow(2, 45))));
+    assertEquals("35184372088832", LongLib.toString(LongLib.fromDouble(Math.pow(2, 45))));
+    assertEquals("17592186044417", LongLib.toString(LongLib.fromDouble(Math.pow(2, 44) + 1)));
+    assertEquals("17592186044416", LongLib.toString(LongLib.fromDouble(Math.pow(2, 44))));
+    assertEquals("17592186044415", LongLib.toString(LongLib.fromDouble(Math.pow(2, 44) - 1)));
+    assertEquals("8796093022208", LongLib.toString(LongLib.fromDouble(Math.pow(2, 43))));
+    assertEquals(LongLib.fromInt(8388608), LongLib.fromDouble(Math.pow(2, 23)));
+    assertEquals(LongLib.fromInt(4194305), LongLib.fromDouble(Math.pow(2, 22) + 1));
+    assertEquals(LongLib.fromInt(4194304), LongLib.fromDouble(Math.pow(2, 22)));
+    assertEquals(LongLib.fromInt(4194303), LongLib.fromDouble(Math.pow(2, 22) - 1));
+    assertEquals(LongLib.fromInt(2097152), LongLib.fromDouble(Math.pow(2, 21)));
+    assertEquals(LongLib.fromInt(1048576), LongLib.fromDouble(Math.pow(2, 20)));
+    
     // these tests are based on JLS3, section 5.1.3
 
     assertEquals(LongLib.fromInt(10), LongLib.fromDouble(10.5));
@@ -190,6 +243,28 @@
     assertEquals(Const.MAX_VALUE, LongLib.neg(LongLib.add(Const.MIN_VALUE,
         LongLib.fromInt(1))));
   }
+  
+  public void testMod() {
+    assertEquals(LongLib.fromInt(0), LongLib.mod(Const.ZERO, LongLib.fromInt(1000)));
+    assertEquals(LongLib.fromInt(0), LongLib.mod(Const.MIN_VALUE, Const.MIN_VALUE));
+    assertEquals(LongLib.fromInt(1000), LongLib.mod(LongLib.fromInt(1000), Const.MIN_VALUE));
+    assertEquals(LongLib.fromInt(0), LongLib.mod(Const.MIN_VALUE, LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(-2048), LongLib.mod(Const.MIN_VALUE, LongLib.fromInt(8193)));
+    assertEquals(LongLib.fromInt(-1000), LongLib.mod(LongLib.fromInt(-1000), LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(-1000), LongLib.mod(LongLib.fromInt(-1000), LongLib.fromInt(8193)));
+    assertEquals(LongLib.fromInt(-2560), LongLib.mod(LongLib.fromInt(-1000000000), LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(-3385), LongLib.mod(LongLib.fromInt(-1000000000), LongLib.fromInt(8193)));
+    assertEquals(LongLib.fromInt(2560), LongLib.mod(LongLib.fromInt(1000000000), LongLib.fromInt(8192)));
+    assertEquals(LongLib.fromInt(3385), LongLib.mod(LongLib.fromInt(1000000000), LongLib.fromInt(8193)));
+    
+    assertEquals(longFromBits(0x0, 0x3ff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000000, 0x00000400)));
+    assertEquals(longFromBits(0x0, 0x3ffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000000, 0x00040000)));
+    assertEquals(longFromBits(0x0, 0x3ffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000000, 0x04000000)));
+    assertEquals(longFromBits(0x3, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000004, 0x00000000)));
+    assertEquals(longFromBits(0x3ff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000400, 0x00000000)));
+    assertEquals(longFromBits(0x3ffff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00040000, 0x00000000)));
+    assertEquals(longFromBits(0x3ffffff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x04000000, 0x00000000)));
+  }
 
   public void testMultiplicative() {
     assertEquals(LongLib.fromInt(3333), LongLib.mul(LongLib.fromInt(1111),
@@ -282,7 +357,7 @@
 
     assertEquals(LongLib.fromInt(-1 << 5), LongLib.shl(LongLib.fromInt(-1), 5));
     assertEquals(LongLib.fromInt(-1), LongLib.shl(LongLib.fromInt(-1), 0));
-    assertEquals(LongLib.neg(LongLib.typeChange(0x4000000000000000L)),
+    assertEquals(LongLib.neg(longFromBits(0x40000000, 0x00000000)),
         LongLib.shr(LongLib.shl(LongLib.fromInt(1), 63), 1));
     assertEquals(LongLib.fromInt(0), LongLib.shl(LongLib.shl(
         LongLib.fromInt(-1), 32), 32));
@@ -292,12 +367,89 @@
         LongLib.neg(longFromBits(8, 0)), 1));
     assertEquals(longFromBits(0x7ffffffc, 0x0), LongLib.shru(
         LongLib.neg(longFromBits(8, 0)), 1));
+    
+    assertEquals(longFromBits(0x00723456, 0x789abcde), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 8));
+    assertEquals(longFromBits(0x00007234, 0x56789abc), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 16));
+    assertEquals(longFromBits(0x00000072, 0x3456789a), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 24));
+    assertEquals(longFromBits(0x00000007, 0x23456789), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 28));
+    assertEquals(longFromBits(0x00000000, 0x72345678), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 32));
+    assertEquals(longFromBits(0x00000000, 0x07234567), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 36));
+    assertEquals(longFromBits(0x00000000, 0x00723456), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 40));
+    assertEquals(longFromBits(0x00000000, 0x00072345), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcde00), 44));
+    assertEquals(longFromBits(0x00000000, 0x00007234), LongLib.shr(
+        longFromBits(0x72345678, 0x9abcdef0), 48));
+    
+    assertEquals(longFromBits(0x00723456, 0x789abcde), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 8));
+    assertEquals(longFromBits(0x00007234, 0x56789abc), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 16));
+    assertEquals(longFromBits(0x00000072, 0x3456789a), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 24));
+    assertEquals(longFromBits(0x00000007, 0x23456789), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 28));
+    assertEquals(longFromBits(0x00000000, 0x72345678), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 32));
+    assertEquals(longFromBits(0x00000000, 0x07234567), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 36));
+    assertEquals(longFromBits(0x00000000, 0x00723456), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 40));
+    assertEquals(longFromBits(0x00000000, 0x00072345), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcde00), 44));
+    assertEquals(longFromBits(0x00000000, 0x00007234), LongLib.shru(
+        longFromBits(0x72345678, 0x9abcdef0), 48));
+    
+    assertEquals(longFromBits(0xff923456, 0x789abcde), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 8));
+    assertEquals(longFromBits(0xffff9234, 0x56789abc), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 16));
+    assertEquals(longFromBits(0xffffff92, 0x3456789a), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 24));
+    assertEquals(longFromBits(0xfffffff9, 0x23456789), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 28));
+    assertEquals(longFromBits(0xffffffff, 0x92345678), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 32));
+    assertEquals(longFromBits(0xffffffff, 0xf9234567), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 36));
+    assertEquals(longFromBits(0xffffffff, 0xff923456), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 40));
+    assertEquals(longFromBits(0xffffffff, 0xfff92345), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 44));
+    assertEquals(longFromBits(0xffffffff, 0xffff9234), LongLib.shr(
+        longFromBits(0x92345678, 0x9abcdef0), 48));
+    
+    assertEquals(longFromBits(0x00923456, 0x789abcde), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 8));
+    assertEquals(longFromBits(0x00009234, 0x56789abc), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 16));
+    assertEquals(longFromBits(0x00000092, 0x3456789a), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 24));
+    assertEquals(longFromBits(0x00000009, 0x23456789), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 28));
+    assertEquals(longFromBits(0x00000000, 0x92345678), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 32));
+    assertEquals(longFromBits(0x00000000, 0x09234567), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 36));
+    assertEquals(longFromBits(0x00000000, 0x00923456), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 40));
+    assertEquals(longFromBits(0x00000000, 0x00092345), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 44));
+    assertEquals(longFromBits(0x00000000, 0x00009234), LongLib.shru(
+        longFromBits(0x92345678, 0x9abcdef0), 48));
   }
 
   // Issue 1198, and also a good exercise of several methods.
   public void testToHexString() {
-    double[] deadbeaf12341234 = longFromBits(0xdeadbeaf, 0x12341234);
+    LongEmul deadbeaf12341234 = longFromBits(0xdeadbeaf, 0x12341234);
 
+    assertEquals("0", toHexString(Const.ZERO));
     assertEquals("deadbeaf12341234", toHexString(deadbeaf12341234));
   }
 
@@ -310,14 +462,14 @@
 
     int top = 922337201;
     int bottom = 967490662;
-    double[] fullnum = LongLib.add(LongLib.mul(LongLib.fromInt(1000000000),
+    LongEmul fullnum = LongLib.add(LongLib.mul(LongLib.fromInt(1000000000),
         LongLib.fromInt(top)), LongLib.fromInt(bottom));
 
     assertEquals("922337201967490662", LongLib.toString(fullnum));
     assertEquals("-922337201967490662", LongLib.toString(LongLib.neg(fullnum)));
   }
 
-  private double[] fact(double[] n) {
+  private LongEmul fact(LongEmul n) {
     if (LongLib.eq(n, LongLib.fromInt(0))) {
       return LongLib.fromInt(1);
     } else {
@@ -325,19 +477,18 @@
     }
   }
 
-  private double[] longFromBits(int top, int bottom) {
-    double[] topHalf = LongLib.shl(LongLib.fromInt(top), 32);
-    double[] bottomHalf = LongLib.fromInt(bottom);
+  private LongEmul longFromBits(int top, int bottom) {
+    LongEmul topHalf = LongLib.shl(LongLib.fromInt(top), 32);
+    LongEmul bottomHalf = LongLib.fromInt(bottom);
     if (LongLib.lt(bottomHalf, Const.ZERO)) {
       bottomHalf = LongLib.add(bottomHalf, LongLib.shl(LongLib.fromInt(1), 32));
     }
-    double[] total = LongLib.add(topHalf, bottomHalf);
+    LongEmul total = LongLib.add(topHalf, bottomHalf);
     return total;
   }
 
-  private String toHexString(double[] x) {
-    // copied from the GWT Long class and modified for double[]
-    double[] zero = LongLib.fromInt(0);
+  private String toHexString(LongEmul x) {
+    LongEmul zero = LongLib.fromInt(0);
 
     if (LongLib.eq(x, zero)) {
       return "0";
diff --git a/tools/api-checker/config/gwt20_21userApi.conf b/tools/api-checker/config/gwt20_21userApi.conf
index 1959dcd..43801d2 100644
--- a/tools/api-checker/config/gwt20_21userApi.conf
+++ b/tools/api-checker/config/gwt20_21userApi.conf
@@ -109,3 +109,32 @@
 
 # Removing HTTPRequest.
 com.google.gwt.user.client.HTTPRequest MISSING
+
+# Change signature for native long from double[2] to LongEmul{l=int,m=int,h=int} 
+com.google.gwt.lang.LongLib::RUN_IN_JVM MISSING
+com.google.gwt.lang.LongLib::add([D[D) MISSING
+com.google.gwt.lang.LongLib::and([D[D) MISSING
+com.google.gwt.lang.LongLib::compare([D[D) MISSING
+com.google.gwt.lang.LongLib::div([D[D) MISSING
+com.google.gwt.lang.LongLib::eq([D[D) MISSING
+com.google.gwt.lang.LongLib::fromDouble(D) RETURN_TYPE_ERROR  from double[] to java.lang.Object
+com.google.gwt.lang.LongLib::fromInt(I) RETURN_TYPE_ERROR  from double[] to java.lang.Object
+com.google.gwt.lang.LongLib::gt([D[D) MISSING
+com.google.gwt.lang.LongLib::gte([D[D) MISSING
+com.google.gwt.lang.LongLib::lt([D[D) MISSING
+com.google.gwt.lang.LongLib::lte([D[D) MISSING
+com.google.gwt.lang.LongLib::mod([D[D) MISSING
+com.google.gwt.lang.LongLib::mul([D[D) MISSING
+com.google.gwt.lang.LongLib::neg([D) MISSING
+com.google.gwt.lang.LongLib::neq([D[D) MISSING
+com.google.gwt.lang.LongLib::not([D) MISSING
+com.google.gwt.lang.LongLib::or([D[D) MISSING
+com.google.gwt.lang.LongLib::shl([DI) MISSING
+com.google.gwt.lang.LongLib::shr([DI) MISSING
+com.google.gwt.lang.LongLib::shru([DI) MISSING
+com.google.gwt.lang.LongLib::sub([D[D) MISSING
+com.google.gwt.lang.LongLib::toDouble([D) MISSING
+com.google.gwt.lang.LongLib::toInt([D) MISSING
+com.google.gwt.lang.LongLib::toString([D) MISSING
+com.google.gwt.lang.LongLib::typeChange(J) MISSING
+com.google.gwt.lang.LongLib::xor([D[D) MISSING
diff --git a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
index 83586a0..39e61eb 100644
--- a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
@@ -65,6 +65,7 @@
 
   @UnsafeNativeLong
   private static native long readLong0(JavaScriptObject obj, int idx) /*-{
+    // TODO (rice): use backwards-compatible wire format?
     return obj[idx];
   }-*/;
 
diff --git a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
index 83c69ee..2085fbc 100644
--- a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
@@ -197,8 +197,7 @@
       value.valueOf && (value = value.valueOf());
 
       // See if the value is our web-mode representation of a long
-      if (!value.@java.lang.Object::typeMarker && value.length && 
-          value.length == 2 && (typeof value[0] == 'number') && (typeof value[1] == 'number')) {
+      if (value.hasOwnProperty('l') && value.hasOwnProperty('m') && value.hasOwnProperty('h')) {
         type = 'long';
       }
     }
diff --git a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
index e8b0d20..5519572 100644
--- a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
+++ b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
@@ -43,7 +43,6 @@
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.SerializationStreamReader;
-import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -167,21 +166,21 @@
 
     @Override
     public void endVisit(LongValueCommand x, Context ctx) {
+      // TODO (rice): use backwards-compatible wire format?
       long fieldValue = x.getValue();
-
+      
       /*
-       * Client code represents longs internally as an array of two Numbers. In
-       * order to make serialization of longs faster, we'll send the component
-       * parts so that the value can be directly reconstituted on the client.
+       * Client code represents longs internally as an Object with numeric
+       * properties l, m, and h. In order to make serialization of longs faster,
+       * we'll send the component parts so that the value can be directly
+       * reconstituted on the client.
        */
-      double[] parts = AbstractSerializationStreamWriter.makeLongComponents(
-          (int) (fieldValue >> 32), (int) fieldValue);
-      assert parts.length == 2;
-      lbracket();
-      push(String.valueOf(parts[0]));
-      comma();
-      push(String.valueOf(parts[1]));
-      rbracket();
+      int l = (int) (fieldValue & 0x3fffff);
+      int m = (int) ((fieldValue >> 22) & 0x3fffff);
+      int h = (int) ((fieldValue >> 44) & 0xfffff);
+      // CHECKSTYLE_OFF
+      push("{l:" + l + ",m:" + m + ",h:" + h + "}");
+      // CHECKSTYLE_ON
     }
 
     @Override
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
index 2ed7015..3766b5d 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
@@ -27,6 +27,59 @@
  */
 public abstract class AbstractSerializationStreamReader extends
     AbstractSerializationStream implements SerializationStreamReader {
+  
+  private static final double TWO_PWR_15_DBL = 0x8000;
+  private static final double TWO_PWR_16_DBL = 0x10000;
+  private static final double TWO_PWR_22_DBL = 0x400000;
+  private static final double TWO_PWR_31_DBL = TWO_PWR_16_DBL * TWO_PWR_15_DBL;
+  private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
+  private static final double TWO_PWR_44_DBL = TWO_PWR_22_DBL * TWO_PWR_22_DBL;
+  private static final double TWO_PWR_63_DBL = TWO_PWR_32_DBL * TWO_PWR_31_DBL;
+
+  /**
+   * Return a long from a pair of doubles { low, high } such that the
+   * actual value is equal to high + low.
+   */
+  public static long fromDoubles(double lowDouble, double highDouble) {
+    long high = fromDouble(highDouble);
+    long low = fromDouble(lowDouble);
+    return high + low;
+  }
+
+  private static long fromDouble(double value) {
+    if (Double.isNaN(value)) {
+      return 0L;
+    }
+    if (value < -TWO_PWR_63_DBL) {
+      return Long.MIN_VALUE;
+    }
+    if (value >= TWO_PWR_63_DBL) {
+      return Long.MAX_VALUE;
+    }
+  
+    boolean negative = false;
+    if (value < 0) {
+      negative = true;
+      value = -value;
+    }
+    int a2 = 0;
+    if (value >= TWO_PWR_44_DBL) {
+      a2 = (int) (value / TWO_PWR_44_DBL);
+      value -= a2 * TWO_PWR_44_DBL;
+    }
+    int a1 = 0;
+    if (value >= TWO_PWR_22_DBL) {
+      a1 = (int) (value / TWO_PWR_22_DBL);
+      value -= a1 * TWO_PWR_22_DBL;
+    }
+    int a0 = (int) value;
+    
+    long result = ((long) a2 << 44) | ((long) a1 << 22) | a0;
+    if (negative) {
+      result = -result;
+    }
+    return result;
+  }
 
   private ArrayList<Object> seenArray = new ArrayList<Object>();
 
@@ -96,5 +149,4 @@
     // index is 1-based
     return seenArray.size();
   }
-
 }
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index 52e75e4..b245553 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -33,22 +33,17 @@
 public abstract class AbstractSerializationStreamWriter extends
     AbstractSerializationStream implements SerializationStreamWriter {
 
-  // Keep synchronized with LongLib
   private static final double TWO_PWR_16_DBL = 0x10000;
-
-  // Keep synchronized with LongLib
   private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
 
   /**
-   * Make an instance equivalent to stringing highBits next to lowBits, where
-   * highBits and lowBits are assumed to be in 32-bit twos-complement notation.
-   * As a result, for any double[] l, the following identity holds:
-   * 
-   * <blockquote> <code>l == makeFromBits(l.highBits(), l.lowBits())</code>
-   * </blockquote>
+   * Return a pair of doubles { low, high } that add up to the given number,
+   * such that "low" is always between 0 and 2^32-1 inclusive and "high" is
+   * always between -2^63 and 2^63-2^32 inclusive and is a multiple of 2^32.
    */
-  // Keep synchronized with LongLib
-  public static double[] makeLongComponents(int highBits, int lowBits) {
+  public static double[] getAsDoubleArray(long value) {
+    int lowBits = (int) (value & 0xffffffff);
+    int highBits = (int) (value >> 32);
     double high = highBits * TWO_PWR_32_DBL;
     double low = lowBits;
     if (lowBits < 0) {
@@ -211,5 +206,4 @@
    */
   protected abstract void serialize(Object instance, String typeSignature)
       throws SerializationException;
-
 }
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index 748f4b3..f1bb0e1 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.SerializationException;
 
@@ -35,11 +34,6 @@
     return array.length;
   }-*/;
 
-  @UnsafeNativeLong
-  private static native long readLong0(double low, double high) /*-{
-    return [low, high];
-  }-*/;
-
   int index;
 
   JavaScriptObject results;
@@ -92,10 +86,12 @@
   }-*/;
 
   public long readLong() {
+    double low = readDouble();
+    double high = readDouble();
     if (GWT.isScript()) {
-      return readLong0(readDouble(), readDouble());
+      return fromDoubles(low, high);
     } else {
-      return (long) readDouble() + (long) readDouble();
+      return (long) low + (long) high;
     }
   }
 
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index 291e877..584d695 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -15,9 +15,7 @@
  */
 package com.google.gwt.user.client.rpc.impl;
 
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.SerializationException;
 
 import java.util.List;
@@ -102,12 +100,6 @@
     }
   }-*/;
 
-  @UnsafeNativeLong
-  // Keep synchronized with LongLib
-  private static native double[] makeLongComponents0(long value) /*-{
-    return value;
-  }-*/;
-
   private StringBuffer encodeBuffer;
 
   private final String moduleBaseURL;
@@ -157,18 +149,9 @@
 
   @Override
   public void writeLong(long fieldValue) {
-    /*
-     * Client code represents longs internally as an array of two Numbers. In
-     * order to make serialization of longs faster, we'll send the component
-     * parts so that the value can be directly reconstituted on the server.
-     */
-    double[] parts;
-    if (GWT.isScript()) {
-      parts = makeLongComponents0(fieldValue);
-    } else {
-      parts = makeLongComponents((int) (fieldValue >> 32), (int) fieldValue);
-    }
-    assert parts.length == 2;
+    // Write longs as a pair of doubles for backwards compatibility
+    double[] parts = getAsDoubleArray(fieldValue);
+    assert parts != null && parts.length == 2;
     writeDouble(parts[0]);
     writeDouble(parts[1]);
   }
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 7bba2cb..3c21899 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -555,16 +555,11 @@
 
     return stream.toString();
   }
-
+  
   public void writeLong(long fieldValue) {
-    /*
-     * Client code represents longs internally as an array of two Numbers. In
-     * order to make serialization of longs faster, we'll send the component
-     * parts so that the value can be directly reconstituted on the client.
-     */
-    double[] parts = makeLongComponents((int) (fieldValue >> 32),
-        (int) fieldValue);
-    assert parts.length == 2;
+    // Write longs as a pair of doubles for backwards compatibility
+    double[] parts = getAsDoubleArray(fieldValue);
+    assert parts != null && parts.length == 2;
     writeDouble(parts[0]);
     writeDouble(parts[1]);
   }
diff --git a/user/super/com/google/gwt/emul/java/lang/Integer.java b/user/super/com/google/gwt/emul/java/lang/Integer.java
index aa4ed50..6a44ab6 100644
--- a/user/super/com/google/gwt/emul/java/lang/Integer.java
+++ b/user/super/com/google/gwt/emul/java/lang/Integer.java
@@ -88,12 +88,37 @@
   }
 
   public static int numberOfLeadingZeros(int i) {
+    // Based on Henry S. Warren, Jr: "Hacker's Delight", p. 80.
     if (i < 0) {
       return 0;
     } else if (i == 0) {
       return SIZE;
     } else {
-      return SIZE - 1 - (int) Math.floor(Math.log(i) / Math.log(2.0d));
+      int y, m, n;
+
+      y = -(i >> 16);
+      m = (y >> 16) & 16;
+      n = 16 - m;
+      i = i >> m;
+
+      y = i - 0x100;
+      m = (y >> 16) & 8;
+      n += m;
+      i <<= m;
+
+      y = i - 0x1000;
+      m = (y >> 16) & 4;
+      n += m;
+      i <<= m;
+
+      y = i - 0x4000;
+      m = (y >> 16) & 2;
+      n += m; 
+      i <<= m;
+
+      y = i >> 14;
+      m = y & ~(y >> 1);
+      return n + 2 - m;
     }
   }
 
diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java
index 913fab7..0971d94 100644
--- a/user/super/com/google/gwt/emul/java/lang/Long.java
+++ b/user/super/com/google/gwt/emul/java/lang/Long.java
@@ -103,62 +103,11 @@
   public static long parseLong(String s) throws NumberFormatException {
     return parseLong(s, 10);
   }
-
-  public static long parseLong(String orig, int intRadix)
-      throws NumberFormatException {
-    if (orig == null) {
-      throw new NumberFormatException("null");
-    }
-    if (orig.length() == 0) {
-      throw NumberFormatException.forInputString(orig);
-    }
-
-    if (intRadix < Character.MIN_RADIX || intRadix > Character.MAX_RADIX) {
-      throw new NumberFormatException("radix " + intRadix + " out of range");
-    }
-
-    boolean neg = false;
-    String s;
-    if (orig.charAt(0) == '-') {
-      neg = true;
-      s = orig.substring(1);
-      if (s.equals("")) { // orig = "-"
-        throw NumberFormatException.forInputString(orig);
-      }
-    } else {
-      s = orig;
-    }
-
-    long result = 0;
-    if (intRadix == 16) {
-      result = parseHex(s);
-    } else {
-      // Cache a converted version for performance (pure long ops are faster).
-      long radix = intRadix;
-      for (int i = 0, len = s.length(); i < len; ++i) {
-        if (result < 0) {
-          throw NumberFormatException.forInputString(s);
-        }
-        result *= radix;
-        char c = s.charAt(i);
-        int value = Character.digit(c, intRadix);
-        if (value < 0) {
-          throw NumberFormatException.forInputString(s);
-        }
-        result += value;
-      }
-    }
-
-    if (result < 0 && result != MIN_VALUE) {
-      throw NumberFormatException.forInputString(s);
-    }
-    if (neg) {
-      return -result;
-    } else {
-      return result;
-    }
+  
+  public static long parseLong(String s, int radix) throws NumberFormatException {
+    return __parseAndValidateLong(s, radix);
   }
-
+ 
   public static long reverse(long i) {
     int high = (int) (i >>> 32);
     int low = (int) i;
@@ -377,5 +326,4 @@
   public String toString() {
     return toString(value);
   }
-
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Number.java b/user/super/com/google/gwt/emul/java/lang/Number.java
index 810df15..32ada57 100644
--- a/user/super/com/google/gwt/emul/java/lang/Number.java
+++ b/user/super/com/google/gwt/emul/java/lang/Number.java
@@ -53,6 +53,89 @@
   }
 
   /**
+   * Use nested class to avoid clinit on outer.
+   */
+  static class __ParseLong {
+    /**
+     * The number of digits (excluding minus sign and leading zeros) to process
+     * at a time.  The largest value expressible in maxDigits digits as well as
+     * the factor radix^maxDigits must be strictly less than 2^31.
+     */
+    private static final int[] maxDigitsForRadix = {-1, -1, // unused
+      30, // base 2
+      19, // base 3
+      15, // base 4
+      13, // base 5
+      11, 11, // base 6-7
+      10, // base 8
+      9, 9, // base 9-10
+      8, 8, 8, 8, // base 11-14
+      7, 7, 7, 7, 7, 7, 7, // base 15-21
+      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // base 22-35
+      5 // base 36
+    };
+  
+    /**
+     * A table of values radix*maxDigitsForRadix[radix].
+     */
+    private static final int[] maxDigitsRadixPower = new int[37];
+  
+    /**
+     * The largest number of digits (excluding minus sign and leading zeros) that
+     * can fit into a long for a given radix between 2 and 36, inclusive.
+     */
+    private static final int[] maxLengthForRadix = {-1, -1, // unused
+      63, // base 2
+      40, // base 3
+      32, // base 4
+      28, // base 5
+      25, // base 6
+      23, // base 7
+      21, // base 8
+      20, // base 9
+      19, // base 10
+      19, // base 11
+      18, // base 12
+      18, // base 13
+      17, // base 14
+      17, // base 15
+      16, // base 16
+      16, // base 17
+      16, // base 18
+      15, // base 19
+      15, // base 20
+      15, // base 21
+      15, // base 22
+      14, // base 23
+      14, // base 24
+      14, // base 25
+      14, // base 26
+      14, // base 27
+      14, // base 28
+      13, // base 29
+      13, // base 30
+      13, // base 31
+      13, // base 32
+      13, // base 33
+      13, // base 34
+      13, // base 35
+      13  // base 36
+    };
+  
+    /**
+     * A table of floor(MAX_VALUE / maxDigitsRadixPower).
+     */
+    private static final long[] maxValueForRadix = new long[37];
+  
+    static {
+      for (int i = 2; i <= 36; i++) {
+        maxDigitsRadixPower[i] = (int) Math.pow(i, maxDigitsForRadix[i]);
+        maxValueForRadix[i] = Long.MAX_VALUE / maxDigitsRadixPower[i];
+      }
+    }
+  }
+
+  /**
    * @skip
    * 
    * This function will determine the radix that the string is expressed in
@@ -111,7 +194,7 @@
 
     return toReturn;
   }
-
+  
   /**
    * @skip
    * 
@@ -146,7 +229,104 @@
 
     return toReturn;
   }
+  
+  /**
+   * @skip
+   * 
+   * This function contains common logic for parsing a String in a given radix
+   * and validating the result.
+   */
+  protected static long __parseAndValidateLong(String s, int radix)
+      throws NumberFormatException {
+    
+    if (s == null) {
+      throw new NumberFormatException("null");
+    }
+    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+      throw new NumberFormatException("radix " + radix + " out of range");
+    }
 
+    int length = s.length();
+    boolean negative = (length > 0) && (s.charAt(0) == '-');
+    if (negative) {
+      s = s.substring(1);
+      length--;
+    }
+    if (length == 0) {
+      throw NumberFormatException.forInputString(s);
+    }
+
+    // Strip leading zeros
+    while (s.length() > 0 && s.charAt(0) == '0') {
+      s = s.substring(1);
+      length--;
+    }
+    
+    // Immediately eject numbers that are too long -- this avoids more complex
+    // overflow handling below
+    if (length > __ParseLong.maxLengthForRadix[radix]) {
+      throw NumberFormatException.forInputString(s);
+    }
+    
+    // Validate the digits
+    int maxNumericDigit = '0' + Math.min(radix, 10);
+    int maxLowerCaseDigit = radix + 'a' - 10;
+    int maxUpperCaseDigit = radix + 'A' - 10;
+    for (int i = 0; i < length; i++) {
+      char c = s.charAt(i);
+      if (c >= '0' && c < maxNumericDigit) {
+        continue;
+      }
+      if (c >= 'a' && c < maxLowerCaseDigit) {
+        continue;
+      }
+      if (c >= 'A' && c < maxUpperCaseDigit) {
+        continue;
+      }
+      throw NumberFormatException.forInputString(s);
+    }
+
+    long toReturn = 0;
+    int maxDigits = __ParseLong.maxDigitsForRadix[radix];
+    long radixPower = __ParseLong.maxDigitsRadixPower[radix];
+    long maxValue = __ParseLong.maxValueForRadix[radix];
+    
+    boolean firstTime = true;
+    int head = length % maxDigits;
+    if (head > 0) {
+      toReturn = __parseInt(s.substring(0, head), radix);
+      s = s.substring(head);
+      length -= head;
+      firstTime = false;
+    }
+    
+    while (length >= maxDigits) {
+      head = __parseInt(s.substring(0, maxDigits), radix);
+      s = s.substring(maxDigits);
+      length -= maxDigits;
+      if (!firstTime) {
+        // Check whether multiplying by radixPower will overflow
+        if (toReturn > maxValue) {
+          throw new NumberFormatException(s);
+        }
+        toReturn *= radixPower;      
+      } else {
+        firstTime = false;
+      }
+      toReturn += head;
+    }
+    
+    // A negative value means we overflowed Long.MAX_VALUE
+    if (toReturn < 0) {
+      throw NumberFormatException.forInputString(s);
+    }
+    
+    if (negative) {
+      toReturn = -toReturn;
+    }
+    return toReturn;
+  }
+  
   /**
    * @skip
    */
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java
index 89dcc33..662a202 100644
--- a/user/super/com/google/gwt/emul/java/util/Arrays.java
+++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -1345,7 +1345,7 @@
    */
   @UnsafeNativeLong
   private static native void nativeLongSort(Object array) /*-{
-    array.sort(@com.google.gwt.lang.LongLib::compare([D[D));
+    array.sort(@com.google.gwt.lang.LongLib::compare(Lcom/google/gwt/lang/LongEmul;Lcom/google/gwt/lang/LongEmul;));
   }-*/;
 
   /**
@@ -1355,7 +1355,7 @@
   private static native void nativeLongSort(Object array, int fromIndex,
       int toIndex) /*-{
     var temp = array.slice(fromIndex, toIndex);
-    temp.sort(@com.google.gwt.lang.LongLib::compare([D[D));
+    temp.sort(@com.google.gwt.lang.LongLib::compare(Lcom/google/gwt/lang/LongEmul;Lcom/google/gwt/lang/LongEmul;));
     var n = toIndex - fromIndex;
     // Do the equivalent of array.splice(fromIndex, n, temp) except
     // flattening the temp slice.
diff --git a/user/test/com/google/gwt/langtest/LongLibGwtTest.java b/user/test/com/google/gwt/langtest/LongLibGwtTest.java
index 66225de..1c6e4b6 100644
--- a/user/test/com/google/gwt/langtest/LongLibGwtTest.java
+++ b/user/test/com/google/gwt/langtest/LongLibGwtTest.java
@@ -15,9 +15,7 @@
  */
 package com.google.gwt.langtest;
 
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.junit.client.GWTTestCase;
-import com.google.gwt.lang.LongLib;
 import com.google.gwt.lang.LongLibTestBase;
 
 /**
@@ -25,12 +23,6 @@
  */
 public class LongLibGwtTest extends GWTTestCase {
 
-  static {
-    if (!GWT.isScript()) {
-      LongLib.RUN_IN_JVM = true;
-    }
-  }
-
   private LongLibTestBase impl = new LongLibTestBase();
 
   @Override
@@ -70,6 +62,10 @@
     impl.testMinMax();
   }
 
+  public void testMod() {
+    impl.testMod();
+  }
+
   public void testMultiplicative() {
     impl.testMultiplicative();
   }
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
index 9ae14ed..4831bfd 100644
--- a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
+++ b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
@@ -493,9 +493,21 @@
   }
 
   public static Long[] createLongArray() {
+    long a = 16123432898849345L;
+    long b = 78234569989880099L;
+    long c = -64289238928934943L;
+    
+    // Create values that are not compile-time constants
+    for (int i = 0; i < 10; i++) {
+      a ^= b;
+      b ^= c;
+      c ^= a;
+    }
+    
     return new Long[] {
         new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE),
-        new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE)};
+        new Long(Long.MAX_VALUE), new Long(Long.MIN_VALUE),
+        new Long(a), new Long(b), new Long(c)};
   }
 
   public static LinkedHashMap<String, MarkerTypeLinkedHashMap> createLRULinkedHashMap() {
@@ -541,8 +553,20 @@
   }
 
   public static long[] createPrimitiveLongArray() {
+    long a = 16123432898849345L;
+    long b = 78234569989880099L;
+    long c = -64289238928934943L;
+    
+    // Create values that are not compile-time constants
+    for (int i = 0; i < 10; i++) {
+      a ^= b;
+      b ^= c;
+      c ^= a;
+    }
+    
     return new long[] {
-        Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE};
+        Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE,
+        a, b, c};
   }
 
   public static short[] createPrimitiveShortArray() {