Fix a couple of bugs in BigDecimal that were corrected in the
gwt-java-math project.

Public review at: http://gwt-code-reviews.appspot.com/153814/show

Patch by: jat, richard@zschech.net
Review by: rice


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7644 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/math/BigDecimal.java b/user/super/com/google/gwt/emul/java/math/BigDecimal.java
index 569918e..8420da2 100644
--- a/user/super/com/google/gwt/emul/java/math/BigDecimal.java
+++ b/user/super/com/google/gwt/emul/java/math/BigDecimal.java
@@ -48,6 +48,11 @@
     Serializable {
 
   /**
+   * One more than the number of bits which can be stored in {@link #smallValue}.
+   */
+  private static final int SMALL_VALUE_BITS = 54;
+
+  /**
    * The constant one as a {@code BigDecimal}.
    */
   public static final BigDecimal ONE = new BigDecimal(1, 0);
@@ -158,7 +163,8 @@
   private static final double[] DOUBLE_TEN_POW = new double[] {
       1D, 10D, 100D, 1000D, 10000D, 100000D, 1000000D, 10000000D, 100000000D,
       1000000000D, 10000000000D, 100000000000D, 1000000000000D,
-      10000000000000D, 100000000000000D,};
+      10000000000000D, 100000000000000D, 1000000000000000D,
+      10000000000000000D,};
 
   private static final int[] DOUBLE_TEN_POW_BIT_LENGTH = new int[DOUBLE_TEN_POW.length];
 
@@ -279,7 +285,8 @@
       BigDecimal augend, double diffScale) {
     if (diffScale < DOUBLE_TEN_POW.length
         && Math.max(thisValue.bitLength, augend.bitLength
-            + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1 < 54) {
+            + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1
+            < SMALL_VALUE_BITS) {
       return valueOf(thisValue.smallValue + augend.smallValue
           * DOUBLE_TEN_POW[(int) diffScale], thisValue.scale);
     }
@@ -317,7 +324,7 @@
     }
     int sign = scaledDividend.signum() * scaledDivisor.signum();
     int compRem; // 'compare to remainder'
-    if (scaledDivisor.bitLength() < 54) {
+    if (scaledDivisor.bitLength() < SMALL_VALUE_BITS) {
       long rem = remainder.longValue();
       long divisor = scaledDivisor.longValue();
       compRem = longCompareTo(Math.abs(rem) << 1, Math.abs(divisor));
@@ -332,7 +339,7 @@
           * (5 + compRem), roundingMode);
     }
     if (compRem != 0) {
-      if (quotient.bitLength() < 54) {
+      if (quotient.bitLength() < SMALL_VALUE_BITS) {
         return valueOf(quotient.longValue() + compRem, scale);
       }
       quotient = quotient.add(BigInteger.valueOf(compRem));
@@ -598,6 +605,10 @@
 
   private double scale;
 
+  /**
+   * The unscaled integer value (stored in a double) if the number of bits is
+   * less than {@link #SMALL_VALUE_BITS}.
+   */
   private transient double smallValue;
 
   /**
@@ -884,7 +895,7 @@
   private BigDecimal(long smallValue, int scale) {
     this.scale = scale;
     this.bitLength = bitLength(smallValue);
-    if (bitLength < 54) {
+    if (bitLength < SMALL_VALUE_BITS) {
       this.smallValue = smallValue;
     } else {
       this.intVal = BigInteger.valueOf(smallValue);
@@ -939,7 +950,7 @@
     // Let be: this = [u1,s1] and augend = [u2,s2]
     if (diffScale == 0) {
       // case s1 == s2: [u1 + u2 , s1]
-      if (Math.max(this.bitLength, augend.bitLength) + 1 < 54) {
+      if (Math.max(this.bitLength, augend.bitLength) + 1 < SMALL_VALUE_BITS) {
         return valueOf(this.smallValue + augend.smallValue, this.scale);
       }
       return new BigDecimal(this.getUnscaledValue().add(
@@ -1037,7 +1048,8 @@
     int valueSign = val.signum();
 
     if (thisSign == valueSign) {
-      if (this.scale == val.scale && this.bitLength < 54 && val.bitLength < 54) {
+      if (this.scale == val.scale && this.bitLength < SMALL_VALUE_BITS
+          && val.bitLength < SMALL_VALUE_BITS) {
         return (smallValue < val.smallValue) ? -1
             : (smallValue > val.smallValue) ? 1 : 0;
       }
@@ -1214,20 +1226,23 @@
     }
 
     double diffScale = this.scale - divisor.scale - scale;
-    if (this.bitLength < 54 && divisor.bitLength < 54) {
+    if (this.bitLength < SMALL_VALUE_BITS
+        && divisor.bitLength < SMALL_VALUE_BITS) {
       if (diffScale == 0) {
         return dividePrimitiveLongs((long) this.smallValue,
             (long) divisor.smallValue, scale, roundingMode);
       } else if (diffScale > 0) {
         if (diffScale < DOUBLE_TEN_POW.length
-            && divisor.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale] < 54) {
+            && divisor.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[
+                (int) diffScale] < SMALL_VALUE_BITS) {
           return dividePrimitiveLongs((long) this.smallValue,
               (long) (divisor.smallValue * DOUBLE_TEN_POW[(int) diffScale]),
               scale, roundingMode);
         }
       } else { // diffScale < 0
         if (-diffScale < DOUBLE_TEN_POW.length
-            && this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -diffScale] < 54) {
+            && this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -diffScale]
+                < SMALL_VALUE_BITS) {
           return dividePrimitiveLongs(
               (long) (this.smallValue * DOUBLE_TEN_POW[(int) -diffScale]),
               (long) divisor.smallValue, scale, roundingMode);
@@ -1611,7 +1626,7 @@
     if (x instanceof BigDecimal) {
       BigDecimal x1 = (BigDecimal) x;
       return x1.scale == scale
-          && (bitLength < 54 ? (x1.smallValue == smallValue)
+          && (bitLength < SMALL_VALUE_BITS ? (x1.smallValue == smallValue)
               : intVal.equals(x1.intVal));
     }
     return false;
@@ -1766,7 +1781,7 @@
     if (hashCode != 0) {
       return hashCode;
     }
-    if (bitLength < 54) {
+    if (bitLength < SMALL_VALUE_BITS) {
       long longValue = (long) smallValue;
       hashCode = (int) (longValue & 0xffffffff);
       hashCode = 33 * hashCode + (int) ((longValue >> 32) & 0xffffffff);
@@ -1915,7 +1930,7 @@
      * Let be: this = [u1,s1] and multiplicand = [u2,s2] so: this x multiplicand
      * = [ s1 * s2 , s1 + s2 ]
      */
-    if (this.bitLength + multiplicand.bitLength < 54) {
+    if (this.bitLength + multiplicand.bitLength < SMALL_VALUE_BITS) {
       return valueOf(this.smallValue * multiplicand.smallValue,
           toIntScale(newScale));
     }
@@ -1947,7 +1962,7 @@
    * @return {@code -this}
    */
   public BigDecimal negate() {
-    if (bitLength < 54) {
+    if (bitLength < SMALL_VALUE_BITS) {
       return valueOf(-smallValue, scale);
     }
     return new BigDecimal(getUnscaledValue().negate(), scale);
@@ -2079,17 +2094,15 @@
     if (precision > 0) {
       return precision;
     }
-    int decimalDigits = 1; // the precision to be calculated
+    double decimalDigits = 1; // the precision to be calculated
     double doubleUnsc = 1; // intVal in 'double'
 
-    if (bitLength < 1024) {
+    if (bitLength < SMALL_VALUE_BITS) {
       // To calculate the precision for small numbers
-      if (bitLength >= 54) {
-        doubleUnsc = getUnscaledValue().doubleValue();
-      } else if (bitLength >= 1) {
+      if (bitLength >= 1) {
         doubleUnsc = smallValue;
       }
-      decimalDigits += (int) Math.log10(Math.abs(doubleUnsc));
+      decimalDigits += Math.log10(Math.abs(doubleUnsc));
     } else {
       // (bitLength >= 1024)
       /*
@@ -2102,7 +2115,7 @@
         decimalDigits++;
       }
     }
-    precision = decimalDigits;
+    precision = (int) decimalDigits;
     return precision;
   }
 
@@ -2190,7 +2203,7 @@
    */
   public BigDecimal scaleByPowerOfTen(int n) {
     double newScale = scale - n;
-    if (bitLength < 54) {
+    if (bitLength < SMALL_VALUE_BITS) {
       // Taking care when a 0 is to be scaled
       if (smallValue == 0) {
         return zeroScaledBy(newScale);
@@ -2271,7 +2284,8 @@
     if (diffScale > 0) {
       // return [u * 10^(s2 - s), newScale]
       if (diffScale < DOUBLE_TEN_POW.length
-          && (this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) < 54) {
+          && (this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[
+              (int) diffScale]) < SMALL_VALUE_BITS) {
         return valueOf(this.smallValue * DOUBLE_TEN_POW[(int) diffScale],
             newScale);
       }
@@ -2280,7 +2294,8 @@
     }
     // diffScale < 0
     // return [u,s] / [1,newScale] with the appropriate scale and rounding
-    if (this.bitLength < 54 && -diffScale < DOUBLE_TEN_POW.length) {
+    if (this.bitLength < SMALL_VALUE_BITS
+        && -diffScale < DOUBLE_TEN_POW.length) {
       return dividePrimitiveLongs((long) this.smallValue,
           (long) DOUBLE_TEN_POW[(int) -diffScale], newScale, roundingMode);
     }
@@ -2308,7 +2323,7 @@
    *         {@code 1} if {@code this > 0}.
    */
   public int signum() {
-    if (bitLength < 54) {
+    if (bitLength < SMALL_VALUE_BITS) {
       return this.smallValue < 0 ? -1 : this.smallValue > 0 ? 1 : 0;
     }
     return getUnscaledValue().signum();
@@ -2385,7 +2400,8 @@
     // Let be: this = [u1,s1] and subtrahend = [u2,s2] so:
     if (diffScale == 0) {
       // case s1 = s2 : [u1 - u2 , s1]
-      if (Math.max(this.bitLength, subtrahend.bitLength) + 1 < 54) {
+      if (Math.max(this.bitLength, subtrahend.bitLength) + 1
+          < SMALL_VALUE_BITS) {
         return valueOf(this.smallValue - subtrahend.smallValue, this.scale);
       }
       return new BigDecimal(this.getUnscaledValue().subtract(
@@ -2394,7 +2410,8 @@
       // case s1 > s2 : [ u1 - u2 * 10 ^ (s1 - s2) , s1 ]
       if (diffScale < DOUBLE_TEN_POW.length
           && Math.max(this.bitLength, subtrahend.bitLength
-              + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1 < 54) {
+              + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1
+              < SMALL_VALUE_BITS) {
         return valueOf(this.smallValue - subtrahend.smallValue
             * DOUBLE_TEN_POW[(int) diffScale], this.scale);
       }
@@ -2407,7 +2424,7 @@
       if (diffScale < DOUBLE_TEN_POW.length
           && Math.max(this.bitLength
               + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale],
-              subtrahend.bitLength) + 1 < 54) {
+              subtrahend.bitLength) + 1 < SMALL_VALUE_BITS) {
         return valueOf(this.smallValue * DOUBLE_TEN_POW[(int) diffScale]
             - subtrahend.smallValue, subtrahend.scale);
       }
@@ -2746,9 +2763,6 @@
     String scaleString = null; // buffer for scale
     StringBuilder unscaledBuffer; // buffer for unscaled value
 
-    if (val == null) {
-      throw new NullPointerException();
-    }
     unscaledBuffer = new StringBuilder(val.length());
     // To skip a possible '+' symbol
     if ((offset <= last) && (val.charAt(offset) == '+')) {
@@ -2822,8 +2836,13 @@
       setUnscaledValue(new BigInteger(unscaled));
     }
     precision = unscaledBuffer.length() - counter;
-    if (unscaledBuffer.charAt(0) == '-') {
-      precision--;
+    // Don't count leading zeros in the precision
+    for (int i = 0; i < unscaledBuffer.length(); ++i) {
+      char ch = unscaledBuffer.charAt(i);
+      if (ch != '-' && ch != '0') {
+        break;
+      }
+      --precision;
     }
   }
 
@@ -2836,7 +2855,7 @@
    */
   private void inplaceRound(MathContext mc) {
     int mcPrecision = mc.getPrecision();
-    if (approxPrecision() - mcPrecision <= 0 || mcPrecision == 0) {
+    if (approxPrecision() - mcPrecision < 0 || mcPrecision == 0) {
       return;
     }
     int discardedPrecision = precision() - mcPrecision;
@@ -2845,7 +2864,7 @@
       return;
     }
     // When the number is small perform an efficient rounding
-    if (this.bitLength < 54) {
+    if (this.bitLength < SMALL_VALUE_BITS) {
       smallRound(mc, discardedPrecision);
       return;
     }
@@ -2893,13 +2912,14 @@
      * movePointRight(-n) since -Integer.MIN_VALUE == Integer.MIN_VALUE
      */
     if (newScale >= 0) {
-      if (bitLength < 54) {
+      if (bitLength < SMALL_VALUE_BITS) {
         return valueOf(smallValue, toIntScale(newScale));
       }
       return new BigDecimal(getUnscaledValue(), toIntScale(newScale));
     }
     if (-newScale < DOUBLE_TEN_POW.length
-        && bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -newScale] < 54) {
+        && bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -newScale]
+            < SMALL_VALUE_BITS) {
       return valueOf(smallValue * DOUBLE_TEN_POW[(int) -newScale], 0);
     }
     return new BigDecimal(Multiplication.multiplyByTenPow(getUnscaledValue(),
@@ -2909,7 +2929,7 @@
   private void setUnscaledValue(BigInteger unscaledValue) {
     this.intVal = unscaledValue;
     this.bitLength = unscaledValue.bitLength();
-    if (this.bitLength < 54) {
+    if (this.bitLength < SMALL_VALUE_BITS) {
       this.smallValue = unscaledValue.longValue();
     }
   }
diff --git a/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java b/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
index 99ae798..a0a7fde 100644
--- a/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
+++ b/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
@@ -1513,6 +1513,32 @@
   }
 
   /**
+   * Test gwt-java-math issues 4 and 5.
+   */
+  public void testRoundMathContextCEILING() {
+    BigDecimal val = BigDecimal.valueOf(1.5);
+    BigDecimal result = val.round(new MathContext(1, RoundingMode.CEILING));
+    assertEquals("2", result.toString());
+    assertEquals(0, result.scale());
+    assertEquals(1, result.precision());
+    
+    // 1 digit left of dp, 14 scale + 1
+    val = BigDecimal.valueOf(5.43445663479765);
+    val.setScale(val.scale() + 1, RoundingMode.CEILING).round(new
+        MathContext(1, RoundingMode.CEILING));
+
+     // 1 digit left of dp, 13 scale + 2
+    val = BigDecimal.valueOf(5.4344566347976);
+    val.setScale(val.scale() + 2, RoundingMode.CEILING).round(new
+        MathContext(1, RoundingMode.CEILING));
+    
+    // 2 digits left of dp, 13 scale + 2
+    BigDecimal test = BigDecimal.valueOf(12.4344566347976);
+    test.setScale(test.scale() + 1, RoundingMode.CEILING).round(new
+        MathContext(1, RoundingMode.CEILING));
+  }
+
+  /**
    * round(BigDecimal, MathContext)
    */
   public void testRoundMathContextHALF_DOWN() {
diff --git a/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java b/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
index 66fe6ba..dea49e7 100644
--- a/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
+++ b/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
@@ -666,6 +666,14 @@
   }
 
   /**
+   * Test second failing example in gwt-java-math issue 4.
+   */
+  public void testConstrStringWithLeadingZeros() {
+    BigDecimal value = new BigDecimal("-000.1");
+    assertEquals("bad precision", 1, value.precision());
+  }
+
+  /**
    * new BigDecimal(String value); value does not contain exponent.
    */
   public void testConstrStringWithoutExpNeg() {