Make Bigint instances immutable by removing all setters.
This required rewriting a large portion of bigint.dart.

R=rmacnak@google.com

Review URL: https://codereview.chromium.org//842033005

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@43492 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/lib/bigint.dart b/runtime/lib/bigint.dart
index a22bb51..07c6d80 100644
--- a/runtime/lib/bigint.dart
+++ b/runtime/lib/bigint.dart
@@ -38,91 +38,88 @@
  * and disclaimer.
  */
 
+// A big integer number is represented by a sign, an array of 32-bit unsigned
+// integers in little endian format, and a number of used digits in that array.
+// The code makes sure that an even number of digits is always accessible and
+// meaningful, so that pairs of digits can be processed as 64-bit unsigned
+// numbers on a 64-bit platform. This requires the initialization of a leading
+// zero if the number of used digits is odd.
 class _Bigint extends _IntegerImplementation implements int {
   // Bits per digit.
-  static const int DIGIT_BITS = 32;
-  static const int DIGIT_BASE = 1 << DIGIT_BITS;
-  static const int DIGIT_MASK = (1 << DIGIT_BITS) - 1;
+  static const int _DIGIT_BITS = 32;
+  static const int _DIGIT_BASE = 1 << _DIGIT_BITS;
+  static const int _DIGIT_MASK = (1 << _DIGIT_BITS) - 1;
 
   // Bits per half digit.
-  static const int DIGIT2_BITS = DIGIT_BITS >> 1;
-  static const int DIGIT2_MASK = (1 << DIGIT2_BITS) - 1;
-
-  // Allocate extra digits so the bigint can be reused.
-  static const int EXTRA_DIGITS = 4;
+  static const int _DIGIT2_BITS = _DIGIT_BITS >> 1;
+  static const int _DIGIT2_MASK = (1 << _DIGIT2_BITS) - 1;
 
   // Min and max of non bigint values.
-  static const int MIN_INT64 = (-1) << 63;
-  static const int MAX_INT64 = 0x7fffffffffffffff;
+  static const int _MIN_INT64 = (-1) << 63;
+  static const int _MAX_INT64 = 0x7fffffffffffffff;
 
   // Bigint constant values.
   // Note: Not declared as final in order to satisfy optimizer, which expects
   // constants to be in canonical form (Smi).
-  static _Bigint ONE = new _Bigint._fromInt(1);
-
-  // Digit conversion table for parsing.
-  static final Map<int, int> DIGIT_TABLE = _createDigitTable();
+  static _Bigint _MINUS_ONE = new _Bigint._fromInt(-1);
+  static _Bigint _ZERO = new _Bigint._fromInt(0);
+  static _Bigint _ONE = new _Bigint._fromInt(1);
 
   // Internal data structure.
-  // TODO(regis): Remove the 3 native setters below and provide a constructor
-  // taking all 3 field values, which is equivalent to making the fields final.
   bool get _neg native "Bigint_getNeg";
-  void set _neg(bool value) native "Bigint_setNeg";
   int get _used native "Bigint_getUsed";
-  void set _used(int value) native "Bigint_setUsed";
   Uint32List get _digits native "Bigint_getDigits";
-  void set _digits(Uint32List value) native "Bigint_setDigits";
 
-  // Factory returning an instance initialized to value 0.
-  factory _Bigint() native "Bigint_allocate";
+  // Factory returning an instance initialized with the given field values.
+  // The 'digits' array is first clamped and 'used' is reduced accordingly.
+  // A leading zero digit may be initialized to guarantee that digit pairs can
+  // be processed as 64-bit values on 64-bit platforms.
+  factory _Bigint(bool neg, int used, Uint32List digits)
+      native "Bigint_allocate";
 
   // Factory returning an instance initialized to an integer value no larger
   // than a Mint.
   factory _Bigint._fromInt(int i) {
     assert(i is! _Bigint);
-    bool neg;
+    var neg;
     var l, h;
     if (i < 0) {
       neg = true;
-      if (i == MIN_INT64) {
+      if (i == _MIN_INT64) {
         l = 0;
         h = 0x80000000;
       } else {
-        l = (-i) & DIGIT_MASK;
-        h = (-i) >> DIGIT_BITS;
+        l = (-i) & _DIGIT_MASK;
+        h = (-i) >> _DIGIT_BITS;
       }
     } else {
       neg = false;
-      l = i & DIGIT_MASK;
-      h = i >> DIGIT_BITS;
+      l = i & _DIGIT_MASK;
+      h = i >> _DIGIT_BITS;
     }
-    var result = new _Bigint();
-    result._ensureLength(2);
-    result._neg = neg;
-    result._used = 2;
-    result._digits[0] = l;
-    result._digits[1] = h;
-    result._clamp();
-    return result;
+    var digits = new Uint32List(2);
+    digits[0] = l;
+    digits[1] = h;
+    return new _Bigint(neg, 2, digits);
   }
 
-  // Create digit conversion table for parsing.
-  static Map<int, int> _createDigitTable() {
-    Map table = new HashMap();
-    int digit, value;
-    digit = "0".codeUnitAt(0);
-    for(value = 0; value <= 9; ++value) table[digit++] = value;
-    digit = "a".codeUnitAt(0);
-    for(value = 10; value < 36; ++value) table[digit++] = value;
-    digit = "A".codeUnitAt(0);
-    for(value = 10; value < 36; ++value) table[digit++] = value;
-    return table;
+  // Allocate an array of the given length (+1 for at least one leading zero
+  // digit if odd) and copy digits[from..to-1] starting at index 0, followed by
+  // leading zero digits.
+  static Uint32List _cloneDigits(Uint32List digits, int from, int to,
+                                 int length) {
+    length += length & 1;  // Even number of digits.
+    var r_digits = new Uint32List(length);
+    var n = to - from;
+    for (var i = 0; i < n; i++) {
+      r_digits[i] = digits[from + i];
+    }
+    return r_digits;
   }
 
   // Return most compact integer (i.e. possibly Smi or Mint).
-  // TODO(regis): Intrinsify.
   int _toValidInt() {
-    assert(DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
+    assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     var used = _used;
     if (used == 0) return 0;
     var digits = _digits;
@@ -132,74 +129,37 @@
       if (digits[1] > 0x80000000) return this;
       if (digits[1] == 0x80000000) {
         if (digits[0] > 0) return this;
-        return MIN_INT64;
+        return _MIN_INT64;
       }
-      return -((digits[1] << DIGIT_BITS) | digits[0]);
+      return -((digits[1] << _DIGIT_BITS) | digits[0]);
     }
     if (digits[1] >= 0x80000000) return this;
-    return (digits[1] << DIGIT_BITS) | digits[0];
+    return (digits[1] << _DIGIT_BITS) | digits[0];
   }
 
   // Conversion from int to bigint.
   _Bigint _toBigint() => this;
 
-  // Make sure at least 'length' _digits are allocated.
-  // Copy existing and used _digits if reallocation is necessary.
-  // Avoid preserving _digits unnecessarily by calling this function with a
-  // meaningful _used field.
-  void _ensureLength(int length) {
-    length++;  // Account for leading zero for 64-bit processing.
-    var digits = _digits;
-    if (length > digits.length) {
-      var new_digits = new Uint32List(length + EXTRA_DIGITS);
-      _digits = new_digits;
-      var used = _used;
-      if (used > 0) {
-        var i = used + 1;  // Copy leading zero for 64-bit processing.
-        while (--i >= 0) {
-          new_digits[i] = digits[i];
-        }
-      }
+  // Return -this.
+  _Bigint _negate() {
+    var used = _used;
+    if (used == 0) {
+      return this;
     }
+    return new _Bigint(!_neg, used, _digits);
   }
 
-  // Clamp off excess high _digits.
-  void _clamp() {
-    var used = _used;
-    if (used > 0) {
-      var digits = _digits;
-      if (digits[used - 1] == 0) {
-        do {
-          --used;
-        } while (used > 0 && digits[used - 1] == 0);
-        _used = used;
-      }
-      digits[used] = 0;  // Set leading zero for 64-bit processing.
+  // Return abs(this).
+  _Bigint _abs() {
+    var neg = _neg;
+    if (!neg) {
+      return this;
     }
-  }
-
-  // Copy this to r.
-  void _copyTo(_Bigint r) {
-    var used = _used;
-    if (used > 0) {
-      // We could set r._used to 0 in order to avoid preserving digits. However,
-      // it would be wrong to do so if this === r. Checking is too expensive.
-      // This case does not occur in the current implementation, but we want to
-      // remain safe.
-      r._ensureLength(used);
-      var digits = _digits;
-      var r_digits = r._digits;
-      var i = used + 1;  // Copy leading zero for 64-bit processing.
-      while (--i >= 0) {
-        r_digits[i] = digits[i];
-      }
-    }
-    r._used = used;
-    r._neg = _neg;
+    return new _Bigint(!neg, _used, _digits);
   }
 
   // Return the bit length of digit x.
-  int _nbits(int x) {
+  static int _nbits(int x) {
     var r = 1, t;
     if ((t = x >> 16) != 0) { x = t; r += 16; }
     if ((t = x >> 8) != 0) { x = t; r += 8; }
@@ -209,162 +169,212 @@
     return r;
   }
 
-  // r = this << n*DIGIT_BITS.
-  void _dlShiftTo(int n, _Bigint r) {
+  // Return this << n*_DIGIT_BITS.
+  _Bigint _dlShift(int n) {
     var used = _used;
     if (used == 0) {
-      r._used = 0;
-      r._neg = false;
-      return;
+      return _ZERO;
     }
     var r_used = used + n;
-    r._ensureLength(r_used);
     var digits = _digits;
-    var r_digits = r._digits;
-    var i = used + 1;  // Copy leading zero for 64-bit processing.
+    var r_digits = new Uint32List(r_used + (r_used & 1));
+    var i = used;
     while (--i >= 0) {
       r_digits[i + n] = digits[i];
     }
+    return new _Bigint(_neg, r_used, r_digits);
+  }
+
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1] << n*_DIGIT_BITS.
+  // Return r_used.
+  static int _dlShiftDigits(Uint32List x_digits, int x_used, int n,
+                            Uint32List r_digits) {
+    if (x_used == 0) {
+      return 0;
+    }
+    if (n == 0 && r_digits == x_digits) {
+      return x_used;
+    }
+    var r_used = x_used + n;
+    assert(r_digits.length >= r_used + (r_used & 1));
+    var i = x_used;
+    while (--i >= 0) {
+      r_digits[i + n] = x_digits[i];
+    }
     i = n;
     while (--i >= 0) {
       r_digits[i] = 0;
     }
-    r._used = r_used;
-    r._neg = _neg;
+    if (r_used.isOdd) {
+      r_digits[r_used] = 0;
+    }
+    return r_used;
   }
 
-  // r = this >> n*DIGIT_BITS.
-  void _drShiftTo(int n, _Bigint r) {
+  // Return this >> n*_DIGIT_BITS.
+  _Bigint _drShift(int n) {
     var used = _used;
     if (used == 0) {
-      r._used = 0;
-      r._neg = false;
-      return;
+      return _ZERO;
     }
     var r_used = used - n;
     if (r_used <= 0) {
-      if (_neg) {
-        // Set r to -1.
-        r._used = 0;  // No digits to preserve.
-        r._ensureLength(1);
-        r._neg = true;
-        r._used = 1;
-        r._digits[0] = 1;
-        r._digits[1] = 0;  // Set leading zero for 64-bit processing.
-      } else {
-        // Set r to 0.
-        r._neg = false;
-        r._used = 0;
-      }
-      return;
+      return _neg ? _MINUS_ONE : _ZERO;
     }
-    r._ensureLength(r_used);
     var digits = _digits;
-    var r_digits = r._digits;
-    for (var i = n; i < used + 1; i++) {  // Copy leading zero for 64-bit proc.
+    var r_digits = new Uint32List(r_used + (r_used & 1));
+    for (var i = n; i < used; i++) {
       r_digits[i - n] = digits[i];
     }
-    r._used = r_used;
-    r._neg = _neg;
+    var r = new _Bigint(_neg, r_used, r_digits);
     if (_neg) {
       // Round down if any bit was shifted out.
       for (var i = 0; i < n; i++) {
         if (digits[i] != 0) {
-          r._subTo(ONE, r);
-          break;
+          return r._sub(_ONE);
         }
       }
     }
+    return r;
   }
 
-  // r = this << n.
-  void _lShiftTo(int n, _Bigint r) {
-    var ds = n ~/ DIGIT_BITS;
-    var bs = n % DIGIT_BITS;
-    if (bs == 0) {
-      _dlShiftTo(ds, r);
-      return;
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1] >> n*_DIGIT_BITS.
+  // Return r_used.
+  static int _drShiftDigits(Uint32List x_digits, int x_used, int n,
+                            Uint32List r_digits) {
+    var r_used = x_used - n;
+    if (r_used <= 0) {
+      return 0;
     }
-    var cbs = DIGIT_BITS - bs;
+    assert(r_digits.length >= r_used + (r_used & 1));
+    for (var i = n; i < x_used; i++) {
+      r_digits[i - n] = x_digits[i];
+    }
+    if (r_used.isOdd) {
+      r_digits[r_used] = 0;
+    }
+    return r_used;
+  }
+
+  // Return this << n.
+  _Bigint _lShift(int n) {
+    var ds = n ~/ _DIGIT_BITS;
+    var bs = n % _DIGIT_BITS;
+    if (bs == 0) {
+      return _dlShift(ds);
+    }
+    var cbs = _DIGIT_BITS - bs;
     var bm = (1 << cbs) - 1;
     var r_used = _used + ds + 1;
-    r._ensureLength(r_used);
     var digits = _digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     var c = 0;
     var i = _used;
     while (--i >= 0) {
       r_digits[i + ds + 1] = (digits[i] >> cbs) | c;
       c = (digits[i] & bm) << bs;
     }
+    r_digits[ds] = c;
+    return new _Bigint(_neg, r_used, r_digits);
+  }
+
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1] << n.
+  // Return r_used.
+  static int _lShiftDigits(Uint32List x_digits, int x_used, int n,
+                           Uint32List r_digits) {
+    var ds = n ~/ _DIGIT_BITS;
+    var bs = n % _DIGIT_BITS;
+    if (bs == 0) {
+      return _dlShiftDigits(x_digits, x_used, ds, r_digits);
+    }
+    var cbs = _DIGIT_BITS - bs;
+    var bm = (1 << cbs) - 1;
+    var r_used = x_used + ds + 1;
+    assert(r_digits.length >= r_used + (r_used & 1));
+    var c = 0;
+    var i = x_used;
+    while (--i >= 0) {
+      r_digits[i + ds + 1] = (x_digits[i] >> cbs) | c;
+      c = (x_digits[i] & bm) << bs;
+    }
+    r_digits[ds] = c;
     i = ds;
     while (--i >= 0) {
       r_digits[i] = 0;
     }
-    r_digits[ds] = c;
-    r._used = r_used;
-    r._neg = _neg;
-    r._clamp();
+    if (r_used.isOdd) {
+      r_digits[r_used] = 0;
+    }
+    return r_used;
   }
 
-  // r = this >> n.
-  void _rShiftTo(int n, _Bigint r) {
-    var ds = n ~/ DIGIT_BITS;
-    var bs = n % DIGIT_BITS;
+  // Return this >> n.
+  _Bigint _rShift(int n) {
+    var ds = n ~/ _DIGIT_BITS;
+    var bs = n % _DIGIT_BITS;
     if (bs == 0) {
-      _drShiftTo(ds, r);
-      return;
+      return _drShift(ds);
     }
     var r_used = _used - ds;
     if (r_used <= 0) {
-      if (_neg) {
-        // Set r to -1.
-        r._neg = true;
-        r._used = 0;  // No digits to preserve.
-        r._ensureLength(1);
-        r._used = 1;
-        r._digits[0] = 1;
-        r._digits[1] = 0;  // Set leading zero for 64-bit processing.
-      } else {
-        // Set r to 0.
-        r._neg = false;
-        r._used = 0;
-      }
-      return;
+      return _neg ? _MINUS_ONE : _ZERO;
     }
-    var cbs = DIGIT_BITS - bs;
+    var cbs = _DIGIT_BITS - bs;
     var bm = (1 << bs) - 1;
-    r._ensureLength(r_used);
     var digits = _digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     r_digits[0] = digits[ds] >> bs;
     var used = _used;
     for (var i = ds + 1; i < used; i++) {
       r_digits[i - ds - 1] |= (digits[i] & bm) << cbs;
       r_digits[i - ds] = digits[i] >> bs;
     }
-    r._neg = _neg;
-    r._used = r_used;
-    r._clamp();
+    var r = new _Bigint(_neg, r_used, r_digits);
     if (_neg) {
       // Round down if any bit was shifted out.
       if ((digits[ds] & bm) != 0) {
-        r._subTo(ONE, r);
-        return;
+        return r._sub(_ONE);
       }
       for (var i = 0; i < ds; i++) {
         if (digits[i] != 0) {
-          r._subTo(ONE, r);
-          return;
+          return r._sub(_ONE);
         }
       }
     }
+    return r;
+  }
+
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1] >> n.
+  // Return r_used.
+  static int _rShiftDigits(Uint32List x_digits, int x_used, int n,
+                           Uint32List r_digits) {
+    var ds = n ~/ _DIGIT_BITS;
+    var bs = n % _DIGIT_BITS;
+    if (bs == 0) {
+      return _drShiftDigits(x_digits, x_used, ds, r_digits);
+    }
+    var r_used = x_used - ds;
+    if (r_used <= 0) {
+      return 0;
+    }
+    var cbs = _DIGIT_BITS - bs;
+    var bm = (1 << bs) - 1;
+    assert(r_digits.length >= r_used + (r_used & 1));
+    r_digits[0] = x_digits[ds] >> bs;
+    for (var i = ds + 1; i < x_used; i++) {
+      r_digits[i - ds - 1] |= (x_digits[i] & bm) << cbs;
+      r_digits[i - ds] = x_digits[i] >> bs;
+    }
+    if (r_used.isOdd) {
+      r_digits[r_used] = 0;
+    }
+    return r_used;
   }
 
   // Return 0 if abs(this) == abs(a).
   // Return a positive number if abs(this) > abs(a).
   // Return a negative number if abs(this) < abs(a).
-  int _absCompareTo(_Bigint a) {
+  int _absCompare(_Bigint a) {
     var r = _used - a._used;
     if (r == 0) {
       var i = _used;
@@ -378,124 +388,133 @@
   // Return 0 if this == a.
   // Return a positive number if this > a.
   // Return a negative number if this < a.
-  int _compareTo(_Bigint a) {
-    var r;
+  int _compare(_Bigint a) {
     if (_neg == a._neg) {
-      r = _absCompareTo(a);
-      if (_neg) {
-        r = -r;
-      }
-    } else if (_neg) {
-      r = -1;
-    } else {
-      r = 1;
+      var r = _absCompare(a);
+      return _neg ? -r : r;
+    }
+    return _neg ? -1 : 1;
+  }
+
+  // Compare digits[0..used-1] with a_digits[0..a_used-1].
+  // Return 0 if equal.
+  // Return a positive number if larger.
+  // Return a negative number if smaller.
+  static int _compareDigits(Uint32List digits, int used,
+                            Uint32List a_digits, int a_used) {
+    var r = used - a_used;
+    if (r == 0) {
+      var i = a_used;
+      while (--i >= 0 && (r = digits[i] - a_digits[i]) == 0);
     }
     return r;
   }
 
   // r_digits[0..used] = digits[0..used-1] + a_digits[0..a_used-1].
   // used >= a_used > 0.
+  // Note: Intrinsics on 64-bit platforms process digit pairs at even indices.
   static void _absAdd(Uint32List digits, int used,
                       Uint32List a_digits, int a_used,
                       Uint32List r_digits) {
+    assert(used >= a_used && a_used > 0);
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(digits.length > ((used - 1) | 1));
+    assert(a_digits.length > ((a_used - 1) | 1));
+    assert(r_digits.length > (used | 1));
     var c = 0;
     for (var i = 0; i < a_used; i++) {
       c += digits[i] + a_digits[i];
-      r_digits[i] = c & DIGIT_MASK;
-      c >>= DIGIT_BITS;
+      r_digits[i] = c & _DIGIT_MASK;
+      c >>= _DIGIT_BITS;
     }
     for (var i = a_used; i < used; i++) {
       c += digits[i];
-      r_digits[i] = c & DIGIT_MASK;
-      c >>= DIGIT_BITS;
+      r_digits[i] = c & _DIGIT_MASK;
+      c >>= _DIGIT_BITS;
     }
     r_digits[used] = c;
   }
 
   // r_digits[0..used-1] = digits[0..used-1] - a_digits[0..a_used-1].
   // used >= a_used > 0.
+  // Note: Intrinsics on 64-bit platforms process digit pairs at even indices.
   static void _absSub(Uint32List digits, int used,
                       Uint32List a_digits, int a_used,
                       Uint32List r_digits) {
+    assert(used >= a_used && a_used > 0);
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(digits.length > ((used - 1) | 1));
+    assert(a_digits.length > ((a_used - 1) | 1));
+    assert(r_digits.length > ((used - 1) | 1));
     var c = 0;
     for (var i = 0; i < a_used; i++) {
       c += digits[i] - a_digits[i];
-      r_digits[i] = c & DIGIT_MASK;
-      c >>= DIGIT_BITS;
+      r_digits[i] = c & _DIGIT_MASK;
+      c >>= _DIGIT_BITS;
     }
     for (var i = a_used; i < used; i++) {
       c += digits[i];
-      r_digits[i] = c & DIGIT_MASK;
-      c >>= DIGIT_BITS;
+      r_digits[i] = c & _DIGIT_MASK;
+      c >>= _DIGIT_BITS;
     }
   }
 
-  // r = abs(this) + abs(a).
-  void _absAddTo(_Bigint a, _Bigint r) {
+  // Return abs(this) + abs(a) with sign set according to neg.
+  _Bigint _absAddSetSign(_Bigint a, bool neg) {
     var used = _used;
     var a_used = a._used;
     if (used < a_used) {
-      a._absAddTo(this, r);
-      return;
+      return a._absAddSetSign(this, neg);
     }
     if (used == 0) {
-      // Set r to 0.
-      r._neg = false;
-      r._used = 0;
-      return;
+      assert(!neg);
+      return _ZERO;
     }
     if (a_used == 0) {
-      _copyTo(r);
-      return;
+      return _neg == neg ? this : this._negate();
     }
-    r._ensureLength(used + 1);
-    _absAdd(_digits, used, a._digits, a_used, r._digits);
-    r._used = used + 1;
-    r._clamp();
+    var r_used = used + 1;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
+    _absAdd(_digits, used, a._digits, a_used, r_digits);
+    return new _Bigint(neg, r_used, r_digits);
   }
 
-  // r = abs(this) - abs(a), with abs(this) >= abs(a).
-  void _absSubTo(_Bigint a, _Bigint r) {
-    assert(_absCompareTo(a) >= 0);
+  // Return abs(this) - abs(a) with sign set according to neg.
+  // Requirement: abs(this) >= abs(a).
+  _Bigint _absSubSetSign(_Bigint a, bool neg) {
+    assert(_absCompare(a) >= 0);
     var used = _used;
     if (used == 0) {
-      // Set r to 0.
-      r._neg = false;
-      r._used = 0;
-      return;
+      assert(!neg);
+      return _ZERO;
     }
     var a_used = a._used;
     if (a_used == 0) {
-      _copyTo(r);
-      return;
+      return _neg == neg ? this : this._negate();
     }
-    r._ensureLength(used);
-    _absSub(_digits, used, a._digits, a_used, r._digits);
-    r._used = used;
-    r._clamp();
+    var r_digits = new Uint32List(used + (used & 1));
+    _absSub(_digits, used, a._digits, a_used, r_digits);
+    return new _Bigint(neg, used, r_digits);
   }
 
-  // r = abs(this) & abs(a).
-  void _absAndTo(_Bigint a, _Bigint r) {
+  // Return abs(this) & abs(a) with sign set according to neg.
+  _Bigint _absAndSetSign(_Bigint a, bool neg) {
     var r_used = (_used < a._used) ? _used : a._used;
-    r._ensureLength(r_used);
     var digits = _digits;
     var a_digits = a._digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     for (var i = 0; i < r_used; i++) {
       r_digits[i] = digits[i] & a_digits[i];
     }
-    r._used = r_used;
-    r._clamp();
+    return new _Bigint(neg, r_used, r_digits);
   }
 
-  // r = abs(this) &~ abs(a).
-  void _absAndNotTo(_Bigint a, _Bigint r) {
+  // Return abs(this) &~ abs(a) with sign set according to neg.
+  _Bigint _absAndNotSetSign(_Bigint a, bool neg) {
     var r_used = _used;
-    r._ensureLength(r_used);
     var digits = _digits;
     var a_digits = a._digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     var m = (r_used < a._used) ? r_used : a._used;
     for (var i = 0; i < m; i++) {
       r_digits[i] = digits[i] &~ a_digits[i];
@@ -503,19 +522,17 @@
     for (var i = m; i < r_used; i++) {
       r_digits[i] = digits[i];
     }
-    r._used = r_used;
-    r._clamp();
+    return new _Bigint(neg, r_used, r_digits);
   }
 
-  // r = abs(this) | abs(a).
-  void _absOrTo(_Bigint a, _Bigint r) {
+  // Return abs(this) | abs(a) with sign set according to neg.
+  _Bigint _absOrSetSign(_Bigint a, bool neg) {
     var used = _used;
     var a_used = a._used;
     var r_used = (used > a_used) ? used : a_used;
-    r._ensureLength(r_used);
     var digits = _digits;
     var a_digits = a._digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     var l, m;
     if (used < a_used) {
       l = a;
@@ -531,19 +548,17 @@
     for (var i = m; i < r_used; i++) {
       r_digits[i] = l_digits[i];
     }
-    r._used = r_used;
-    r._clamp();
+    return new _Bigint(neg, r_used, r_digits);
   }
 
-  // r = abs(this) ^ abs(a).
-  void _absXorTo(_Bigint a, _Bigint r) {
+  // Return abs(this) ^ abs(a) with sign set according to neg.
+  _Bigint _absXorSetSign(_Bigint a, bool neg) {
     var used = _used;
     var a_used = a._used;
     var r_used = (used > a_used) ? used : a_used;
-    r._ensureLength(r_used);
     var digits = _digits;
     var a_digits = a._digits;
-    var r_digits = r._digits;
+    var r_digits = new Uint32List(r_used + (r_used & 1));
     var l, m;
     if (used < a_used) {
       l = a;
@@ -559,29 +574,22 @@
     for (var i = m; i < r_used; i++) {
       r_digits[i] = l_digits[i];
     }
-    r._used = r_used;
-    r._clamp();
+    return new _Bigint(neg, r_used, r_digits);
   }
 
-  // Return r = this & a.
-  _Bigint _andTo(_Bigint a, _Bigint r) {
+  // Return this & a.
+  _Bigint _and(_Bigint a) {
     if (_neg == a._neg) {
       if (_neg) {
         // (-this) & (-a) == ~(this-1) & ~(a-1)
         //                == ~((this-1) | (a-1))
         //                == -(((this-1) | (a-1)) + 1)
-        _Bigint t1 = new _Bigint();
-        _absSubTo(ONE, t1);
-        _Bigint a1 = new _Bigint();
-        a._absSubTo(ONE, a1);
-        t1._absOrTo(a1, r);
-        r._absAddTo(ONE, r);
-        r._neg = true;  // r cannot be zero if this and a are negative.
-        return r;
+        _Bigint t1 = _absSubSetSign(_ONE, true);
+        _Bigint a1 = a._absSubSetSign(_ONE, true);
+        // Result cannot be zero if this and a are negative.
+        return t1._absOrSetSign(a1, true)._absAddSetSign(_ONE, true);
       }
-      _absAndTo(a, r);
-      r._neg = false;
-      return r;
+      return _absAndSetSign(a, false);
     }
     // _neg != a._neg
     var p, n;
@@ -593,71 +601,50 @@
       n = a;
     }
     // p & (-n) == p & ~(n-1) == p &~ (n-1)
-    _Bigint n1 = new _Bigint();
-    n._absSubTo(ONE, n1);
-    p._absAndNotTo(n1, r);
-    r._neg = false;
-    return r;
+    _Bigint n1 = n._absSubSetSign(_ONE, false);
+    return p._absAndNotSetSign(n1, false);
   }
 
-  // Return r = this &~ a.
-  _Bigint _andNotTo(_Bigint a, _Bigint r) {
+  // Return this &~ a.
+  _Bigint _andNot(_Bigint a) {
     if (_neg == a._neg) {
       if (_neg) {
         // (-this) &~ (-a) == ~(this-1) &~ ~(a-1)
         //                 == ~(this-1) & (a-1)
         //                 == (a-1) &~ (this-1)
-        _Bigint t1 = new _Bigint();
-        _absSubTo(ONE, t1);
-        _Bigint a1 = new _Bigint();
-        a._absSubTo(ONE, a1);
-        a1._absAndNotTo(t1, r);
-        r._neg = false;
-        return r;
+        _Bigint t1 = _absSubSetSign(_ONE, true);
+        _Bigint a1 = a._absSubSetSign(_ONE, true);
+        return a1._absAndNotSetSign(t1, false);
       }
-      _absAndNotTo(a, r);
-      r._neg = false;
-      return r;
+      return _absAndNotSetSign(a, false);
     }
     if (_neg) {
       // (-this) &~ a == ~(this-1) &~ a
       //              == ~(this-1) & ~a
       //              == ~((this-1) | a)
       //              == -(((this-1) | a) + 1)
-      _Bigint t1 = new _Bigint();
-      _absSubTo(ONE, t1);
-      t1._absOrTo(a, r);
-      r._absAddTo(ONE, r);
-      r._neg = true;  // r cannot be zero if this is negative and a is positive.
-      return r;
+      _Bigint t1 = _absSubSetSign(_ONE, true);
+      // Result cannot be zero if this is negative and a is positive.
+      return t1._absOrSetSign(a, true)._absAddSetSign(_ONE, true);
     }
     // this &~ (-a) == this &~ ~(a-1) == this & (a-1)
-    _Bigint a1 = new _Bigint();
-    a._absSubTo(ONE, a1);
-    _absAndTo(a1, r);
-    r._neg = false;
-    return r;
+    _Bigint a1 = a._absSubSetSign(_ONE, true);
+    return _absAndSetSign(a1, false);
   }
 
-  // Return r = this | a.
-  _Bigint _orTo(_Bigint a, _Bigint r) {
+  // Return this | a.
+  _Bigint _or(_Bigint a) {
     if (_neg == a._neg) {
       if (_neg) {
         // (-this) | (-a) == ~(this-1) | ~(a-1)
         //                == ~((this-1) & (a-1))
         //                == -(((this-1) & (a-1)) + 1)
-        _Bigint t1 = new _Bigint();
-        _absSubTo(ONE, t1);
-        _Bigint a1 = new _Bigint();
-        a._absSubTo(ONE, a1);
-        t1._absAndTo(a1, r);
-        r._absAddTo(ONE, r);
-        r._neg = true;  // r cannot be zero if this and a are negative.
-        return r;
+        _Bigint t1 = _absSubSetSign(_ONE, true);
+        _Bigint a1 = a._absSubSetSign(_ONE, true);
+        // Result cannot be zero if this and a are negative.
+        return t1._absAndSetSign(a1, true)._absAddSetSign(_ONE, true);
       }
-      _absOrTo(a, r);
-      r._neg = false;
-      return r;
+      return _absOrSetSign(a, false);
     }
     // _neg != a._neg
     var p, n;
@@ -669,30 +656,21 @@
       n = a;
     }
     // p | (-n) == p | ~(n-1) == ~((n-1) &~ p) == -(~((n-1) &~ p) + 1)
-    _Bigint n1 = new _Bigint();
-    n._absSubTo(ONE, n1);
-    n1._absAndNotTo(p, r);
-    r._absAddTo(ONE, r);
-    r._neg = true;  // r cannot be zero if only one of this or a is negative.
-    return r;
+    _Bigint n1 = n._absSubSetSign(_ONE, true);
+    // Result cannot be zero if only one of this or a is negative.
+    return n1._absAndNotSetSign(p, true)._absAddSetSign(_ONE, true);
   }
 
-  // Return r = this ^ a.
-  _Bigint _xorTo(_Bigint a, _Bigint r) {
+  // Return this ^ a.
+  _Bigint _xor(_Bigint a) {
     if (_neg == a._neg) {
       if (_neg) {
         // (-this) ^ (-a) == ~(this-1) ^ ~(a-1) == (this-1) ^ (a-1)
-        _Bigint t1 = new _Bigint();
-        _absSubTo(ONE, t1);
-        _Bigint a1 = new _Bigint();
-        a._absSubTo(ONE, a1);
-        t1._absXorTo(a1, r);
-        r._neg = false;
-        return r;
+        _Bigint t1 = _absSubSetSign(_ONE, true);
+        _Bigint a1 = a._absSubSetSign(_ONE, true);
+        return t1._absXorSetSign(a1, false);
       }
-      _absXorTo(a, r);
-      r._neg = false;
-      return r;
+      return _absXorSetSign(a, false);
     }
     // _neg != a._neg
     var p, n;
@@ -704,68 +682,52 @@
       n = a;
     }
     // p ^ (-n) == p ^ ~(n-1) == ~(p ^ (n-1)) == -((p ^ (n-1)) + 1)
-    _Bigint n1 = new _Bigint();
-    n._absSubTo(ONE, n1);
-    p._absXorTo(n1, r);
-    r._absAddTo(ONE, r);
-    r._neg = true;  // r cannot be zero if only one of this or a is negative.
-    return r;
+    _Bigint n1 = n._absSubSetSign(_ONE, true);
+    // Result cannot be zero if only one of this or a is negative.
+    return p._absXorSetSign(n1, true)._absAddSetSign(_ONE, true);
   }
 
-  // Return r = ~this.
-  _Bigint _notTo(_Bigint r) {
+  // Return ~this.
+  _Bigint _not() {
     if (_neg) {
       // ~(-this) == ~(~(this-1)) == this-1
-      _absSubTo(ONE, r);
-      r._neg = false;
-      return r;
+      return _absSubSetSign(_ONE, false);
     }
     // ~this == -this-1 == -(this+1)
-    _absAddTo(ONE, r);
-    r._neg = true;  // r cannot be zero if this is positive.
-    return r;
+    // Result cannot be zero if this is positive.
+    return _absAddSetSign(_ONE, true);
   }
 
-  // Return r = this + a.
-  _Bigint _addTo(_Bigint a, _Bigint r) {
-    var r_neg = _neg;
-    if (_neg == a._neg) {
+  // Return this + a.
+  _Bigint _add(_Bigint a) {
+    var neg = _neg;
+    if (neg == a._neg) {
       // this + a == this + a
       // (-this) + (-a) == -(this + a)
-      _absAddTo(a, r);
-    } else {
-      // this + (-a) == this - a == -(this - a)
-      // (-this) + a == a - this == -(this - a)
-      if (_absCompareTo(a) >= 0) {
-        _absSubTo(a, r);
-      } else {
-        r_neg = !r_neg;
-        a._absSubTo(this, r);
-      }
+      return _absAddSetSign(a, neg);
     }
-  	r._neg = r_neg;
-    return r;
+    // this + (-a) == this - a == -(this - a)
+    // (-this) + a == a - this == -(this - a)
+    if (_absCompare(a) >= 0) {
+      return _absSubSetSign(a, neg);
+    }
+    return a._absSubSetSign(this, !neg);
   }
 
-  // Return r = this - a.
-  _Bigint _subTo(_Bigint a, _Bigint r) {
-  	var r_neg = _neg;
-    if (_neg != a._neg) {
-  		// this - (-a) == this + a
-  		// (-this) - a == -(this + a)
-      _absAddTo(a, r);
-  	} else {
-  		// this - a == this - a == -(this - a)
-  		// (-this) - (-a) == a - this == -(this - a)
-      if (_absCompareTo(a) >= 0) {
-        _absSubTo(a, r);
-  		} else {
-        r_neg = !r_neg;
-        a._absSubTo(this, r);
-      }
+  // Return this - a.
+  _Bigint _sub(_Bigint a) {
+    var neg = _neg;
+    if (neg != a._neg) {
+      // this - (-a) == this + a
+      // (-this) - a == -(this + a)
+      return _absAddSetSign(a, neg);
     }
-  	r._neg = r_neg;
-    return r;
+    // this - a == this - a == -(this - a)
+    // (-this) - (-a) == a - this == -(this - a)
+    if (_absCompare(a) >= 0) {
+      return _absSubSetSign(a, neg);
+    }
+    return a._absSubSetSign(this, !neg);
   }
 
   // Multiply and accumulate.
@@ -776,32 +738,35 @@
   // Operation:
   //   a_digits[j..j+n] += x_digits[xi]*m_digits[i..i+n-1].
   //   return 1.
-  // Note: intrinsics on 64-bit platform may process a digit pair:
-  //   a_digits[j..j+n] += x_digits[xi..xi+1]*m_digits[i..i+n-1].
-  //   return 2.
+  // Note: Intrinsics on 64-bit platforms process digit pairs at even indices
+  //   and return 2.
   static int _mulAdd(Uint32List x_digits, int xi,
                      Uint32List m_digits, int i,
                      Uint32List a_digits, int j, int n) {
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(x_digits.length > (xi | 1));
+    assert(m_digits.length > ((i + n - 1) | 1));
+    assert(a_digits.length > ((j + n) | 1));
     int x = x_digits[xi];
     if (x == 0) {
       // No-op if x is 0.
       return 1;
     }
     int c = 0;
-    int xl = x & DIGIT2_MASK;
-    int xh = x >> DIGIT2_BITS;
+    int xl = x & _DIGIT2_MASK;
+    int xh = x >> _DIGIT2_BITS;
     while (--n >= 0) {
-      int l = m_digits[i] & DIGIT2_MASK;
-      int h = m_digits[i++] >> DIGIT2_BITS;
+      int l = m_digits[i] & _DIGIT2_MASK;
+      int h = m_digits[i++] >> _DIGIT2_BITS;
       int m = xh*l + h*xl;
-      l = xl*l + ((m & DIGIT2_MASK) << DIGIT2_BITS) + a_digits[j] + c;
-      c = (l >> DIGIT_BITS) + (m >> DIGIT2_BITS) + xh*h;
-      a_digits[j++] = l & DIGIT_MASK;
+      l = xl*l + ((m & _DIGIT2_MASK) << _DIGIT2_BITS) + a_digits[j] + c;
+      c = (l >> _DIGIT_BITS) + (m >> _DIGIT2_BITS) + xh*h;
+      a_digits[j++] = l & _DIGIT_MASK;
     }
     while (c != 0) {
       int l = a_digits[j] + c;
-      c = l >> DIGIT_BITS;
-      a_digits[j++] = l & DIGIT_MASK;
+      c = l >> _DIGIT_BITS;
+      a_digits[j++] = l & _DIGIT_MASK;
     }
     return 1;
   }
@@ -814,39 +779,40 @@
   //   a_digits[2*i..i+used-1] += x_digits[i]*x_digits[i] +
   //                              2*x_digits[i]*x_digits[i+1..used-1].
   //   return 1.
-  // Note: intrinsics on 64-bit platform may process a digit pair:
-  //   a_digits[2*i..i+used-1] += x_digits[i..i+1]*x_digits[i..i+1] +
-  //                              2*x_digits[i..i+1]*x_digits[i+2..used-1].
-  //   return 2.
+  // Note: Intrinsics on 64-bit platforms process digit pairs at even indices
+  //   and return 2.
   static int _sqrAdd(Uint32List x_digits, int i,
                      Uint32List a_digits, int used) {
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(x_digits.length > ((used - 1) | 1));
+    assert(a_digits.length > ((i + used - 1) | 1));
     int x = x_digits[i];
     if (x == 0) return 1;
     int j = 2*i;
     int c = 0;
-    int xl = x & DIGIT2_MASK;
-    int xh = x >> DIGIT2_BITS;
+    int xl = x & _DIGIT2_MASK;
+    int xh = x >> _DIGIT2_BITS;
     int m = 2*xh*xl;
-    int l = xl*xl + ((m & DIGIT2_MASK) << DIGIT2_BITS) + a_digits[j];
-    c = (l >> DIGIT_BITS) + (m >> DIGIT2_BITS) + xh*xh;
-    a_digits[j] = l & DIGIT_MASK;
+    int l = xl*xl + ((m & _DIGIT2_MASK) << _DIGIT2_BITS) + a_digits[j];
+    c = (l >> _DIGIT_BITS) + (m >> _DIGIT2_BITS) + xh*xh;
+    a_digits[j] = l & _DIGIT_MASK;
     x <<= 1;
-    xl = x & DIGIT2_MASK;
-    xh = x >> DIGIT2_BITS;
+    xl = x & _DIGIT2_MASK;
+    xh = x >> _DIGIT2_BITS;
     int n = used - i - 1;
     int k = i + 1;
     j++;
     while (--n >= 0) {
-      int l = x_digits[k] & DIGIT2_MASK;
-      int h = x_digits[k++] >> DIGIT2_BITS;
+      int l = x_digits[k] & _DIGIT2_MASK;
+      int h = x_digits[k++] >> _DIGIT2_BITS;
       int m = xh*l + h*xl;
-      l = xl*l + ((m & DIGIT2_MASK) << DIGIT2_BITS) + a_digits[j] + c;
-      c = (l >> DIGIT_BITS) + (m >> DIGIT2_BITS) + xh*h;
-      a_digits[j++] = l & DIGIT_MASK;
+      l = xl*l + ((m & _DIGIT2_MASK) << _DIGIT2_BITS) + a_digits[j] + c;
+      c = (l >> _DIGIT_BITS) + (m >> _DIGIT2_BITS) + xh*h;
+      a_digits[j++] = l & _DIGIT_MASK;
     }
     c += a_digits[i + used];
-    if (c >= DIGIT_BASE) {
-      a_digits[i + used] = c - DIGIT_BASE;
+    if (c >= _DIGIT_BASE) {
+      a_digits[i + used] = c - _DIGIT_BASE;
       a_digits[i + used + 1] = 1;
     } else {
       a_digits[i + used] = c;
@@ -854,60 +820,83 @@
     return 1;
   }
 
-  // r = this * a.
-  void _mulTo(_Bigint a, _Bigint r) {
+  // Return this * a.
+  _Bigint _mul(_Bigint a) {
     // TODO(regis): Use karatsuba multiplication when appropriate.
     var used = _used;
     var a_used = a._used;
     if (used == 0 || a_used == 0) {
-      r._used = 0;
-      r._neg = false;
-      return;
+      return _ZERO;
     }
     var r_used = used + a_used;
-    r._ensureLength(r_used);
     var digits = _digits;
     var a_digits = a._digits;
-    var r_digits = r._digits;
-    r._used = r_used;
-    var i = r_used + 1;  // Set leading zero for 64-bit processing.
+    var r_digits = new Uint32List(r_used + (r_used & 1));
+    var i = 0;
+    while (i < a_used) {
+      i += _mulAdd(a_digits, i, digits, 0, r_digits, i, used);
+    }
+    return new _Bigint(_neg != a._neg, r_used, r_digits);
+  }
+
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1]*a_digits[0..a_used-1].
+  // Return r_used = x_used + a_used.
+  static int _mulDigits(Uint32List x_digits, int x_used,
+                        Uint32List a_digits, int a_used,
+                        Uint32List r_digits) {
+    var r_used = x_used + a_used;
+    var i = r_used + (r_used & 1);
+    assert(r_digits.length >= i);
     while (--i >= 0) {
       r_digits[i] = 0;
     }
     i = 0;
     while (i < a_used) {
-      i += _mulAdd(a_digits, i, digits, 0, r_digits, i, used);
+      i += _mulAdd(a_digits, i, x_digits, 0, r_digits, i, x_used);
     }
-    r._clamp();
-    r._neg = r._used > 0 && _neg != a._neg;  // Zero cannot be negative.
+    return r_used;
   }
 
-  // r = this^2, r != this.
-  void _sqrTo(_Bigint r) {
+  // Return this^2.
+  _Bigint _sqr() {
     var used = _used;
     if (used == 0) {
-      r._used = 0;
-      r._neg = false;
-      return;
+      return _ZERO;
     }
     var r_used = 2 * used;
-    r._ensureLength(r_used);
     var digits = _digits;
-    var r_digits = r._digits;
-    var i = r_used + 1;  // Set leading zero for 64-bit processing.
+    var r_digits = new Uint32List(r_used);
+    // Since r_used is even, no need for a leading zero for 64-bit processing.
+    var i = 0;
+    while (i < used - 1) {
+      i += _sqrAdd(digits, i, r_digits, used);
+    }
+    // The last step is already done if digit pairs were processed above.
+    if (i < used) {
+      _mulAdd(digits, i, digits, i, r_digits, 2*i, 1);
+    }
+    return new _Bigint(false, r_used, r_digits);
+  }
+
+  // r_digits[0..r_used-1] = x_digits[0..x_used-1]*x_digits[0..x_used-1].
+  // Return r_used = 2*x_used.
+  static int _sqrDigits(Uint32List x_digits, int x_used, Uint32List r_digits) {
+    var r_used = 2 * x_used;
+    assert(r_digits.length >= r_used);
+    // Since r_used is even, no need for a leading zero for 64-bit processing.
+    var i = r_used;
     while (--i >= 0) {
       r_digits[i] = 0;
     }
     i = 0;
-    while (i < used - 1) {
-      i += _sqrAdd(digits, i, r_digits, used);
+    while (i < x_used - 1) {
+      i += _sqrAdd(x_digits, i, r_digits, x_used);
     }
-    if (r_used > 0) {
-      _mulAdd(digits, i, digits, i, r_digits, 2*i, 1);
+    // The last step is already done if digit pairs were processed above.
+    if (i < x_used) {
+      _mulAdd(x_digits, i, x_digits, i, r_digits, 2*i, 1);
     }
-    r._used = r_used;
-    r._neg = false;
-    r._clamp();
+    return r_used;
   }
 
   // Indices of the arguments of _estQuotientDigit.
@@ -923,18 +912,20 @@
   // Operation:
   //   Estimate args[_QD] = digits[i-1..i] ~/ args[_YT]
   //   return 1
-  // Note: intrinsics on 64-bit platform may process a digit pair:
+  // Note: Intrinsics on 64-bit platforms process a digit pair (i always odd):
   //   Estimate args[_QD.._QD_HI] = digits[i-3..i] ~/ args[_YT_LO.._YT]
   //   return 2
   static int _estQuotientDigit(Uint32List args, Uint32List digits, int i) {
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(digits.length >= 4);
     if (digits[i] == args[_YT]) {
-      args[_QD] = DIGIT_MASK;
+      args[_QD] = _DIGIT_MASK;
     } else {
       // Chop off one bit, since a Mint cannot hold 2 DIGITs.
-      var qd = ((digits[i] << (DIGIT_BITS - 1)) | (digits[i - 1] >> 1))
+      var qd = ((digits[i] << (_DIGIT_BITS - 1)) | (digits[i - 1] >> 1))
           ~/ (args[_YT] >> 1);
-      if (qd > DIGIT_MASK) {
-        args[_QD] = DIGIT_MASK;
+      if (qd > _DIGIT_MASK) {
+        args[_QD] = _DIGIT_MASK;
       } else {
         args[_QD] = qd;
       }
@@ -942,71 +933,70 @@
     return 1;
   }
 
+  // Return trunc(this / a), a != 0.
+  _Bigint _div(_Bigint a) {
+    return _divRem(a, true);
+  }
 
-  // Truncating division and remainder.
-  // If q != null, q = trunc(this / a).
-  // If r != null, r = this - a * trunc(this / a).
-  void _divRemTo(_Bigint a, _Bigint q, _Bigint r) {
-    if (a._used == 0) return;
+  // Return this - a * trunc(this / a), a != 0.
+  _Bigint _rem(_Bigint a) {
+    return _divRem(a, false);
+  }
+
+  // Return trunc(this / a), a != 0, if div == true.
+  // Return this - a * trunc(this / a), a != 0, if div == false.
+  _Bigint _divRem(_Bigint a, bool div) {
+    assert(a._used > 0);
     if (_used < a._used) {
-      if (q != null) {
-        // Set q to 0.
-        q._neg = false;
-        q._used = 0;
-      }
-      if (r != null) {
-        _copyTo(r);
-      }
-      return;
+      return div ? _ZERO : this;
     }
-    if (r == null) {
-      r = new _Bigint();
-    }
-    var y = new _Bigint();  // Normalized modulus.
-    var nsh = DIGIT_BITS - _nbits(a._digits[a._used - 1]);
+    var nsh = _DIGIT_BITS - _nbits(a._digits[a._used - 1]);
     // For 64-bit processing, make sure y has an even number of digits.
-    if ((a._used & 1) == 1) {
-      nsh += DIGIT_BITS;
+    if (a._used.isOdd) {
+      nsh += _DIGIT_BITS;
     }
+    var y;  // Normalized positive divisor.
+    var r;  // Concatenated positive quotient and normalized positive remainder.
     if (nsh > 0) {
-      a._lShiftTo(nsh, y);
-      _lShiftTo(nsh, r);
+      y = a._lShift(nsh)._abs();
+      r = _lShift(nsh)._abs();
     }
     else {
-      a._copyTo(y);
-      _copyTo(r);
+      y = a._abs();
+      r = _abs();
     }
-    // We consider this and a positive. Ignore the copied sign.
-    y._neg = false;
-    r._neg = false;
     var y_used = y._used;
-    assert((y_used & 1) == 0);
     var y_digits = y._digits;
     Uint32List args = new Uint32List(4);
     args[_YT_LO] = y_digits[y_used - 2];
     args[_YT] = y_digits[y_used - 1];
-    var r_digits = r._digits;
-    var i = r._used;
-    if ((i & 1) == 1) {
-      // For 64-bit processing, make sure r has an even number of digits.
-      r_digits[i++] = 0;
-    }
+    var r_used = r._used;
+    // For 64-bit processing, make sure y_used, i, and j are even.
+    assert(y_used.isEven);
+    var i = r_used + (r_used & 1);
     var j = i - y_used;
-    _Bigint t = (q == null) ? new _Bigint() : q;
-    y._dlShiftTo(j, t);
-    if (r._compareTo(t) >= 0) {
-      r_digits[r._used++] = 1;
-      r_digits[r._used] = 0;  // Set leading zero for 64-bit processing.
-      r._subTo(t, r);
+    var t = y._dlShift(j);
+    if (r._compare(t) >= 0) {
+      assert(i == r_used);
+      r = r._or(_ONE._dlShift(r_used++))._sub(t);
+      assert(r._used == r_used && (i + 1) == r_used);
     }
-    ONE._dlShiftTo(y_used, t);
-    t._subTo(y, y);  // Negate y so we can replace sub with _mulAdd later.
-    while (y._used < y_used) {
-      y_digits[y._used++] = 0;
+    // Negate y so we can later use _mulAdd instead of non-existent _mulSub.
+    y = _ONE._dlShift(y_used)._sub(y);
+    if (y._used < y_used) {
+      y_digits = _cloneDigits(y._digits, 0, y._used, y_used);
+    } else {
+      y_digits = y._digits;
     }
-    y_digits[y._used] = 0;  // Set leading zero for 64-bit processing.
+    // y_digits is read-only and has y_used digits (possibly including several
+    // leading zeros) plus a leading zero for 64-bit processing.
+    var r_digits = _cloneDigits(r._digits, 0, r._used, i + 1);
+    // r_digits is modified during iteration.
+    // r_digits[0..y_used-1] is the current remainder.
+    // r_digits[y_used..r_used-1] is the current quotient.
+    --i;
     while (j > 0) {
-      var d0 = _estQuotientDigit(args, r_digits, --i);
+      var d0 = _estQuotientDigit(args, r_digits, i);
       j -= d0;
       var d1 = _mulAdd(args, _QD, y_digits, 0, r_digits, j, y_used);
       // _estQuotientDigit and _mulAdd must agree on the number of digits to
@@ -1014,25 +1004,29 @@
       assert(d0 == d1);
       if (d0 == 1) {
         if (r_digits[i] < args[_QD]) {
-          y._dlShiftTo(j, t);
-          r._subTo(t, r);
+          var t = y._dlShift(j);
+          var t_digits = t._digits;
+          var t_used = t._used;
+          _absSub(r_digits, r_used, t_digits, t_used, r_digits);
           while (r_digits[i] < --args[_QD]) {
-            r._subTo(t, r);
+            _absSub(r_digits, r_used, t_digits, t_used, r_digits);
           }
         }
       } else {
         assert(d0 == 2);
         assert(r_digits[i] <= args[_QD_HI]);
         if ((r_digits[i] < args[_QD_HI]) || (r_digits[i-1] < args[_QD])) {
-          y._dlShiftTo(j, t);
-          r._subTo(t, r);
+          var t = y._dlShift(j);
+          var t_digits = t._digits;
+          var t_used = t._used;
+          _absSub(r_digits, r_used, t_digits, t_used, r_digits);
           if (args[_QD] == 0) {
             --args[_QD_HI];
           }
           --args[_QD];
           assert(r_digits[i] <= args[_QD_HI]);
           while ((r_digits[i] < args[_QD_HI]) || (r_digits[i-1] < args[_QD])) {
-            r._subTo(t, r);
+            _absSub(r_digits, r_used, t_digits, t_used, r_digits);
             if (args[_QD] == 0) {
               --args[_QD_HI];
             }
@@ -1040,45 +1034,141 @@
             assert(r_digits[i] <= args[_QD_HI]);
           }
         }
-        --i;
       }
+      i -= d0;
     }
-    if (q != null) {
-      r._drShiftTo(y_used, q);
-      if (_neg != a._neg && q._used > 0) {
-        q._neg = !q._neg;
+    if (div) {
+      // Return quotient, i.e. r_digits[y_used..r_used-1] with proper sign.
+      r_digits = _cloneDigits(r_digits, y_used, r_used, r_used - y_used);
+      r = new _Bigint(false, r_used - y_used, r_digits);
+      if (_neg != a._neg && r._used > 0) {
+        r = r._negate();
       }
+      return r;
     }
-    r._used = y_used;
-    r._clamp();
+    // Return remainder, i.e. denormalized r_digits[0..y_used-1] with
+    // proper sign.
+    r_digits = _cloneDigits(r_digits, 0, y_used, y_used);
+    r = new _Bigint(false, y_used, r_digits);
     if (nsh > 0) {
-      r._rShiftTo(nsh, r);  // Denormalize remainder.
+      r = r._rShift(nsh);  // Denormalize remainder.
     }
     if (_neg && r._used > 0) {
-      r._neg = !r._neg;
+      r = r._negate();
     }
+    return r;
+  }
+
+  // Customized version of _rem() minimizing allocations for use in reduction.
+  // Input:
+  //   x_digits[0..x_used-1]: positive dividend.
+  //   y_digits[0..y_used-1]: normalized positive divisor.
+  //   ny_digits[0..y_used-1]: negated y_digits.
+  //   nsh: normalization shift amount.
+  //   yt_qd: top y digit(s) and place holder for estimated quotient digit(s).
+  //   t_digits: temp array of 2*y_used digits.
+  //   r_digits: result digits array large enough to temporarily hold
+  //             concatenated quotient and normalized remainder.
+  // Output:
+  //   r_digits[0..r_used-1]: positive remainder.
+  // Returns r_used.
+  static int _remDigits(Uint32List x_digits, int x_used,
+                        Uint32List y_digits, int y_used, Uint32List ny_digits,
+                        int nsh,
+                        Uint32List yt_qd,
+                        Uint32List t_digits,
+                        Uint32List r_digits) {
+    assert(y_used > 0 && x_used >= y_used);
+    // Initialize r_digits to normalized positive dividend.
+    var r_used = _lShiftDigits(x_digits, x_used, nsh, r_digits);
+    // For 64-bit processing, make sure y_used, i, and j are even.
+    assert(y_used.isEven);
+    var i = r_used + (r_used & 1);
+    var j = i - y_used;
+    var t_used = _dlShiftDigits(y_digits, y_used, j, t_digits);
+    // Explicit first division step in case normalized dividend is larger or
+    // equal to shifted normalized divisor.
+    if (_compareDigits(r_digits, r_used, t_digits, t_used) >= 0) {
+      assert(i == r_used);
+      r_digits[r_used++] = 1;  // Quotient = 1.
+      r_digits[r_used] = 0;  // Leading zero.
+      // Subtract divisor from remainder.
+      _absSub(r_digits, r_used, t_digits, t_used, r_digits);
+    }
+    // Negated y_digits passed in ny_digits allow the use of _mulAdd instead of
+    // unimplemented _mulSub.
+    // ny_digits is read-only and has y_used digits (possibly including several
+    // leading zeros) plus a leading zero for 64-bit processing.
+    // r_digits is modified during iteration.
+    // r_digits[0..y_used-1] is the current remainder.
+    // r_digits[y_used..r_used-1] is the current quotient.
+    --i;
+    while (j > 0) {
+      var d0 = _estQuotientDigit(yt_qd, r_digits, i);
+      j -= d0;
+      var d1 = _mulAdd(yt_qd, _QD, ny_digits, 0, r_digits, j, y_used);
+      // _estQuotientDigit and _mulAdd must agree on the number of digits to
+      // process.
+      assert(d0 == d1);
+      if (d0 == 1) {
+        if (r_digits[i] < yt_qd[_QD]) {
+          var t_used = _dlShiftDigits(ny_digits, y_used, j, t_digits);
+          _absSub(r_digits, r_used, t_digits, t_used, r_digits);
+          while (r_digits[i] < --yt_qd[_QD]) {
+            _absSub(r_digits, r_used, t_digits, t_used, r_digits);
+          }
+        }
+      } else {
+        assert(d0 == 2);
+        assert(r_digits[i] <= yt_qd[_QD_HI]);
+        if ((r_digits[i] < yt_qd[_QD_HI]) || (r_digits[i-1] < yt_qd[_QD])) {
+          var t_used = _dlShiftDigits(ny_digits, y_used, j, t_digits);
+          _absSub(r_digits, r_used, t_digits, t_used, r_digits);
+          if (yt_qd[_QD] == 0) {
+            --yt_qd[_QD_HI];
+          }
+          --yt_qd[_QD];
+          assert(r_digits[i] <= yt_qd[_QD_HI]);
+          while ((r_digits[i] < yt_qd[_QD_HI]) ||
+                 (r_digits[i-1] < yt_qd[_QD])) {
+            _absSub(r_digits, r_used, t_digits, t_used, r_digits);
+            if (yt_qd[_QD] == 0) {
+              --yt_qd[_QD_HI];
+            }
+            --yt_qd[_QD];
+            assert(r_digits[i] <= yt_qd[_QD_HI]);
+          }
+        }
+      }
+      i -= d0;
+    }
+    // Return remainder, i.e. denormalized r_digits[0..y_used-1].
+    r_used = y_used;
+    if (nsh > 0) {
+      // Denormalize remainder.
+      r_used = _rShiftDigits(r_digits, r_used, nsh, r_digits);
+    }
+    return r_used;
   }
 
   int get _identityHashCode {
     return this;
   }
   int operator ~() {
-    _Bigint result = new _Bigint();
-    _notTo(result);
-    return result._toValidInt();
+    return _not()._toValidInt();
   }
 
   int get bitLength {
     if (_used == 0) return 0;
     if (_neg) return (~this).bitLength;
-    return DIGIT_BITS*(_used - 1) + _nbits(_digits[_used - 1]);
+    return _DIGIT_BITS*(_used - 1) + _nbits(_digits[_used - 1]);
   }
 
   // This method must support smi._toBigint()._shrFromInt(int).
   int _shrFromInt(int other) {
     if (_used == 0) return other;  // Shift amount is zero.
     if (_neg) throw "negative shift amount";  // TODO(regis): What exception?
-    assert(DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
+    assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     var shift;
     if ((_used > 2) || ((_used == 2) && (_digits[1] > 0x10000000))) {
       if (other < 0) {
@@ -1087,11 +1177,9 @@
         return 0;
       }
     } else {
-      shift = ((_used == 2) ? (_digits[1] << DIGIT_BITS) : 0) + _digits[0];
+      shift = ((_used == 2) ? (_digits[1] << _DIGIT_BITS) : 0) + _digits[0];
     }
-    _Bigint result = new _Bigint();
-    other._toBigint()._rShiftTo(shift, result);
-    return result._toValidInt();
+    return other._toBigint()._rShift(shift)._toValidInt();
   }
 
   // This method must support smi._toBigint()._shlFromInt(int).
@@ -1099,16 +1187,14 @@
   int _shlFromInt(int other) {
     if (_used == 0) return other;  // Shift amount is zero.
     if (_neg) throw "negative shift amount";  // TODO(regis): What exception?
-    assert(DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
+    assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     var shift;
     if (_used > 2 || (_used == 2 && _digits[1] > 0x10000000)) {
       throw new OutOfMemoryError();
     } else {
-      shift = ((_used == 2) ? (_digits[1] << DIGIT_BITS) : 0) + _digits[0];
+      shift = ((_used == 2) ? (_digits[1] << _DIGIT_BITS) : 0) + _digits[0];
     }
-    _Bigint result = new _Bigint();
-    other._toBigint()._lShiftTo(shift, result);
-    return result._toValidInt();
+    return other._toBigint()._lShift(shift)._toValidInt();
   }
 
   // Overriden operators and methods.
@@ -1155,13 +1241,7 @@
   // End of operator shortcuts.
 
   int operator -() {
-    if (_used == 0) {
-      return this;
-    }
-    var r = new _Bigint();
-    _copyTo(r);
-    r._neg = !_neg;
-    return r._toValidInt();
+    return _negate()._toValidInt();
   }
 
   int get sign {
@@ -1177,7 +1257,7 @@
     final bitsPerChar = radix.bitLength - 1;
     final firstcx = _neg ? 1 : 0;  // Index of first char in str after the sign.
     final lastdx = _used - 1;  // Index of last digit in bigint.
-    final bitLength = lastdx*DIGIT_BITS + _nbits(_digits[lastdx]);
+    final bitLength = lastdx*_DIGIT_BITS + _nbits(_digits[lastdx]);
     // Index of char in str. Initialize with str length.
     var cx = firstcx + (bitLength + bitsPerChar - 1) ~/ bitsPerChar;
     _OneByteString str = _OneByteString._allocate(cx);
@@ -1187,17 +1267,17 @@
     var bx = 0;  // Bit index in bigint digit.
     do {
       var ch;
-      if (bx > (DIGIT_BITS - bitsPerChar)) {
+      if (bx > (_DIGIT_BITS - bitsPerChar)) {
         ch = _digits[dx++] >> bx;
-        bx += bitsPerChar - DIGIT_BITS;
+        bx += bitsPerChar - _DIGIT_BITS;
         if (dx <= lastdx) {
           ch |= (_digits[dx] & ((1 << bx) - 1)) << (bitsPerChar - bx);
         }
       } else {
         ch = (_digits[dx] >> bx) & mask;
         bx += bitsPerChar;
-        if (bx >= DIGIT_BITS) {
-          bx -= DIGIT_BITS;
+        if (bx >= _DIGIT_BITS) {
+          bx -= _DIGIT_BITS;
           dx++;
         }
       }
@@ -1211,135 +1291,134 @@
     if (count is! _Smi) {
       _shlFromInt(count);  // Throws out of memory exception.
     }
-    assert(DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
+    assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     if (count > 31) return 0;
     return (_digits[0] << count) & mask;
   }
 
   int _bitAndFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._andTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._and(this)._toValidInt();
   }
   int _bitOrFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._orTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._or(this)._toValidInt();
   }
   int _bitXorFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._xorTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._xor(this)._toValidInt();
   }
   int _addFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._addTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._add(this)._toValidInt();
   }
   int _subFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._subTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._sub(this)._toValidInt();
   }
   int _mulFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._mulTo(this, result);
-    return result._toValidInt();
+    return other._toBigint()._mul(this)._toValidInt();
   }
   int _truncDivFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._divRemTo(this, result, null);
-    return result._toValidInt();
+    return other._toBigint()._div(this)._toValidInt();
   }
   int _moduloFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._divRemTo(this, null, result);
+    _Bigint result = other._toBigint()._rem(this);
     if (result._neg) {
       if (_neg) {
-        result._subTo(this, result);
+        result = result._sub(this);
       } else {
-        result._addTo(this, result);
+        result = result._add(this);
       }
     }
     return result._toValidInt();
   }
   int _remainderFromInteger(int other) {
-    _Bigint result = new _Bigint();
-    other._toBigint()._divRemTo(this, null, result);
-    return result._toValidInt();
+    return other._toBigint()._rem(this)._toValidInt();
   }
   bool _greaterThanFromInteger(int other) {
-    return other._toBigint()._compareTo(this) > 0;
+    return other._toBigint()._compare(this) > 0;
   }
   bool _equalToInteger(int other) {
-    return other._toBigint()._compareTo(this) == 0;
+    return other._toBigint()._compare(this) == 0;
   }
 
-  // TODO(regis): Make this method private once the plumbing to invoke it from
-  // dart:math is in place. Move the argument checking to dart:math.
-  // Return pow(this, e) % m.
+  // Return pow(this, e) % m, with e >= 0, m > 0.
   int modPow(int e, int m) {
-    if (e is! int) throw new ArgumentError(e);
-    if (m is! int) throw new ArgumentError(m);
-    int i = e.bitLength;
-    if (i <= 0) return 1;
+    if (e is! int || e < 0) throw new ArgumentError(e);
+    if (m is! int || m <= 0) throw new ArgumentError(m);
+    final m_used = m._used;
+    final m_used2p2 = 2*m_used + 1 + 1;  // +1 for leading zero.
+    final e_bitlen = e.bitLength;
+    if (e_bitlen <= 0) return 1;
     if ((e is! _Bigint) || m.isEven) {
-      _Reduction z = (i < 8 || m.isEven) ? new _Classic(m) : new _Montgomery(m);
+      _Reduction z = (e_bitlen < 8 || m.isEven) ?
+          new _Classic(m) : new _Montgomery(m);
       // TODO(regis): Should we use Barrett reduction for an even modulus?
-      var r = new _Bigint();
-      var r2 = new _Bigint();
-      var g = z._convert(this);
-      i--;
-      g._copyTo(r);
+      var m_used = m._used;
+      var r_digits = new Uint32List(m_used2p2);
+      var r2_digits = new Uint32List(m_used2p2);
+      var g_digits = new Uint32List(m_used + (m_used & 1));
+      var g_used = z._convert(this, g_digits);
+      // Initialize r with g.
+      var j = g_used + (g_used & 1);  // Copy leading zero if any.
+      while (--j >= 0) {
+        r_digits[j] = g_digits[j];
+      }
+      var r_used = g_used;
+      var r2_used;
+      var i = e_bitlen - 1;
       while (--i >= 0) {
-        z._sqrTo(r, r2);
+        r2_used = z._sqr(r_digits, r_used, r2_digits);
         if ((e & (1 << i)) != 0) {
-          z._mulTo(r2, g, r);
+          r_used = z._mul(r2_digits, r2_used, g_digits, g_used, r_digits);
         } else {
-          var t = r;
-          r = r2;
-          r2 = t;
+          var t_digits = r_digits;
+          var t_used = r_used;
+          r_digits = r2_digits;
+          r_used = r2_used;
+          r2_digits = t_digits;
+          r2_used = t_used;
         }
       }
-      return z._revert(r)._toValidInt();
+      return z._revert(r_digits, r_used)._toValidInt();
     }
     var k;
-    // TODO(regis): Are these values of k really optimal for our implementation?
-    if (i < 18) k = 1;
-    else if (i < 48) k = 3;
-    else if (i < 144) k = 4;
-    else if (i < 768) k = 5;
+    if (e_bitlen < 18) k = 1;
+    else if (e_bitlen < 48) k = 3;
+    else if (e_bitlen < 144) k = 4;
+    else if (e_bitlen < 768) k = 5;
     else k = 6;
     _Reduction z = new _Montgomery(m);
     var n = 3;
-    var k1 = k - 1;
-    var km = (1 << k) - 1;
-    List g = new List(km + 1);
-    g[1] = z._convert(this);
+    final k1 = k - 1;
+    final km = (1 << k) - 1;
+    List g_digits = new List(km + 1);
+    List g_used = new List(km + 1);
+    g_digits[1] = new Uint32List(m_used + (m_used & 1));
+    g_used[1] = z._convert(this, g_digits[1]);
     if (k > 1) {
-      var g2 = new _Bigint();
-      z._sqrTo(g[1], g2);
+      var g2_digits = new Uint32List(m_used2p2);
+      var g2_used = z._sqr(g_digits[1], g_used[1], g2_digits);
       while (n <= km) {
-        g[n] = new _Bigint();
-        z._mulTo(g2, g[n - 2], g[n]);
+        g_digits[n] = new Uint32List(m_used2p2);
+        g_used[n] = z._mul(g2_digits, g2_used,
+                           g_digits[n - 2], g_used[n - 2],
+                           g_digits[n]);
         n += 2;
       }
     }
-    var j = e._used - 1;
     var w;
     var is1 = true;
-    var r = new _Bigint._fromInt(1);
-    var r2 = new _Bigint();
-    var t;
+    var r_digits = _ONE._digits;
+    var r_used = _ONE._used;
+    var r2_digits = new Uint32List(m_used2p2);
+    var r2_used;
     var e_digits = e._digits;
-    i = _nbits(e_digits[j]) - 1;
+    var j = e._used - 1;
+    var i = _nbits(e_digits[j]) - 1;
     while (j >= 0) {
       if (i >= k1) {
         w = (e_digits[j] >> (i - k1)) & km;
       } else {
         w = (e_digits[j] & ((1 << (i + 1)) - 1)) << (k1 - i);
         if (j > 0) {
-          w |= e_digits[j - 1] >> (DIGIT_BITS + i - k1);
+          w |= e_digits[j - 1] >> (_DIGIT_BITS + i - k1);
         }
       }
       n = k;
@@ -1348,56 +1427,72 @@
         --n;
       }
       if ((i -= n) < 0) {
-        i += DIGIT_BITS;
+        i += _DIGIT_BITS;
         --j;
       }
       if (is1) {  // r == 1, don't bother squaring or multiplying it.
-        g[w]._copyTo(r);
+        r_digits = new Uint32List(m_used2p2);
+        r_used = g_used[w];
+        var gw_digits = g_digits[w];
+        var ri = r_used + (r_used & 1);  // Copy leading zero if any.
+        while (--ri >= 0) {
+          r_digits[ri] = gw_digits[ri];
+        }
         is1 = false;
       }
       else {
         while (n > 1) {
-          z._sqrTo(r, r2);
-          z._sqrTo(r2, r);
+          r2_used = z._sqr(r_digits, r_used, r2_digits);
+          r_used = z._sqr(r2_digits, r2_used, r_digits);
           n -= 2;
         }
         if (n > 0) {
-          z._sqrTo(r, r2);
+          r2_used = z._sqr(r_digits, r_used, r2_digits);
         } else {
-          t = r;
-          r = r2;
-          r2 = t;
+          var t_digits = r_digits;
+          var t_used = r_used;
+          r_digits = r2_digits;
+          r_used = r2_used;
+          r2_digits = t_digits;
+          r2_used = t_used;
         }
-        z._mulTo(r2,g[w], r);
+        r_used = z._mul(r2_digits, r2_used, g_digits[w], g_used[w], r_digits);
       }
-
       while (j >= 0 && (e_digits[j] & (1 << i)) == 0) {
-        z._sqrTo(r, r2);
-        t = r;
-        r = r2;
-        r2 = t;
+        r2_used = z._sqr(r_digits, r_used, r2_digits);
+        var t_digits = r_digits;
+        var t_used = r_used;
+        r_digits = r2_digits;
+        r_used = r2_used;
+        r2_digits = t_digits;
+        r2_used = t_used;
         if (--i < 0) {
-          i = DIGIT_BITS - 1;
+          i = _DIGIT_BITS - 1;
           --j;
         }
       }
     }
-    return z._revert(r)._toValidInt();
+    assert(!is1);
+    return z._revert(r_digits, r_used)._toValidInt();
   }
 }
 
 // Interface for modular reduction.
 class _Reduction {
-  _Bigint _convert(_Bigint x);
-  _Bigint _revert(_Bigint x);
-  void _mulTo(_Bigint x, _Bigint y, _Bigint r);
-  void _sqrTo(_Bigint x, _Bigint r);
+  // Return the number of digits used by r_digits.
+  int _convert(_Bigint x, Uint32List r_digits);
+  int _mul(Uint32List x_digits, int x_used,
+           Uint32List y_digits, int y_used, Uint32List r_digits);
+  int _sqr(Uint32List x_digits, int x_used, Uint32List r_digits);
+
+  // Return x reverted to _Bigint.
+  _Bigint _revert(Uint32List x_digits, int x_used);
 }
 
 // Montgomery reduction on _Bigint.
 class _Montgomery implements _Reduction {
-  _Bigint _m;
-  int _mused2;
+  _Bigint _m;  // Modulus.
+  int _mused2p2;
   Uint32List _args;
   int _digits_per_step;  // Number of digits processed in one step. 1 or 2.
   static const int _X = 0;  // Index of x.
@@ -1409,7 +1504,7 @@
 
   _Montgomery(m) {
     _m = m._toBigint();
-    _mused2 = 2*_m._used;
+    _mused2p2 = 2*_m._used + 2;
     _args = new Uint32List(6);
     // Determine if we can process digit pairs by calling an intrinsic.
     _digits_per_step = _mulMod(_args, _args, 0);
@@ -1423,7 +1518,7 @@
     }
   }
 
-  // Calculates -1/x % DIGIT_BASE, x is 32-bit digit.
+  // Calculates -1/x % _DIGIT_BASE, x is 32-bit digit.
   //         xy == 1 (mod m)
   //         xy =  1+km
   //   xy(2-xy) = (1+km)(1-km)
@@ -1433,25 +1528,24 @@
   // Should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
   //
   // Operation:
-  //   args[_RHO] = 1/args[_X] mod DIGIT_BASE.
+  //   args[_RHO] = 1/args[_X] mod _DIGIT_BASE.
   static void _invDigit(Uint32List args) {
     var x = args[_X];
     var y = x & 3;    // y == 1/x mod 2^2
     y = (y*(2 - (x & 0xf)*y)) & 0xf;  // y == 1/x mod 2^4
     y = (y*(2 - (x & 0xff)*y)) & 0xff;  // y == 1/x mod 2^8
     y = (y*(2 - (((x & 0xffff)*y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
-    // Last step - calculate inverse mod DIGIT_BASE directly;
-    // Assumes 16 < DIGIT_BITS <= 32 and assumes ability to handle 48-bit ints.
-    y = (y*(2 - x*y % _Bigint.DIGIT_BASE)) % _Bigint.DIGIT_BASE;
-    // y == 1/x mod DIGIT_BASE
+    // Last step - calculate inverse mod _DIGIT_BASE directly;
+    // Assumes 16 < _DIGIT_BITS <= 32 and assumes ability to handle 48-bit ints.
+    y = (y*(2 - x*y % _Bigint._DIGIT_BASE)) % _Bigint._DIGIT_BASE;
+    // y == 1/x mod _DIGIT_BASE
     y = -y;  // We really want the negative inverse.
-    args[_RHO] = y & _Bigint.DIGIT_MASK;
+    args[_RHO] = y & _Bigint._DIGIT_MASK;
   }
 
-
-  // Calculates -1/x % DIGIT_BASE^2, x is a pair of 32-bit digits.
+  // Calculates -1/x % _DIGIT_BASE^2, x is a pair of 32-bit digits.
   // Operation:
-  //   args[_RHO.._RHO_HI] = 1/args[_X.._X_HI] mod DIGIT_BASE^2.
+  //   args[_RHO.._RHO_HI] = 1/args[_X.._X_HI] mod _DIGIT_BASE^2.
   static void _invDigitPair(Uint32List args) {
     var xl = args[_X];  // Lower 32-bit digit of x.
     var y = xl & 3;    // y == 1/x mod 2^2
@@ -1459,60 +1553,66 @@
     y = (y*(2 - (xl & 0xff)*y)) & 0xff;  // y == 1/x mod 2^8
     y = (y*(2 - (((xl & 0xffff)*y) & 0xffff))) & 0xffff;  // y == 1/x mod 2^16
     y = (y*(2 - ((xl*y) & 0xffffffff))) & 0xffffffff;  // y == 1/x mod 2^32
-    var x = (args[_X_HI] << _Bigint.DIGIT_BITS) | xl;
+    var x = (args[_X_HI] << _Bigint._DIGIT_BITS) | xl;
     y = (y*(2 - ((x*y) & 0xffffffffffffffff))) & 0xffffffffffffffff;
-    // y == 1/x mod DIGIT_BASE^2
+    // y == 1/x mod _DIGIT_BASE^2
     y = -y;  // We really want the negative inverse.
-    args[_RHO] = y & _Bigint.DIGIT_MASK;
-    args[_RHO_HI] = (y >> _Bigint.DIGIT_BITS) & _Bigint.DIGIT_MASK;
+    args[_RHO] = y & _Bigint._DIGIT_MASK;
+    args[_RHO_HI] = (y >> _Bigint._DIGIT_BITS) & _Bigint._DIGIT_MASK;
   }
 
-
   // Operation:
-  //   args[_MU] = args[_RHO]*digits[i] mod DIGIT_BASE.
+  //   args[_MU] = args[_RHO]*digits[i] mod _DIGIT_BASE.
   //   return 1.
-  // Note: intrinsics on 64-bit platform may process a digit pair:
-  //   args[_MU.._MU_HI] = args[_RHO.._RHO_HI]*digits[i..i+1] mod DIGIT_BASE^2.
+  // Note: Intrinsics on 64-bit platforms process digit pairs at even indices:
+  //   args[_MU.._MU_HI] = args[_RHO.._RHO_HI]*digits[i..i+1] mod _DIGIT_BASE^2.
   //   return 2.
   static int _mulMod(Uint32List args, Uint32List digits, int i) {
-    const int MU_MASK = (1 << (_Bigint.DIGIT_BITS - _Bigint.DIGIT2_BITS)) - 1;
-    var rhol = args[_RHO] & _Bigint.DIGIT2_MASK;
-    var rhoh = args[_RHO] >> _Bigint.DIGIT2_BITS;
-    var dh = digits[i] >> _Bigint.DIGIT2_BITS;
-    var dl = digits[i] & _Bigint.DIGIT2_MASK;
+    // Verify that digit pairs are accessible for 64-bit processing.
+    assert(digits.length > (i | 1));
+    const int MU_MASK = (1 << (_Bigint._DIGIT_BITS - _Bigint._DIGIT2_BITS)) - 1;
+    var rhol = args[_RHO] & _Bigint._DIGIT2_MASK;
+    var rhoh = args[_RHO] >> _Bigint._DIGIT2_BITS;
+    var dh = digits[i] >> _Bigint._DIGIT2_BITS;
+    var dl = digits[i] & _Bigint._DIGIT2_MASK;
     args[_MU] =
-        (dl*rhol + (((dl*rhoh + dh*rhol) & MU_MASK) << _Bigint.DIGIT2_BITS))
-        & _Bigint.DIGIT_MASK;
+        (dl*rhol + (((dl*rhoh + dh*rhol) & MU_MASK) << _Bigint._DIGIT2_BITS))
+        & _Bigint._DIGIT_MASK;
     return 1;
   }
 
-  // Return x*R mod _m
-  _Bigint _convert(_Bigint x) {
-    var r = new _Bigint();
-    x.abs()._dlShiftTo(_m._used, r);
-    r._divRemTo(_m, null, r);
+  // r = x*R mod _m.
+  // Return r_used.
+  int _convert(_Bigint x, Uint32List r_digits) {
+    var r = x._abs()._dlShift(_m._used)._rem(_m);
     if (x._neg && !r._neg && r._used > 0) {
-      _m._subTo(r, r);
+      r = _m._sub(r);
     }
-    return r;
+    var used = r._used;
+    var digits = r._digits;
+    var i = used + (used & 1);
+    while (--i >= 0) {
+      r_digits[i] = digits[i];
+    }
+    return used;
   }
 
-  // Return x/R mod _m
-  _Bigint _revert(_Bigint x) {
-    var r = new _Bigint();
-    x._copyTo(r);
-    _reduce(r);
-    return r;
+  _Bigint _revert(Uint32List x_digits, int x_used) {
+    var r_digits = new Uint32List(_mused2p2);
+    var i = x_used + (x_used & 1);
+    while (--i >= 0) {
+      r_digits[i] = x_digits[i];
+    }
+    var r_used = _reduce(r_digits, x_used);
+    return new _Bigint(false, r_used, r_digits);
   }
 
-  // x = x/R mod _m
-  void _reduce(_Bigint x) {
-    x._ensureLength(_mused2 + 1);
-    var x_digits = x._digits;
-    while (x._used <= _mused2) {  // Pad x so _mulAdd has enough room later.
-      x_digits[x._used++] = 0;
+  // x = x/R mod _m.
+  // Return x_used.
+  int _reduce(Uint32List x_digits, int x_used) {
+    while (x_used < _mused2p2) {  // Pad x so _mulAdd has enough room later.
+      x_digits[x_used++] = 0;
     }
-    x_digits[x._used] = 0;  // Set leading zero for 64-bit processing.
     var m_used = _m._used;
     var m_digits = _m._digits;
     var i = 0;
@@ -1523,61 +1623,125 @@
       assert(d == _digits_per_step);
       i += d;
     }
-    x._clamp();
-    x._drShiftTo(m_used, x);
-    if (x._compareTo(_m) >= 0) {
-      x._subTo(_m, x);
+    // Clamp x.
+    while (x_used > 0 && x_digits[x_used - 1] == 0) {
+      --x_used;
     }
+    x_used = _Bigint._drShiftDigits(x_digits, x_used, m_used, x_digits);
+    if (_Bigint._compareDigits(x_digits, x_used, m_digits, m_used) >= 0) {
+      _Bigint._absSub(x_digits, x_used, m_digits, m_used, x_digits);
+    }
+    // Clamp x.
+    while (x_used > 0 && x_digits[x_used - 1] == 0) {
+      --x_used;
+    }
+    return x_used;
   }
 
-  // r = x^2/R mod _m ; x != r
-  void _sqrTo(_Bigint x, _Bigint r) {
-    x._sqrTo(r);
-    _reduce(r);
+  int _sqr(Uint32List x_digits, int x_used, Uint32List r_digits) {
+    var r_used = _Bigint._sqrDigits(x_digits, x_used, r_digits);
+    return _reduce(r_digits, r_used);
   }
 
-  // r = x*y/R mod _m ; x, y != r
-  void _mulTo(_Bigint x, _Bigint y, _Bigint r) {
-    x._mulTo(y, r);
-    _reduce(r);
+  int _mul(Uint32List x_digits, int x_used,
+           Uint32List y_digits, int y_used,
+           Uint32List r_digits) {
+    var r_used = _Bigint._mulDigits(x_digits, x_used,
+                                    y_digits, y_used,
+                                    r_digits);
+    return _reduce(r_digits, r_used);
   }
 }
 
 // Modular reduction using "classic" algorithm.
 class _Classic implements _Reduction {
-  _Bigint _m;
+  _Bigint _m;  // Modulus.
+  _Bigint _norm_m;  // Normalized _m.
+  Uint32List _neg_norm_m_digits;  // Negated _norm_m digits.
+  int _m_nsh;  // Normalization shift amount.
+  Uint32List _mt_qd;  // Top _norm_m digit(s) and place holder for
+                      // estimated quotient digit(s).
+  Uint32List _t_digits;  // Temporary digits used during reduction.
 
   _Classic(int m) {
     _m = m._toBigint();
-  }
-
-  _Bigint _convert(_Bigint x) {
-    if (x._neg || x._compareTo(_m) >= 0) {
-      var r = new _Bigint();
-      x._divRemTo(_m, null, r);
-      if (x._neg && !r._neg && r._used > 0) {
-        _m._subTo(r, r);
-      }
-      return r;
+    // Preprocess arguments to _remDigits.
+    var nsh = _Bigint._DIGIT_BITS - _Bigint._nbits(_m._digits[_m._used - 1]);
+    // For 64-bit processing, make sure _norm_m_digits has an even number of
+    // digits.
+    if (_m._used.isOdd) {
+      nsh += _Bigint._DIGIT_BITS;
     }
-    return x;
+    _m_nsh = nsh;
+    _norm_m = _m._lShift(nsh);
+    var nm_used = _norm_m._used;
+    assert(nm_used.isEven);
+    _mt_qd = new Uint32List(4);
+    _mt_qd[_Bigint._YT_LO] = _norm_m._digits[nm_used - 2];
+    _mt_qd[_Bigint._YT] = _norm_m._digits[nm_used - 1];
+    // Negate _norm_m so we can use _mulAdd instead of unimplemented _mulSub.
+    var neg_norm_m = _Bigint._ONE._dlShift(nm_used)._sub(_norm_m);
+    if (neg_norm_m._used < nm_used) {
+      _neg_norm_m_digits =
+          _Bigint._cloneDigits(neg_norm_m._digits, 0, nm_used, nm_used);
+    } else {
+      _neg_norm_m_digits = neg_norm_m._digits;
+    }
+    // _neg_norm_m_digits is read-only and has nm_used digits (possibly
+    // including several leading zeros) plus a leading zero for 64-bit
+    // processing.
+    _t_digits = new Uint32List(2*nm_used);
   }
 
-  _Bigint _revert(_Bigint x) {
-    return x;
+  int _convert(_Bigint x, Uint32List r_digits) {
+    var digits;
+    var used;
+    if (x._neg || x._compare(_m) >= 0) {
+      var r = x.rem(_m);
+      if (x._neg && !r._neg && r._used > 0) {
+        r = _m._sub(r);
+      }
+      assert(!r._neg);
+      used = r._used;
+      digits = r._digits;
+    } else {
+      used = x._used;
+      digits = x._digits;
+    }
+    var i = used + (used + 1);  // Copy leading zero if any.
+    while (--i >= 0) {
+      r_digits[i] = digits[i];
+    }
+    return used;
   }
 
-  void _reduce(_Bigint x) {
-    x._divRemTo(_m, null, x);
+  _Bigint _revert(Uint32List x_digits, int x_used) {
+    return new _Bigint(false, x_used, x_digits);
   }
 
-  void _sqrTo(_Bigint x, _Bigint r) {
-    x._sqrTo(r);
-    _reduce(r);
+  int _reduce(Uint32List x_digits, int x_used) {
+    // The function _remDigits(...) is optimized for reduction and equivalent to
+    // calling _convert(_revert(x_digits, x_used)._rem(_m), x_digits);
+    return _Bigint._remDigits(x_digits, x_used,
+                              _norm_m._digits, _norm_m._used,
+                              _neg_norm_m_digits,
+                              _m_nsh,
+                              _mt_qd,
+                              _t_digits,
+                              x_digits);
   }
 
-  void _mulTo(_Bigint x, _Bigint y, _Bigint r) {
-    x._mulTo(y, r);
-    _reduce(r);
+  int _sqr(Uint32List x_digits, int x_used, Uint32List r_digits) {
+    var r_used = _Bigint._sqrDigits(x_digits, x_used, r_digits);
+    return _reduce(r_digits, r_used);
+  }
+
+  int _mul(Uint32List x_digits, int x_used,
+           Uint32List y_digits, int y_used,
+           Uint32List r_digits) {
+    var r_used = _Bigint._mulDigits(x_digits, x_used,
+                                    y_digits, y_used,
+                                    r_digits);
+    return _reduce(r_digits, r_used);
   }
 }
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index d9f9e06..c44d18d 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -409,46 +409,25 @@
 }
 
 
-DEFINE_NATIVE_ENTRY(Bigint_setNeg, 2) {
-  const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0));
-  const Bool& neg = Bool::CheckedHandle(arguments->NativeArgAt(1));
-  bigint.set_neg(neg);
-  return Object::null();
-}
-
-
 DEFINE_NATIVE_ENTRY(Bigint_getUsed, 1) {
   const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0));
   return bigint.used();
 }
 
 
-DEFINE_NATIVE_ENTRY(Bigint_setUsed, 2) {
-  const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0));
-  const Smi& used = Smi::CheckedHandle(arguments->NativeArgAt(1));
-  bigint.set_used(used);
-  return Object::null();
-}
-
-
 DEFINE_NATIVE_ENTRY(Bigint_getDigits, 1) {
   const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0));
   return bigint.digits();
 }
 
 
-DEFINE_NATIVE_ENTRY(Bigint_setDigits, 2) {
-  const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0));
-  const TypedData& digits = TypedData::CheckedHandle(arguments->NativeArgAt(1));
+DEFINE_NATIVE_ENTRY(Bigint_allocate, 4) {
+  // First arg is null type arguments, since class Bigint is not parameterized.
+  const Bool& neg = Bool::CheckedHandle(arguments->NativeArgAt(1));
+  const Smi& used = Smi::CheckedHandle(arguments->NativeArgAt(2));
+  const TypedData& digits = TypedData::CheckedHandle(arguments->NativeArgAt(3));
   ASSERT(!digits.IsNull());
-  bigint.set_digits(digits);
-  return Object::null();
-}
-
-
-DEFINE_NATIVE_ENTRY(Bigint_allocate, 1) {
-  // Argument is null type arguments, since class Bigint is not parameterized.
-  return Bigint::New();
+  return Bigint::New(neg.value(), used.Value(), digits);
 }
 
 }  // namespace dart
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index 48f6dc1..26a996d 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -265,12 +265,10 @@
 
   _leftShiftWithMask32(count, mask)  native "Integer_leftShiftWithMask32";
 
-  // TODO(regis): Make this method private once the plumbing to invoke it from
-  // dart:math is in place. Move the argument checking to dart:math.
   // Return pow(this, e) % m.
   int modPow(int e, int m) {
-    if (e is! int) throw new ArgumentError(e);
-    if (m is! int) throw new ArgumentError(m);
+    if (e is! int || e < 0) throw new ArgumentError(e);
+    if (m is! int || m <= 0) throw new ArgumentError(m);
     if (e is _Bigint || m is _Bigint) {
       return _toBigint().modPow(e, m);
     }
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 2369a1c..f0de423 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -58,12 +58,9 @@
   V(Mint_bitLength, 1)                                                         \
   V(Mint_shlFromInt, 2)                                                        \
   V(Bigint_getNeg, 1)                                                          \
-  V(Bigint_setNeg, 2)                                                          \
   V(Bigint_getUsed, 1)                                                         \
-  V(Bigint_setUsed, 2)                                                         \
   V(Bigint_getDigits, 1)                                                       \
-  V(Bigint_setDigits, 2)                                                       \
-  V(Bigint_allocate, 1)                                                        \
+  V(Bigint_allocate, 4)                                                        \
   V(Double_getIsNegative, 1)                                                   \
   V(Double_getIsInfinite, 1)                                                   \
   V(Double_getIsNaN, 1)                                                        \
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 061d914..009c60c 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -3318,23 +3318,6 @@
         load->set_recognized_kind(kind);
         return ReturnDefinition(load);
       }
-      case MethodRecognizer::kBigint_setDigits: {
-        Value* receiver = Bind(BuildLoadThisVar(node->scope()));
-        LocalVariable* value_var =
-            node->scope()->LookupVariable(Symbols::Value(), true);
-        ASSERT(value_var != NULL);
-        Value* value = Bind(new(I) LoadLocalInstr(*value_var));
-        StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
-            Bigint::digits_offset(),
-            receiver,
-            value,
-            kEmitStoreBarrier,
-            node->token_pos());
-        Do(store);
-        ConstantInstr* null_const = new(I) ConstantInstr(
-            Object::ZoneHandle(I, Object::null()));
-        return ReturnDefinition(null_const);
-      }
       case MethodRecognizer::kBigint_getUsed: {
         Value* receiver = Bind(BuildLoadThisVar(node->scope()));
         LoadFieldInstr* load = new(I) LoadFieldInstr(
@@ -3346,23 +3329,6 @@
         load->set_recognized_kind(kind);
         return ReturnDefinition(load);
       }
-      case MethodRecognizer::kBigint_setUsed: {
-        Value* receiver = Bind(BuildLoadThisVar(node->scope()));
-        LocalVariable* value_var =
-            node->scope()->LookupVariable(Symbols::Value(), true);
-        ASSERT(value_var != NULL);
-        Value* value = Bind(new(I) LoadLocalInstr(*value_var));
-        StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
-            Bigint::used_offset(),
-            receiver,
-            value,
-            kNoStoreBarrier,
-            node->token_pos());
-        Do(store);
-        ConstantInstr* null_const = new(I) ConstantInstr(
-            Object::ZoneHandle(I, Object::null()));
-        return ReturnDefinition(null_const);
-      }
       case MethodRecognizer::kBigint_getNeg: {
         Value* receiver = Bind(BuildLoadThisVar(node->scope()));
         LoadFieldInstr* load = new(I) LoadFieldInstr(
@@ -3374,23 +3340,6 @@
         load->set_recognized_kind(kind);
         return ReturnDefinition(load);
       }
-      case MethodRecognizer::kBigint_setNeg: {
-        Value* receiver = Bind(BuildLoadThisVar(node->scope()));
-        LocalVariable* value_var =
-            node->scope()->LookupVariable(Symbols::Value(), true);
-        ASSERT(value_var != NULL);
-        Value* value = Bind(new(I) LoadLocalInstr(*value_var));
-        StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
-            Bigint::neg_offset(),
-            receiver,
-            value,
-            kEmitStoreBarrier,
-            node->token_pos());
-        Do(store);
-        ConstantInstr* null_const = new(I) ConstantInstr(
-            Object::ZoneHandle(I, Object::null()));
-        return ReturnDefinition(null_const);
-      }
       default:
         break;
     }
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 5a43fab..54a8692 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -799,27 +799,6 @@
 }
 
 
-void Intrinsifier::Bigint_setNeg(Assembler* assembler) {
-  __ ldrd(R0, Address(SP, 0 * kWordSize));  // R0 = this, R1 = neg value.
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::neg_offset()), R0, false);
-  __ Ret();
-}
-
-
-void Intrinsifier::Bigint_setUsed(Assembler* assembler) {
-  __ ldrd(R0, Address(SP, 0 * kWordSize));  // R0 = this, R1 = used value.
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::used_offset()), R0);
-  __ Ret();
-}
-
-
-void Intrinsifier::Bigint_setDigits(Assembler* assembler) {
-  __ ldrd(R0, Address(SP, 0 * kWordSize));  // R0 = this, R1 = digits value.
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::digits_offset()), R0, false);
-  __ Ret();
-}
-
-
 void Intrinsifier::Bigint_absAdd(Assembler* assembler) {
   // static void _absAdd(Uint32List digits, int used,
   //                     Uint32List a_digits, int a_used,
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index cc7c9df..c61db05 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -688,30 +688,6 @@
 }
 
 
-void Intrinsifier::Bigint_setNeg(Assembler* assembler) {
-  __ ldr(R0, Address(SP, 0 * kWordSize));
-  __ ldr(R1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::neg_offset()), R0, false);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setUsed(Assembler* assembler) {
-  __ ldr(R0, Address(SP, 0 * kWordSize));
-  __ ldr(R1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::used_offset()), R0);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setDigits(Assembler* assembler) {
-  __ ldr(R0, Address(SP, 0 * kWordSize));
-  __ ldr(R1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(R1, FieldAddress(R1, Bigint::digits_offset()), R0, false);
-  __ ret();
-}
-
-
 void Intrinsifier::Bigint_absAdd(Assembler* assembler) {
   // static void _absAdd(Uint32List digits, int used,
   //                     Uint32List a_digits, int a_used,
@@ -767,9 +743,12 @@
   __ cbnz(&carry_loop, R9);
 
   __ Bind(&last_carry);
-  __ adc(R0, ZR, ZR);
+  Label done;
+  __ b(&done, CC);
+  __ LoadImmediate(R0, 1, kNoPP);
   __ str(R0, Address(R6, 0));
 
+  __ Bind(&done);
   // Returning Object::null() is not required, since this method is private.
   __ ret();
 }
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index e3d8700..4fb2397 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -803,31 +803,6 @@
 }
 
 
-void Intrinsifier::Bigint_setNeg(Assembler* assembler) {
-  __ movl(EAX, Address(ESP, + 1 * kWordSize));
-  __ movl(ECX, Address(ESP, + 2 * kWordSize));
-  __ StoreIntoObject(ECX, FieldAddress(ECX, Bigint::neg_offset()), EAX, false);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setUsed(Assembler* assembler) {
-  __ movl(EAX, Address(ESP, + 1 * kWordSize));
-  __ movl(ECX, Address(ESP, + 2 * kWordSize));
-  __ StoreIntoObject(ECX, FieldAddress(ECX, Bigint::used_offset()), EAX);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setDigits(Assembler* assembler) {
-  __ movl(EAX, Address(ESP, + 1 * kWordSize));
-  __ movl(ECX, Address(ESP, + 2 * kWordSize));
-  __ StoreIntoObject(ECX,
-                     FieldAddress(ECX, Bigint::digits_offset()), EAX, false);
-  __ ret();
-}
-
-
 void Intrinsifier::Bigint_absAdd(Assembler* assembler) {
   // static void _absAdd(Uint32List digits, int used,
   //                     Uint32List a_digits, int a_used,
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index a2913b0..a6bff54 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -781,30 +781,6 @@
 }
 
 
-void Intrinsifier::Bigint_setNeg(Assembler* assembler) {
-  __ lw(T0, Address(SP, 0 * kWordSize));
-  __ lw(T1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(T1, FieldAddress(T1, Bigint::neg_offset()), T0, false);
-  __ Ret();
-}
-
-
-void Intrinsifier::Bigint_setUsed(Assembler* assembler) {
-  __ lw(T0, Address(SP, 0 * kWordSize));
-  __ lw(T1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(T1, FieldAddress(T1, Bigint::used_offset()), T0);
-  __ Ret();
-}
-
-
-void Intrinsifier::Bigint_setDigits(Assembler* assembler) {
-  __ lw(T0, Address(SP, 0 * kWordSize));
-  __ lw(T1, Address(SP, 1 * kWordSize));
-  __ StoreIntoObject(T1, FieldAddress(T1, Bigint::digits_offset()), T0, false);
-  __ Ret();
-}
-
-
 void Intrinsifier::Bigint_absAdd(Assembler* assembler) {
   // static void _absAdd(Uint32List digits, int used,
   //                     Uint32List a_digits, int a_used,
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index 7530497..a3f40bf 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -723,31 +723,6 @@
 }
 
 
-void Intrinsifier::Bigint_setNeg(Assembler* assembler) {
-  __ movq(RAX, Address(RSP, + 1 * kWordSize));
-  __ movq(RCX, Address(RSP, + 2 * kWordSize));
-  __ StoreIntoObject(RCX, FieldAddress(RCX, Bigint::neg_offset()), RAX, false);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setUsed(Assembler* assembler) {
-  __ movq(RAX, Address(RSP, + 1 * kWordSize));
-  __ movq(RCX, Address(RSP, + 2 * kWordSize));
-  __ StoreIntoObject(RCX, FieldAddress(RCX, Bigint::used_offset()), RAX);
-  __ ret();
-}
-
-
-void Intrinsifier::Bigint_setDigits(Assembler* assembler) {
-  __ movq(RAX, Address(RSP, + 1 * kWordSize));
-  __ movq(RCX, Address(RSP, + 2 * kWordSize));
-  __ StoreIntoObject(RCX,
-                     FieldAddress(RCX, Bigint::digits_offset()), RAX, false);
-  __ ret();
-}
-
-
 void Intrinsifier::Bigint_absAdd(Assembler* assembler) {
   // static void _absAdd(Uint32List digits, int used,
   //                     Uint32List a_digits, int a_used,
@@ -793,10 +768,12 @@
   __ j(NOT_ZERO, &carry_loop, Assembler::kNearJump);
 
   __ Bind(&last_carry);
-  __ movq(RAX, Immediate(0));
-  __ adcq(RAX, Immediate(0));
-  __ movq(FieldAddress(RBX, RDX, TIMES_8, TypedData::data_offset()), RAX);
+  Label done;
+  __ j(NOT_CARRY, &done);
+  __ movq(FieldAddress(RBX, RDX, TIMES_8, TypedData::data_offset()),
+          Immediate(1));
 
+  __ Bind(&done);
   // Returning Object::null() is not required, since this method is private.
   __ ret();
 }
diff --git a/runtime/vm/method_recognizer.h b/runtime/vm/method_recognizer.h
index ce71543..08260c3 100644
--- a/runtime/vm/method_recognizer.h
+++ b/runtime/vm/method_recognizer.h
@@ -155,15 +155,12 @@
 #define CORE_LIB_INTRINSIC_LIST(V)                                             \
   V(_Smi, ~, Smi_bitNegate, 134149043)                                         \
   V(_Smi, get:bitLength, Smi_bitLength, 869986288)                             \
-  V(_Bigint, set:_neg, Bigint_setNeg, 1924982939)                              \
-  V(_Bigint, set:_used, Bigint_setUsed, 1574448752)                            \
-  V(_Bigint, set:_digits, Bigint_setDigits, 1109140916)                        \
-  V(_Bigint, _absAdd, Bigint_absAdd, 97148049)                                 \
-  V(_Bigint, _absSub, Bigint_absSub, 159012285)                                \
-  V(_Bigint, _mulAdd, Bigint_mulAdd, 1625285265)                               \
-  V(_Bigint, _sqrAdd, Bigint_sqrAdd, 56442243)                                 \
-  V(_Bigint, _estQuotientDigit, Bigint_estQuotientDigit, 1844507489)           \
-  V(_Montgomery, _mulMod, Montgomery_mulMod, 1004817085)                       \
+  V(_Bigint, _absAdd, Bigint_absAdd, 222437051)                                \
+  V(_Bigint, _absSub, Bigint_absSub, 599465997)                                \
+  V(_Bigint, _mulAdd, Bigint_mulAdd, 1696801459)                               \
+  V(_Bigint, _sqrAdd, Bigint_sqrAdd, 1937424317)                               \
+  V(_Bigint, _estQuotientDigit, Bigint_estQuotientDigit, 1873913198)           \
+  V(_Montgomery, _mulMod, Montgomery_mulMod, 2040316431)                       \
   V(_Double, >, Double_greaterThan, 1538121903)                                \
   V(_Double, >=, Double_greaterEqualThan, 1058495718)                          \
   V(_Double, <, Double_lessThan, 62910596)                                     \
@@ -397,20 +394,17 @@
   V(::, tan, MathTan, 982072809)                                               \
   V(Lists, copy, ListsCopy, 605584668)                                         \
   V(_Bigint, get:_neg, Bigint_getNeg, 1151543890)                              \
-  V(_Bigint, set:_neg, Bigint_setNeg, 1924982939)                              \
   V(_Bigint, get:_used, Bigint_getUsed, 1308559334)                            \
-  V(_Bigint, set:_used, Bigint_setUsed, 1574448752)                            \
   V(_Bigint, get:_digits, Bigint_getDigits, 1408092463)                        \
-  V(_Bigint, set:_digits, Bigint_setDigits, 1109140916)                        \
 
 // A list of core function that should never be inlined.
 #define INLINE_BLACK_LIST(V)                                                   \
-  V(_Bigint, _absAdd, Bigint_absAdd, 97148049)                                 \
-  V(_Bigint, _absSub, Bigint_absSub, 159012285)                                \
-  V(_Bigint, _mulAdd, Bigint_mulAdd, 1625285265)                               \
-  V(_Bigint, _sqrAdd, Bigint_sqrAdd, 56442243)                                 \
-  V(_Bigint, _estQuotientDigit, Bigint_estQuotientDigit, 1844507489)           \
-  V(_Montgomery, _mulMod, Montgomery_mulMod, 1004817085)                       \
+  V(_Bigint, _absAdd, Bigint_absAdd, 222437051)                                \
+  V(_Bigint, _absSub, Bigint_absSub, 599465997)                                \
+  V(_Bigint, _mulAdd, Bigint_mulAdd, 1696801459)                               \
+  V(_Bigint, _sqrAdd, Bigint_sqrAdd, 1937424317)                               \
+  V(_Bigint, _estQuotientDigit, Bigint_estQuotientDigit, 1873913198)           \
+  V(_Montgomery, _mulMod, Montgomery_mulMod, 2040316431)                       \
 
 // A list of core functions that internally dispatch based on received id.
 #define POLYMORPHIC_TARGET_LIST(V)                                             \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 9e36fef..8bb0a39 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -16361,13 +16361,29 @@
 }
 
 
-void Bigint::set_neg(const Bool& value) const {
-  StorePointer(&raw_ptr()->neg_, value.raw());
+bool Bigint::Neg() const {
+  return Bool::Handle(neg()).value();
 }
 
 
-void Bigint::set_used(const Smi& value) const {
-  StoreSmi(&raw_ptr()->used_, value.raw());
+void Bigint::SetNeg(bool value) const {
+  StorePointer(&raw_ptr()->neg_, Bool::Get(value).raw());
+}
+
+
+intptr_t Bigint::Used() const {
+  return Smi::Value(used());
+}
+
+
+void Bigint::SetUsed(intptr_t value) const {
+  StoreSmi(&raw_ptr()->used_, Smi::New(value));
+}
+
+
+uint32_t Bigint::DigitAt(intptr_t index) const {
+  const TypedData& typed_data = TypedData::Handle(digits());
+  return typed_data.GetUint32(index << 2);
 }
 
 
@@ -16378,35 +16394,22 @@
 }
 
 
-bool Bigint::Neg() const {
-  return Bool::Handle(neg()).value();
+RawTypedData* Bigint::NewDigits(intptr_t length, Heap::Space space) {
+  ASSERT(length > 0);
+  // Account for leading zero for 64-bit processing.
+  return TypedData::New(kTypedDataUint32ArrayCid, length + 1, space);
 }
 
 
-void Bigint::SetNeg(bool value) const {
-  set_neg(Bool::Get(value));
+uint32_t Bigint::DigitAt(const TypedData& digits, intptr_t index) {
+  return digits.GetUint32(index << 2);
 }
 
 
-intptr_t Bigint::Used() const {
-  return Smi::Value(used());
-}
-
-
-void Bigint::SetUsed(intptr_t value) const {
-  set_used(Smi::Handle(Smi::New(value)));
-}
-
-
-uint32_t Bigint::DigitAt(intptr_t index) const {
-  const TypedData& typed_data = TypedData::Handle(digits());
-  return typed_data.GetUint32(index << 2);
-}
-
-
-void Bigint::SetDigitAt(intptr_t index, uint32_t value) const {
-  const TypedData& typed_data = TypedData::Handle(digits());
-  typed_data.SetUint32(index << 2, value);
+void Bigint::SetDigitAt(const TypedData& digits,
+                        intptr_t index,
+                        uint32_t value) {
+  digits.SetUint32(index << 2, value);
 }
 
 
@@ -16468,37 +16471,71 @@
     NoGCScope no_gc;
     result ^= raw;
   }
-  result.set_neg(Bool::Get(false));
-  result.set_used(Smi::Handle(isolate, Smi::New(0)));
+  result.SetNeg(false);
+  result.SetUsed(0);
   result.set_digits(
       TypedData::Handle(isolate, TypedData::EmptyUint32Array(isolate)));
   return result.raw();
 }
 
 
-RawBigint* Bigint::NewFromInt64(int64_t value, Heap::Space space) {
-  const Bigint& result = Bigint::Handle(New(space));
-  result.EnsureLength(2, space);
-  result.SetUsed(2);
-  if (value < 0) {
-    result.SetNeg(true);
-    value = -value;  // No concern about overflow, since sign is captured.
+RawBigint* Bigint::New(bool neg, intptr_t used, const TypedData& digits,
+                       Heap::Space space) {
+  ASSERT((used == 0) ||
+         (!digits.IsNull() && (digits.Length() >= (used + (used & 1)))));
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate->object_store()->bigint_class() != Class::null());
+  Bigint& result = Bigint::Handle(isolate);
+  {
+    RawObject* raw = Object::Allocate(Bigint::kClassId,
+                                      Bigint::InstanceSize(),
+                                      space);
+    NoGCScope no_gc;
+    result ^= raw;
   }
-  result.SetDigitAt(0, static_cast<uint32_t>(value));
-  result.SetDigitAt(1, static_cast<uint32_t>(value >> 32));  // value >= 0.
-  result.Clamp();
+  // Clamp the digits array.
+  while ((used > 0) && (digits.GetUint32((used - 1) << 2) == 0)) {
+    --used;
+  }
+  if (used > 0) {
+    if ((used & 1) != 0) {
+      // Set leading zero for 64-bit processing of digit pairs.
+      digits.SetUint32(used << 2, 0);
+    }
+    result.set_digits(digits);
+  } else {
+    neg = false;
+    result.set_digits(
+        TypedData::Handle(isolate, TypedData::EmptyUint32Array(isolate)));
+  }
+  result.SetNeg(neg);
+  result.SetUsed(used);
   return result.raw();
 }
 
 
+RawBigint* Bigint::NewFromInt64(int64_t value, Heap::Space space) {
+  const TypedData& digits = TypedData::Handle(NewDigits(2, space));
+  bool neg;
+  uint64_t abs_value;
+  if (value < 0) {
+    neg = true;
+    abs_value = -value;
+  } else {
+    neg = false;
+    abs_value = value;
+  }
+  SetDigitAt(digits, 0, static_cast<uint32_t>(abs_value));
+  SetDigitAt(digits, 1, static_cast<uint32_t>(abs_value >> 32));
+  return New(neg, 2, digits, space);
+}
+
+
 RawBigint* Bigint::NewFromUint64(uint64_t value, Heap::Space space) {
-  const Bigint& result = Bigint::Handle(New(space));
-  result.EnsureLength(2, space);
-  result.SetUsed(2);
-  result.SetDigitAt(0, static_cast<uint32_t>(value));
-  result.SetDigitAt(1, static_cast<uint32_t>(value >> 32));
-  result.Clamp();
-  return result.raw();
+  const TypedData& digits = TypedData::Handle(NewDigits(2, space));
+  SetDigitAt(digits, 0, static_cast<uint32_t>(value));
+  SetDigitAt(digits, 1, static_cast<uint32_t>(value >> 32));
+  return New(false, 2, digits, space);
 }
 
 
@@ -16506,95 +16543,52 @@
                                        Heap::Space space) {
   ASSERT(kBitsPerDigit == 32);
   ASSERT(shift >= 0);
-  const Bigint& result = Bigint::Handle(New(space));
   const intptr_t digit_shift = shift / kBitsPerDigit;
   const intptr_t bit_shift = shift % kBitsPerDigit;
-  result.EnsureLength(3 + digit_shift, space);
-  result.SetUsed(3 + digit_shift);
+  const intptr_t used = 3 + digit_shift;
+  const TypedData& digits = TypedData::Handle(NewDigits(used, space));
+  bool neg;
   uint64_t abs_value;
   if (value < 0) {
-    result.SetNeg(true);
-    abs_value = -value;  // No concern about overflow, since sign is captured.
+    neg = true;
+    abs_value = -value;
   } else {
+    neg = false;
     abs_value = value;
   }
   for (intptr_t i = 0; i < digit_shift; i++) {
-    result.SetDigitAt(i, 0);
+    SetDigitAt(digits, i, 0);
   }
-  result.SetDigitAt(0 + digit_shift,
-                    static_cast<uint32_t>(abs_value << bit_shift));
-  result.SetDigitAt(1 + digit_shift,
-                    static_cast<uint32_t>(abs_value >> (32 - bit_shift)));
-  result.SetDigitAt(2 + digit_shift,
+  SetDigitAt(digits, 0 + digit_shift,
+             static_cast<uint32_t>(abs_value << bit_shift));
+  SetDigitAt(digits, 1 + digit_shift,
+             static_cast<uint32_t>(abs_value >> (32 - bit_shift)));
+  SetDigitAt(digits, 2 + digit_shift,
       (bit_shift == 0) ? 0
                        : static_cast<uint32_t>(abs_value >> (64 - bit_shift)));
-  result.Clamp();
-  return result.raw();
-}
-
-
-void Bigint::EnsureLength(intptr_t length, Heap::Space space) const {
-  ASSERT(length >= 0);
-  length++;  // Account for leading zero for 64-bit processing.
-  TypedData& old_digits = TypedData::Handle(digits());
-  if (length > old_digits.Length()) {
-    TypedData& new_digits = TypedData::Handle(
-        TypedData::New(kTypedDataUint32ArrayCid, length + kExtraDigits, space));
-    set_digits(new_digits);
-    if (Used() > 0) {
-      TypedData::Copy(new_digits, TypedData::data_offset(),
-                      old_digits, TypedData::data_offset(),
-                      (Used() + 1)*kBytesPerDigit);  // Copy leading zero.
-    }
-  }
-}
-
-
-void Bigint::Clamp() const {
-  intptr_t used = Used();
-  if (used > 0) {
-    if (DigitAt(used - 1) == 0) {
-      do {
-        --used;
-      } while ((used > 0) && (DigitAt(used - 1) == 0));
-      SetUsed(used);
-    }
-    SetDigitAt(used, 0);  // Set leading zero for 64-bit processing.
-  }
-}
-
-
-bool Bigint::IsClamped() const {
-  intptr_t used = Used();
-  return (used == 0) || (DigitAt(used - 1) > 0);
+  return New(neg, used, digits, space);
 }
 
 
 RawBigint* Bigint::NewFromCString(const char* str, Heap::Space space) {
   ASSERT(str != NULL);
-  // If the string starts with '-' recursively restart the whole operation
-  // without the character and then toggle the sign.
+  bool neg = false;
+  TypedData& digits = TypedData::Handle();
   if (str[0] == '-') {
     ASSERT(str[1] != '-');
-    const Bigint& result = Bigint::Handle(NewFromCString(&str[1], space));
-    result.SetNeg(!result.Neg());  // Toggle sign.
-    ASSERT(result.IsZero() || result.IsNegative());
-    ASSERT(result.IsClamped());
-    return result.raw();
+    neg = true;
+    str++;
   }
-
-  // No overflow check needed since overflowing str_length implies that we take
-  // the branch to NewFromDecCString() which contains a check itself.
+  intptr_t used;
   const intptr_t str_length = strlen(str);
   if ((str_length >= 2) &&
       (str[0] == '0') &&
       ((str[1] == 'x') || (str[1] == 'X'))) {
-    const Bigint& result = Bigint::Handle(NewFromHexCString(&str[2], space));
-    ASSERT(result.IsClamped());
-    return result.raw();
+    digits = NewDigitsFromHexCString(&str[2], &used, space);
   } else {
-    return NewFromDecCString(str, space);
+    digits = NewDigitsFromDecCString(str, &used, space);
   }
+  return New(neg, used, digits, space);
 }
 
 
@@ -16627,28 +16621,17 @@
 }
 
 
-RawBigint* Bigint::NewFromHexCString(const char* str, Heap::Space space) {
-  // If the string starts with '-' recursively restart the whole operation
-  // without the character and then toggle the sign.
-  if (str[0] == '-') {
-    ASSERT(str[1] != '-');
-    const Bigint& result = Bigint::Handle(NewFromHexCString(&str[1], space));
-    if (!result.IsZero()) {
-      result.SetNeg(!result.Neg());  // Toggle sign.
-    }
-    ASSERT(result.IsClamped());
-    return result.raw();
-  }
-  const Bigint& result = Bigint::Handle(New(space));
+RawTypedData* Bigint::NewDigitsFromHexCString(const char* str, intptr_t* used,
+                                              Heap::Space space) {
   const int kBitsPerHexDigit = 4;
   const int kHexDigitsPerDigit = 8;
   const int kBitsPerDigit = kBitsPerHexDigit * kHexDigitsPerDigit;
   intptr_t hex_i = strlen(str);  // Terminating byte excluded.
   if ((hex_i <= 0) || (hex_i >= kMaxInt32)) {
-    FATAL("Fatal error in Bigint::NewFromHexCString: string too long or empty");
+    FATAL("Fatal error parsing hex bigint: string too long or empty");
   }
-  result.EnsureLength((hex_i + kHexDigitsPerDigit - 1) / kHexDigitsPerDigit,
-                      space);
+  const intptr_t length = (hex_i + kHexDigitsPerDigit - 1) / kHexDigitsPerDigit;
+  const TypedData& digits = TypedData::Handle(NewDigits(length, space));
   intptr_t used_ = 0;
   uint32_t digit = 0;
   intptr_t bit_i = 0;
@@ -16657,53 +16640,48 @@
     bit_i += kBitsPerHexDigit;
     if (bit_i == kBitsPerDigit) {
       bit_i = 0;
-      result.SetDigitAt(used_++, digit);
+      SetDigitAt(digits, used_++, digit);
       digit = 0;
     }
   }
   if (bit_i != 0) {
-    result.SetDigitAt(used_++, digit);
+    SetDigitAt(digits, used_++, digit);
   }
-  result.SetUsed(used_);
-  result.Clamp();
-  return result.raw();
+  *used = used_;
+  return digits.raw();
 }
 
 
-RawBigint* Bigint::NewFromDecCString(const char* str, Heap::Space space) {
+RawTypedData* Bigint::NewDigitsFromDecCString(const char* str, intptr_t* used,
+                                              Heap::Space space) {
   // Read 9 digits a time. 10^9 < 2^32.
   const int kDecDigitsPerIteration = 9;
   const uint32_t kTenMultiplier = 1000000000;
   ASSERT(kBitsPerDigit == 32);
-
   const intptr_t str_length = strlen(str);
   if ((str_length <= 0) || (str_length >= kMaxInt32)) {
-    FATAL("Fatal error in Bigint::NewFromDecCString: string too long or empty");
+    FATAL("Fatal error parsing dec bigint: string too long or empty");
   }
-  intptr_t str_pos = 0;
-
-  Bigint& result = Bigint::Handle(Bigint::New(space));
   // One decimal digit takes log2(10) bits, i.e. ~3.32192809489 bits.
   // That is a theoretical limit for large numbers.
-  // The extra digits allocated take care of variations (kExtraDigits).
+  // The extra 5 digits allocated take care of variations.
   const int64_t kLog10Dividend = 33219281;
   const int64_t kLog10Divisor = 10000000;
-
-  result.EnsureLength((kLog10Dividend * str_length) /
-                      (kLog10Divisor * kBitsPerDigit) + 1, space);
-
+  const intptr_t length = (kLog10Dividend * str_length) /
+                          (kLog10Divisor * kBitsPerDigit) + 5;
+  const TypedData& digits = TypedData::Handle(NewDigits(length, space));
   // Read first digit separately. This avoids a multiplication and addition.
   // The first digit might also not have kDecDigitsPerIteration decimal digits.
   const intptr_t lsdigit_length = str_length % kDecDigitsPerIteration;
   uint32_t digit = 0;
+  intptr_t str_pos = 0;
   for (intptr_t i = 0; i < lsdigit_length; i++) {
     char c = str[str_pos++];
     ASSERT(('0' <= c) && (c <= '9'));
     digit = digit * 10 + c - '0';
   }
-  result.SetDigitAt(0, digit);
-  intptr_t used = 1;
-
+  SetDigitAt(digits, 0, digit);
+  intptr_t used_ = 1;
   // Read kDecDigitsPerIteration at a time, and store it in 'digit'.
   // Then multiply the temporary result by 10^kDecDigitsPerIteration and add
   // 'digit' to the new result.
@@ -16715,17 +16693,16 @@
       digit = digit * 10 + c - '0';
     }
     // Multiply result with kTenMultiplier and add digit.
-    for (intptr_t i = 0; i < used; i++) {
+    for (intptr_t i = 0; i < used_; i++) {
       uint64_t product =
-          (static_cast<uint64_t>(result.DigitAt(i)) * kTenMultiplier) + digit;
-      result.SetDigitAt(i, static_cast<uint32_t>(product & kDigitMask));
+          (static_cast<uint64_t>(DigitAt(digits, i)) * kTenMultiplier) + digit;
+      SetDigitAt(digits, i, static_cast<uint32_t>(product & kDigitMask));
       digit = static_cast<uint32_t>(product >> kBitsPerDigit);
     }
-    result.SetDigitAt(used++, digit);
+    SetDigitAt(digits, used_++, digit);
   }
-  result.SetUsed(used);
-  result.Clamp();
-  return result.raw();
+  *used = used_;
+  return digits.raw();
 }
 
 
@@ -16775,7 +16752,6 @@
 
 double Bigint::AsDoubleValue() const {
   ASSERT(kBitsPerDigit == 32);
-  ASSERT(IsClamped());
   const intptr_t used = Used();
   if (used == 0) {
     return 0.0;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 90c3acf..c57f824 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5579,35 +5579,33 @@
     return RoundedAllocationSize(sizeof(RawBigint));
   }
 
+  // Offsets of fields accessed directly by optimized code.
+  static intptr_t neg_offset() { return OFFSET_OF(RawBigint, neg_); }
+  static intptr_t used_offset() { return OFFSET_OF(RawBigint, used_); }
+  static intptr_t digits_offset() { return OFFSET_OF(RawBigint, digits_); }
+
   // Accessors used by native calls from Dart.
   RawBool* neg() const { return raw_ptr()->neg_; }
-  void set_neg(const Bool& value) const;
-  static intptr_t neg_offset() { return OFFSET_OF(RawBigint, neg_); }
   RawSmi* used() const { return raw_ptr()->used_; }
-  void set_used(const Smi& value) const;
-  static intptr_t used_offset() { return OFFSET_OF(RawBigint, used_); }
   RawTypedData* digits() const { return raw_ptr()->digits_; }
-  void set_digits(const TypedData& value) const;
-  static intptr_t digits_offset() { return OFFSET_OF(RawBigint, digits_); }
 
   // Accessors used by runtime calls from C++.
   bool Neg() const;
-  void SetNeg(bool value) const;
   intptr_t Used() const;
-  void SetUsed(intptr_t value) const;
   uint32_t DigitAt(intptr_t index) const;
-  void SetDigitAt(intptr_t index, uint32_t value) const;
 
   const char* ToDecCString(uword (*allocator)(intptr_t size)) const;
   const char* ToHexCString(uword (*allocator)(intptr_t size)) const;
 
-  static const intptr_t kExtraDigits = 4;  // Same as _Bigint.EXTRA_DIGITS
-  static const intptr_t kBitsPerDigit = 32;  // Same as _Bigint.DIGIT_BITS
+  static const intptr_t kBitsPerDigit = 32;  // Same as _Bigint._DIGIT_BITS
   static const intptr_t kBytesPerDigit = 4;
   static const int64_t kDigitBase = 1LL << kBitsPerDigit;
   static const int64_t kDigitMask = kDigitBase - 1;
 
-  static RawBigint* New(Heap::Space space = Heap::kNew);
+  static RawBigint* New(Heap::Space space = Heap::kNew);  // For snapshots.
+
+  static RawBigint* New(bool neg, intptr_t used, const TypedData& digits,
+                        Heap::Space space = Heap::kNew);
 
   static RawBigint* NewFromInt64(int64_t value,
                                   Heap::Space space = Heap::kNew);
@@ -5625,19 +5623,23 @@
   static RawBigint* NewCanonical(const String& str);
 
  private:
-  static RawBigint* NewFromHexCString(const char* str,
-                                       Heap::Space space = Heap::kNew);
-  static RawBigint* NewFromDecCString(const char* str,
-                                       Heap::Space space = Heap::kNew);
+  void SetNeg(bool value) const;
+  void SetUsed(intptr_t value) const;
+  void set_digits(const TypedData& value) const;
 
-  // Make sure at least 'length' _digits are allocated.
-  // Copy existing _digits if reallocation is necessary.
-  void EnsureLength(intptr_t length, Heap::Space space = Heap::kNew) const;
+  // Convenience helpers.
+  static RawTypedData* NewDigits(intptr_t length,
+                                 Heap::Space space = Heap::kNew);
+  static uint32_t DigitAt(const TypedData& digits, intptr_t index);
+  static void SetDigitAt(const TypedData& digits,
+                         intptr_t index,
+                         uint32_t value);
 
-  // Do not count zero high digits as used.
-  void Clamp() const;
+  static RawTypedData* NewDigitsFromHexCString(const char* str, intptr_t* used,
+                                               Heap::Space space = Heap::kNew);
 
-  bool IsClamped() const;
+  static RawTypedData* NewDigitsFromDecCString(const char* str, intptr_t* used,
+                                               Heap::Space space = Heap::kNew);
 
   static RawBigint* Allocate(intptr_t length, Heap::Space space = Heap::kNew);