# [pypy-svn] pypy default: Refactor rbigint to use the proper types more explicitly:

Sun Feb 6 13:48:00 CET 2011

```Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r41647:04682c4565b6
Date: 2011-02-06 12:21 +0100
http://bitbucket.org/pypy/pypy/changeset/04682c4565b6/

Log:	Refactor rbigint to use the proper types more explicitly:

* the digits are now a list of rffi.INT, so that they are 32-bits
even on 64-bit machines

* to read a digit out of the list, there are now three functions:
digit() which returns an int, udigit() which returns an unsigned
int, and widedigit() which returns a longlong. Trying to use
carefully the right one at each place...

diff --git a/pypy/rlib/test/test_rbigint.py b/pypy/rlib/test/test_rbigint.py
--- a/pypy/rlib/test/test_rbigint.py
+++ b/pypy/rlib/test/test_rbigint.py
@@ -3,6 +3,7 @@
import operator, sys
from random import random, randint, sample
from pypy.rlib.rbigint import rbigint, SHIFT, MASK, KARATSUBA_CUTOFF
+from pypy.rlib.rbigint import _store_digit
from pypy.rlib import rbigint as lobj
from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong, intmask
from pypy.rpython.test.test_llinterp import interpret
@@ -17,7 +18,15 @@
r1 = getattr(rl_op1, op)(rl_op2)
r2 = getattr(operator, op)(op1, op2)
assert r1.tolong() == r2
-
+
+    def test_str(self):
+        for i in range(100):
+            n = 3 ** i
+            r1 = rbigint.fromlong(n)
+            assert r1.str() == str(n)
+            r2 = rbigint.fromlong(-n)
+            assert r2.str() == str(-n)
+
def test_floordiv(self):
for op1 in [-12, -2, -1, 1, 2, 50]:
for op2 in [-4, -2, -1, 1, 2, 8]:
@@ -67,47 +76,52 @@
yield s
yield -s

+def bigint(lst, sign):
+    for digit in lst:
+        assert digit & MASK == digit    # wrongly written test!
+    return rbigint(map(_store_digit, lst), sign)
+

class Test_rbigint(object):

def test_args_from_long(self):
BASE = 1 << SHIFT
-        assert rbigint.fromlong(0).eq(rbigint([0], 0))
-        assert rbigint.fromlong(17).eq(rbigint([17], 1))
-        assert rbigint.fromlong(BASE).eq(rbigint([0, 1], 1))
-        assert rbigint.fromlong(BASE**2).eq(rbigint([0, 0, 1], 1))
-        assert rbigint.fromlong(-17).eq(rbigint([17], -1))
-        assert rbigint.fromlong(-BASE).eq(rbigint([0, 1], -1))
-        assert rbigint.fromlong(-(BASE**2)).eq(rbigint([0, 0, 1], -1))
+        assert rbigint.fromlong(0).eq(bigint([0], 0))
+        assert rbigint.fromlong(17).eq(bigint([17], 1))
+        assert rbigint.fromlong(BASE).eq(bigint([0, 1], 1))
+        assert rbigint.fromlong(BASE**2).eq(bigint([0, 0, 1], 1))
+        assert rbigint.fromlong(-17).eq(bigint([17], -1))
+        assert rbigint.fromlong(-BASE).eq(bigint([0, 1], -1))
+        assert rbigint.fromlong(-(BASE**2)).eq(bigint([0, 0, 1], -1))
#        assert rbigint.fromlong(-sys.maxint-1).eq(
#            rbigint.digits_for_most_neg_long(-sys.maxint-1), -1)

def test_args_from_int(self):
BASE = 1 << SHIFT
MAX = int(BASE-1)
-        assert rbigint.fromrarith_int(0).eq(rbigint([0], 0))
-        assert rbigint.fromrarith_int(17).eq(rbigint([17], 1))
-        assert rbigint.fromrarith_int(MAX).eq(rbigint([MAX], 1))
-        assert rbigint.fromrarith_int(r_longlong(BASE)).eq(rbigint([0, 1], 1))
+        assert rbigint.fromrarith_int(0).eq(bigint([0], 0))
+        assert rbigint.fromrarith_int(17).eq(bigint([17], 1))
+        assert rbigint.fromrarith_int(MAX).eq(bigint([MAX], 1))
+        assert rbigint.fromrarith_int(r_longlong(BASE)).eq(bigint([0, 1], 1))
assert rbigint.fromrarith_int(r_longlong(BASE**2)).eq(
-            rbigint([0, 0, 1], 1))
-        assert rbigint.fromrarith_int(-17).eq(rbigint([17], -1))
-        assert rbigint.fromrarith_int(-MAX).eq(rbigint([MAX], -1))
-        assert rbigint.fromrarith_int(-MAX-1).eq(rbigint([0, 1], -1))
+            bigint([0, 0, 1], 1))
+        assert rbigint.fromrarith_int(-17).eq(bigint([17], -1))
+        assert rbigint.fromrarith_int(-MAX).eq(bigint([MAX], -1))
+        assert rbigint.fromrarith_int(-MAX-1).eq(bigint([0, 1], -1))
assert rbigint.fromrarith_int(r_longlong(-(BASE**2))).eq(
-            rbigint([0, 0, 1], -1))
+            bigint([0, 0, 1], -1))
#        assert rbigint.fromrarith_int(-sys.maxint-1).eq((
#            rbigint.digits_for_most_neg_long(-sys.maxint-1), -1)

def test_args_from_uint(self):
BASE = 1 << SHIFT
-        assert rbigint.fromrarith_int(r_uint(0)).eq(rbigint([0], 0))
-        assert rbigint.fromrarith_int(r_uint(17)).eq(rbigint([17], 1))
-        assert rbigint.fromrarith_int(r_uint(BASE)).eq(rbigint([0, 1], 1))
-        #assert rbigint.fromrarith_int(r_uint(BASE**2)).eq(rbigint([0], 0))
+        assert rbigint.fromrarith_int(r_uint(0)).eq(bigint([0], 0))
+        assert rbigint.fromrarith_int(r_uint(17)).eq(bigint([17], 1))
+        assert rbigint.fromrarith_int(r_uint(BASE)).eq(bigint([0, 1], 1))
+        #assert rbigint.fromrarith_int(r_uint(BASE**2)).eq(bigint([0], 0))
assert rbigint.fromrarith_int(r_uint(sys.maxint)).eq(
rbigint.fromint(sys.maxint))
assert rbigint.fromrarith_int(r_uint(sys.maxint+1)).eq(
@@ -175,7 +189,7 @@
x = x ** 100
f1 = rbigint.fromlong(x)
assert raises(OverflowError, f1.tofloat)
-        f2 = rbigint([0, 2097152], 1)
+        f2 = rbigint.fromlong(2097152 << SHIFT)
d = f2.tofloat()
assert d == float(2097152 << SHIFT)

@@ -290,12 +304,30 @@
f3 = rbigint.fromlong(z)
v = f1.pow(f2, f3)
assert v.tolong() == pow(x, y, z)
+        f3n = f3.neg()
+        v = f1.pow(f2, f3n)
+        assert v.tolong() == pow(x, y, -z)
+        #
f1, f2, f3 = [rbigint.fromlong(i)
for i in (10L, -1L, 42L)]
py.test.raises(TypeError, f1.pow, f2, f3)
f1, f2, f3 = [rbigint.fromlong(i)
for i in (10L, 5L, 0L)]
py.test.raises(ValueError, f1.pow, f2, f3)
+        #
+        MAX = 1E40
+        x = long(random() * MAX) + 1
+        y = long(random() * MAX) + 1
+        z = long(random() * MAX) + 1
+        f1 = rbigint.fromlong(x)
+        f2 = rbigint.fromlong(y)
+        f3 = rbigint.fromlong(z)
+        print f1
+        print f2
+        print f3
+        v = f1.pow(f2, f3)
+        print '--->', v
+        assert v.tolong() == pow(x, y, z)

def test_pow_lln(self):
x = 10L
@@ -306,10 +338,10 @@
assert v.tolong() == x ** y

def test_normalize(self):
-        f1 = rbigint([1, 0], 1)
+        f1 = bigint([1, 0], 1)
f1._normalize()
-        assert len(f1.digits) == 1
-        f0 = rbigint([0], 0)
+        assert len(f1._digits) == 1
+        f0 = bigint([0], 0)
assert f1.sub(f1).eq(f0)

def test_invert(self):
@@ -394,11 +426,13 @@
def test__inplace_divrem1(self):
# signs are not handled in the helpers!
for x, y in [(1238585838347L, 3), (1234123412311231L, 1231231), (99, 100)]:
+                continue
f1 = rbigint.fromlong(x)
f2 = y
remainder = lobj._inplace_divrem1(f1, f1, f2)
assert (f1.tolong(), remainder) == divmod(x, y)
-        out = rbigint([99, 99], 1)
+        out = bigint([99, 99], 1)
remainder = lobj._inplace_divrem1(out, out, 100)

def test__divrem1(self):
@@ -447,16 +481,16 @@

# testing Karatsuba stuff
-        f1 = rbigint([lobj.MASK] * 10, 1)
-        f2 = rbigint([1], 1)
-        carry = lobj._v_iadd(f1, 1, len(f1.digits)-1, f2, 1)
+        f1 = bigint([lobj.MASK] * 10, 1)
+        f2 = bigint([1], 1)
+        carry = lobj._v_iadd(f1, 1, len(f1._digits)-1, f2, 1)
assert carry == 1

def test__v_isub(self):
-        f1 = rbigint([lobj.MASK] + [0] * 9 + [1], 1)
-        f2 = rbigint([1], 1)
-        borrow = lobj._v_isub(f1, 1, len(f1.digits)-1, f2, 1)
+        f1 = bigint([lobj.MASK] + [0] * 9 + [1], 1)
+        f2 = bigint([1], 1)
+        borrow = lobj._v_isub(f1, 1, len(f1._digits)-1, f2, 1)
assert borrow == 0
assert f1.tolong() == (1 << lobj.SHIFT) ** 10 - 1

@@ -464,23 +498,23 @@
split = 5
diglo = [0] * split
-        f1 = rbigint(diglo + dighi, 1)
+        f1 = bigint(diglo + dighi, 1)
hi, lo = lobj._kmul_split(f1, split)
-        assert lo.digits == [0]
-        assert hi.digits == dighi
+        assert lo._digits == [_store_digit(0)]
+        assert hi._digits == map(_store_digit, dighi)

def test__k_mul(self):
digs = KARATSUBA_CUTOFF * 5
-        f1 = rbigint([lobj.MASK] * digs, 1)
+        f1 = bigint([lobj.MASK] * digs, 1)
+        f2 = lobj._x_add(f1, bigint([1], 1))
ret = lobj._k_mul(f1, f2)
assert ret.tolong() == f1.tolong() * f2.tolong()

def test__k_lopsided_mul(self):
digs_a = KARATSUBA_CUTOFF + 3
digs_b = 3 * digs_a
-        f1 = rbigint([lobj.MASK] * digs_a, 1)
-        f2 = rbigint([lobj.MASK] * digs_b, 1)
+        f1 = bigint([lobj.MASK] * digs_a, 1)
+        f2 = bigint([lobj.MASK] * digs_b, 1)
ret = lobj._k_lopsided_mul(f1, f2)
assert ret.tolong() == f1.tolong() * f2.tolong()

@@ -547,7 +581,9 @@
class TestTranslatable(object):
def test_square(self):
def test():
-            x = rbigint([1410065408, 4], 1)
+            xlo = rbigint.fromint(1410065408)
+            xhi = rbigint.fromint(4)
+            x = xlo.or_(xhi.lshift(31))
y = x.mul(x)
return y.str()
res = interpret(test, [])

diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -3,6 +3,8 @@
from pypy.rlib.rarithmetic import most_neg_value_of_same_type
from pypy.rlib.debug import make_sure_not_resized, check_regular_int
from pypy.rlib.objectmodel import we_are_translated
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython import extregistry

import math, sys

@@ -39,36 +41,84 @@
FIVEARY_CUTOFF = 8

+    if not we_are_translated():
+        assert type(x) is not long, "overflow occurred!"

-def widen_digit(x):
+def _widen_digit(x):
+    if not we_are_translated():
+        assert type(x) is int, "widen_digit() takes an int, got a %r" % type(x)
if SHIFT <= 15:
return int(x)
return r_longlong(x)

+def _store_digit(x):
+    if not we_are_translated():
+        assert type(x) is int, "store_digit() takes an int, got a %r" % type(x)
+    if SHIFT <= 15:
+        return rffi.cast(rffi.SHORT, x)
+    elif SHIFT <= 31:
+        return rffi.cast(rffi.INT, x)
+    else:
+        raise ValueError("SHIFT too large!")
+
+    return rffi.cast(lltype.Signed, x)
+
+    return rffi.cast(lltype.Unsigned, x)
+
+NULLDIGIT = _store_digit(0)
+
+def _check_digits(l):
+    for x in l:
+        assert type(x) is type(NULLDIGIT)
+class Entry(extregistry.ExtRegistryEntry):
+    def compute_result_annotation(self, s_list):
+        from pypy.annotation import model as annmodel
+        assert isinstance(s_list, annmodel.SomeList)
+        s_DIGIT = self.bookkeeper.valueoftype(type(NULLDIGIT))
+        assert s_DIGIT.contains(s_list.listdef.listitem.s_value)
+    def specialize_call(self, hop):
+        pass
+

class rbigint(object):
"""This is a reimplementation of longs using a list of digits."""
-    # XXX relace the list of ints with a list of rffi.INTs, maybe
-
-    def __init__(self, digits=None, sign=0):
-        if digits is None or len(digits) == 0:
-            digits = [0]
+
+    def __init__(self, digits=[], sign=0):
+        if len(digits) == 0:
+            digits = [NULLDIGIT]
+        _check_digits(digits)
make_sure_not_resized(digits)
-        self.digits = digits
+        self._digits = digits
self.sign = sign

-    def _digit(self, x):
-        return widen_digit(self.digits[x])
-    def _setdigit(self, x, val):
+    def digit(self, x):
+        """Return the x'th digit, as an int."""
+
+    def widedigit(self, x):
+        """Return the x'th digit, as a long long int if needed
+        to have enough room to contain two digits."""
+
+    def udigit(self, x):
+        """Return the x'th digit, as an unsigned int."""
+
+    def setdigit(self, x, val):
assert val >= 0
-        self.digits[x] = int(val)
-    _setdigit._annspecialcase_ = 'specialize:argtype(2)'
-    def _numdigits(self):
-        return len(self.digits)
+        self._digits[x] = _store_digit(val)
+    setdigit._annspecialcase_ = 'specialize:argtype(2)'
+
+    def numdigits(self):
+        return len(self._digits)

def fromint(intval):
check_regular_int(intval)
@@ -89,11 +139,11 @@
while t:
ndigits += 1
t >>= SHIFT
-        v = rbigint([0] * ndigits, sign)
+        v = rbigint([NULLDIGIT] * ndigits, sign)
t = ival
p = 0
while t:
-            v._setdigit(p, t)
+            v.setdigit(p, t)
t >>= SHIFT
p += 1
return v
@@ -121,11 +171,11 @@
if expo <= 0:
return rbigint()
ndig = (expo-1) // SHIFT + 1 # Number of 'digits' in result
-        v = rbigint([0] * ndig, 1)
+        v = rbigint([NULLDIGIT] * ndig, 1)
frac = math.ldexp(frac, (expo-1) % SHIFT + 1)
for i in range(ndig-1, -1, -1):
-            v._setdigit(i, bits)
+            bits = int(frac)
+            v.setdigit(i, bits)
frac -= float(bits)
frac = math.ldexp(frac, SHIFT)
if neg:
@@ -170,10 +220,10 @@

def _touint_helper(self):
x = r_uint(0)
-        i = self._numdigits() - 1
+        i = self.numdigits() - 1
while i >= 0:
prev = x
-            x = r_uint((x << SHIFT) + self.digits[i])
+            x = (x << SHIFT) + self.udigit(i)
if (x >> SHIFT) != prev:
raise OverflowError(
"long int too large to convert to unsigned int")
@@ -195,26 +245,6 @@
def tofloat(self):
return _AsDouble(self)

-    def _count_bits(self):
-        # return the number of bits in the digits
-        if self.sign == 0:
-            return 0
-        p = self._numdigits() - 1
-        bits = SHIFT * p
-        digit = self.digits[p]
-        while digit:
-            digit >>= 1
-            bits += 1
-        return bits
-
-    def is_odd(self):
-        # Note: this is a tiny optimization.
-        # Instead of implementing a general "get_bit" operation,
-        # which would be expensive for negative numbers,
-        # get_odd has the nice feature that it is always correct,
-        # no matter what the sign is (two's complement)
-        return self.digits[0] & 1
-
def format(self, digits, prefix='', suffix=''):
# 'digits' is a string whose length is the base to use,
# and where each character is the corresponding digit.
@@ -228,12 +258,12 @@

def eq(self, other):
if (self.sign != other.sign or
-            self._numdigits() != other._numdigits()):
+            self.numdigits() != other.numdigits()):
return False
i = 0
-        ld = self._numdigits()
+        ld = self.numdigits()
while i < ld:
-            if self.digits[i] != other.digits[i]:
+            if self.digit(i) != other.digit(i):
return False
i += 1
return True
@@ -246,8 +276,8 @@
return False
if self.sign < other.sign:
return True
-        ld1 = self._numdigits()
-        ld2 = other._numdigits()
+        ld1 = self.numdigits()
+        ld2 = other.numdigits()
if ld1 > ld2:
if other.sign > 0:
return False
@@ -260,8 +290,8 @@
return False
i = ld1 - 1
while i >= 0:
-            d1 = self.digits[i]
-            d2 = other.digits[i]
+            d1 = self.digit(i)
+            d2 = other.digit(i)
if d1 < d2:
if other.sign > 0:
return True
@@ -303,7 +333,7 @@
if other.sign == 0:
return self
if self.sign == 0:
-            return rbigint(other.digits[:], -other.sign)
+            return rbigint(other._digits[:], -other.sign)
if self.sign == other.sign:
result = _x_sub(self, other)
else:
@@ -355,7 +385,7 @@
div, mod = _divrem(v, w)
if mod.sign * w.sign == -1:
-            div = div.sub(rbigint([1], 1))
+            div = div.sub(rbigint([_store_digit(1)], 1))
return div, mod

def pow(a, b, c=None):
@@ -382,11 +412,11 @@
#     modulus = -modulus
if c.sign < 0:
negativeOutput = True
-                c = rbigint(c.digits, -c.sign)
+                c = c.neg()

# if modulus == 1:
#     return 0
-            if c._numdigits() == 1 and c.digits[0] == 1:
+            if c.numdigits() == 1 and c.digit(0) == 1:
return rbigint()

# if base < 0:
@@ -399,16 +429,16 @@
# At this point a, b, and c are guaranteed non-negative UNLESS
# c is NULL, in which case a may be negative. */

-        z = rbigint([1], 1)
+        z = rbigint([_store_digit(1)], 1)

# python adaptation: moved macros REDUCE(X) and MULT(X, Y, result)
# into helper function result = _help_mult(x, y, c)
-        if b._numdigits() <= FIVEARY_CUTOFF:
+        if b.numdigits() <= FIVEARY_CUTOFF:
# Left-to-right binary exponentiation (HAC Algorithm 14.79)
-            i = b._numdigits() - 1
+            i = b.numdigits() - 1
while i >= 0:
-                bi = b.digits[i]
+                bi = b.digit(i)
j = 1 << (SHIFT-1)
while j != 0:
z = _help_mult(z, z, c)
@@ -418,14 +448,15 @@
i -= 1
else:
# Left-to-right 5-ary exponentiation (HAC Algorithm 14.82)
+            # This is only useful in the case where c != None.
# z still holds 1L
table = [z] * 32
-            table[0] = z;
+            table[0] = z
for i in range(1, 32):
table[i] = _help_mult(table[i-1], a, c)
-            i = b._numdigits() - 1
+            i = b.numdigits() - 1
while i >= 0:
-                bi = b.digits[i]
+                bi = b.digit(i)
j = SHIFT - 5
while j >= 0:
index = (bi >> j) & 0x1f
@@ -441,13 +472,13 @@
return z

def neg(self):
-        return rbigint(self.digits, -self.sign)
+        return rbigint(self._digits, -self.sign)

def abs(self):
-        return rbigint(self.digits, abs(self.sign))
+        return rbigint(self._digits, abs(self.sign))

def invert(self): #Implement ~x as -(x + 1)

def lshift(self, int_other):
if int_other < 0:
@@ -459,22 +490,22 @@
wordshift = int_other // SHIFT
remshift  = int_other - wordshift * SHIFT

-        oldsize = self._numdigits()
+        oldsize = self.numdigits()
newsize = oldsize + wordshift
if remshift:
newsize += 1
-        z = rbigint([0] * newsize, self.sign)
-        accum = 0
+        z = rbigint([NULLDIGIT] * newsize, self.sign)
+        accum = _widen_digit(0)
i = wordshift
j = 0
while j < oldsize:
-            accum |= self._digit(j) << remshift
-            z._setdigit(i, accum)
+            accum |= self.widedigit(j) << remshift
+            z.setdigit(i, accum)
accum >>= SHIFT
i += 1
j += 1
if remshift:
-            z._setdigit(newsize - 1, accum)
+            z.setdigit(newsize - 1, accum)
else:
assert not accum
z._normalize()
@@ -491,21 +522,22 @@
return a2.invert()

wordshift = int_other // SHIFT
-        newsize = self._numdigits() - wordshift
+        newsize = self.numdigits() - wordshift
if newsize <= 0:
return rbigint()

loshift = int_other % SHIFT
hishift = SHIFT - loshift
-        lomask = (1 << hishift) - 1
-        z = rbigint([0] * newsize, self.sign)
+        z = rbigint([NULLDIGIT] * newsize, self.sign)
i = 0
j = wordshift
while i < newsize:
-            z.digits[i] = (self.digits[j] >> loshift) & lomask
+            newdigit = (self.digit(j) >> loshift) & lomask
if i+1 < newsize:
-                z.digits[i] |= (self.digits[j+1] << hishift) & himask
+            z.setdigit(i, newdigit)
i += 1
j += 1
z._normalize()
@@ -538,34 +570,35 @@
ret /= math.log(base)
return ret

-    def tolong(self): #YYYYYY
-        l = 0
-        digits = list(self.digits)
+    def tolong(self):
+        "NOT_RPYTHON"
+        l = 0L
+        digits = list(self._digits)
digits.reverse()
for d in digits:
l = l << SHIFT
-            l += long(d)
return l * self.sign

def _normalize(self):
-        if self._numdigits() == 0:
+        if self.numdigits() == 0:
self.sign = 0
-            self.digits = [0]
+            self._digits = [NULLDIGIT]
return
-        i = self._numdigits()
-        while i > 1 and self.digits[i - 1] == 0:
+        i = self.numdigits()
+        while i > 1 and self.digit(i - 1) == 0:
i -= 1
assert i >= 1
-        if i != self._numdigits():
-            self.digits = self.digits[:i]
-        if self._numdigits() == 1 and self.digits[0] == 0:
+        if i != self.numdigits():
+            self._digits = self._digits[:i]
+        if self.numdigits() == 1 and self.digit(0) == 0:
self.sign = 0

def bit_length(self):
-        i = self._numdigits()
-        if i == 1 and self.digits[0] == 0:
+        i = self.numdigits()
+        if i == 1 and self.digit(0) == 0:
return 0
-        msd = self.digits[i - 1]
+        msd = self.digit(i - 1)
msd_bits = 0
while msd >= 32:
msd_bits += 6
@@ -580,7 +613,8 @@
return bits

def __repr__(self):
-        return "<rbigint digits=%s, sign=%s, %s>" % (self.digits, self.sign, self.str())
+        return "<rbigint digits=%s, sign=%s, %s>" % (self._digits,
+                                                     self.sign, self.str())

#_________________________________________________________________

@@ -605,7 +639,7 @@
def digits_from_nonneg_long(l):
digits = []
while True:
l = l >> SHIFT
if not l:
return digits[:] # to make it non-resizable
@@ -615,15 +649,15 @@
# This helper only works if 'l' is the most negative integer of its
# type, which in base 2 looks like: 1000000..0000
digits = []
-        digits.append(0)
+        digits.append(NULLDIGIT)
l = l >> SHIFT
# now 'l' looks like: ...111100000
# turn it into:       ...000100000
# to drop the extra unwanted 1's introduced by the signed right shift
-    assert l >= 0
-    digits.append(l)
+    assert l & MASK == l
+    digits.append(_store_digit(l))
return digits[:] # to make it non-resizable
digits_for_most_neg_long._annspecialcase_ = "specialize:argtype(0)"

@@ -631,7 +665,7 @@
if x > 0:
return digits_from_nonneg_long(x), 1
elif x == 0:
-        return [0], 0
+        return [NULLDIGIT], 0
elif x != most_neg_value_of_same_type(x):
# normal case
return digits_from_nonneg_long(-x), -1
@@ -650,42 +684,42 @@
"NOT_RPYTHON"
if x >= 0:
if x == 0:
-            return [0], 0
+            return [NULLDIGIT], 0
else:
return digits_from_nonneg_long(x), 1
else:
-        return digits_from_nonneg_long(-long(x)), -1
+        return digits_from_nonneg_long(-x), -1

""" Add the absolute values of two bigint integers. """
-    size_a = a._numdigits()
-    size_b = b._numdigits()
+    size_a = a.numdigits()
+    size_b = b.numdigits()

# Ensure a is the larger of the two:
if size_a < size_b:
a, b = b, a
size_a, size_b = size_b, size_a
-    z = rbigint([0] * (a._numdigits() + 1), 1)
+    z = rbigint([NULLDIGIT] * (a.numdigits() + 1), 1)
i = 0
-    carry = 0
+    carry = r_uint(0)
while i < size_b:
-        carry += a._digit(i) + b._digit(i)
-        z._setdigit(i, carry)
+        carry += a.udigit(i) + b.udigit(i)
+        z.setdigit(i, carry)
carry >>= SHIFT
i += 1
while i < size_a:
-        carry += a._digit(i)
-        z._setdigit(i, carry)
+        carry += a.udigit(i)
+        z.setdigit(i, carry)
carry >>= SHIFT
i += 1
-    z._setdigit(i, carry)
+    z.setdigit(i, carry)
z._normalize()
return z

def _x_sub(a, b):
""" Subtract the absolute values of two integers. """
-    size_a = a._numdigits()
-    size_b = b._numdigits()
+    size_a = a.numdigits()
+    size_b = b.numdigits()
sign = 1

# Ensure a is the larger of the two:
@@ -696,34 +730,32 @@
elif size_a == size_b:
# Find highest digit where a and b differ:
i = size_a - 1
-        while i >= 0 and a.digits[i] == b.digits[i]:
+        while i >= 0 and a.digit(i) == b.digit(i):
i -= 1
if i < 0:
-            return rbigint([0], 0)
-        if a.digits[i] < b.digits[i]:
+            return rbigint()
+        if a.digit(i) < b.digit(i):
sign = -1
a, b = b, a
size_a = size_b = i+1
-    borrow = 0
-    z = rbigint([0] * size_a, 1)
+    z = rbigint([NULLDIGIT] * size_a, sign)
+    borrow = r_uint(0)
i = 0
while i < size_b:
# The following assumes unsigned arithmetic
# works modulo 2**N for some N>SHIFT.
-        borrow = a._digit(i) - b._digit(i) - borrow
-        z._setdigit(i, borrow)
+        borrow = a.udigit(i) - b.udigit(i) - borrow
+        z.setdigit(i, borrow)
borrow >>= SHIFT
borrow &= 1 # Keep only one sign bit
i += 1
while i < size_a:
-        borrow = a._digit(i) - borrow
-        z._setdigit(i, borrow)
+        borrow = a.udigit(i) - borrow
+        z.setdigit(i, borrow)
borrow >>= SHIFT
borrow &= 1 # Keep only one sign bit
i += 1
assert borrow == 0
-    if sign < 0:
-        z.sign = -1
z._normalize()
return z

@@ -734,9 +766,9 @@
Returns the absolute value of the product, or None if error.
"""

-    size_a = a._numdigits()
-    size_b = b._numdigits()
-    z = rbigint([0] * (size_a + size_b), 1)
+    size_a = a.numdigits()
+    size_b = b.numdigits()
+    z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
if a is b:
# Efficient squaring per HAC, Algorithm 14.16:
@@ -745,13 +777,13 @@
# pyramid appears twice (except for the size_a squares).
i = 0
while i < size_a:
-            f = a._digit(i)
+            f = a.widedigit(i)
pz = i << 1
pa = i + 1
paend = size_a

-            carry = z._digit(pz) + f * f
-            z._setdigit(pz, carry)
+            carry = z.widedigit(pz) + f * f
+            z.setdigit(pz, carry)
pz += 1
carry >>= SHIFT
@@ -760,19 +792,19 @@
# pyramid it appears.  Same as adding f<<1 once.
f <<= 1
while pa < paend:
-                carry += z._digit(pz) + a._digit(pa) * f
+                carry += z.widedigit(pz) + a.widedigit(pa) * f
pa += 1
-                z._setdigit(pz, carry)
+                z.setdigit(pz, carry)
pz += 1
carry >>= SHIFT
-                assert carry <= (widen_digit(MASK) << 1)
+                assert carry <= (_widen_digit(MASK) << 1)
if carry:
-                carry += z._digit(pz)
-                z._setdigit(pz, carry)
+                carry += z.widedigit(pz)
+                z.setdigit(pz, carry)
pz += 1
carry >>= SHIFT
if carry:
-                z._setdigit(pz, z._digit(pz) + carry)
+                z.setdigit(pz, z.widedigit(pz) + carry)
assert (carry >> SHIFT) == 0
i += 1
else:
@@ -780,19 +812,19 @@
i = 0
while i < size_a:
carry = 0
-            f = a._digit(i)
+            f = a.widedigit(i)
pz = i
pb = 0
pbend = size_b
while pb < pbend:
-                carry += z._digit(pz) + b._digit(pb) * f
+                carry += z.widedigit(pz) + b.widedigit(pb) * f
pb += 1
-                z._setdigit(pz, carry)
+                z.setdigit(pz, carry)
pz += 1
carry >>= SHIFT
if carry:
-                z._setdigit(pz, z._digit(pz) + carry)
+                z.setdigit(pz, z.widedigit(pz) + carry)
assert (carry >> SHIFT) == 0
i += 1
z._normalize()
@@ -807,11 +839,11 @@
viewing the shift as being by digits.  The sign bit is ignored, and
the return values are >= 0.
"""
-    size_n = n._numdigits()
+    size_n = n.numdigits()
size_lo = min(size_n, size)

-    lo = rbigint(n.digits[:size_lo], 1)
-    hi = rbigint(n.digits[size_lo:], 1)
+    lo = rbigint(n._digits[:size_lo], 1)
+    hi = rbigint(n._digits[size_lo:], 1)
lo._normalize()
hi._normalize()
return hi, lo
@@ -822,8 +854,8 @@
absolute value of the product (or raises if error).
See Knuth Vol. 2 Chapter 4.3.3 (Pp. 294-295).
"""
-    asize = a._numdigits()
-    bsize = b._numdigits()
+    asize = a.numdigits()
+    bsize = b.numdigits()
# (ah*X+al)(bh*X+bl) = ah*bh*X*X + (ah*bl + al*bh)*X + al*bl
# Let k = (ah+al)*(bh+bl) = ah*bl + al*bh  + ah*bh + al*bl
# Then the original product is
@@ -843,7 +875,7 @@
i = KARATSUBA_CUTOFF
if asize <= i:
if a.sign == 0:
-            return rbigint([0], 0)
+            return rbigint()     # zero
else:
return _x_mul(a, b)

@@ -882,13 +914,13 @@
#    at shift.

# 1. Allocate result space.
-    ret = rbigint([0] * (asize + bsize), 1)
+    ret = rbigint([NULLDIGIT] * (asize + bsize), 1)

# 2. t1 <- ah*bh, and copy into high digits of result.
t1 = _k_mul(ah, bh)
assert t1.sign >= 0
-    assert 2*shift + t1._numdigits() <= ret._numdigits()
-    ret.digits[2*shift : 2*shift + t1._numdigits()] = t1.digits
+    assert 2*shift + t1.numdigits() <= ret.numdigits()
+    ret._digits[2*shift : 2*shift + t1.numdigits()] = t1._digits

# Zero-out the digits higher than the ah*bh copy. */
## ignored, assuming that we initialize to zero
@@ -900,8 +932,8 @@
# 3. t2 <- al*bl, and copy into the low digits.
t2 = _k_mul(al, bl)
assert t2.sign >= 0
-    assert t2._numdigits() <= 2*shift # no overlap with high digits
-    ret.digits[:t2._numdigits()] = t2.digits
+    assert t2.numdigits() <= 2*shift # no overlap with high digits
+    ret._digits[:t2.numdigits()] = t2._digits

# Zero out remaining digits.
## ignored, assuming that we initialize to zero
@@ -911,9 +943,9 @@

# 4 & 5. Subtract ah*bh (t1) and al*bl (t2).  We do al*bl first
# because it's fresher in cache.
-    i = ret._numdigits() - shift  # # digits after shift
-    _v_isub(ret, shift, i, t2, t2._numdigits())
-    _v_isub(ret, shift, i, t1, t1._numdigits())
+    i = ret.numdigits() - shift  # # digits after shift
+    _v_isub(ret, shift, i, t2, t2.numdigits())
+    _v_isub(ret, shift, i, t1, t1.numdigits())
del t1, t2

# 6. t3 <- (ah+al)(bh+bl), and add into result.
@@ -932,7 +964,7 @@

# Add t3.  It's not obvious why we can't run out of room here.
# See the (*) comment after this function.
-    _v_iadd(ret, shift, i, t3, t3._numdigits())
+    _v_iadd(ret, shift, i, t3, t3.numdigits())
del t3

ret._normalize()
@@ -993,20 +1025,20 @@
at a time, then move on, never bactracking except for the helpful
single-width slice overlap between successive partial sums).
"""
-    asize = a._numdigits()
-    bsize = b._numdigits()
+    asize = a.numdigits()
+    bsize = b.numdigits()
# nbdone is # of b digits already multiplied

assert asize > KARATSUBA_CUTOFF
assert 2 * asize <= bsize

# Allocate result space, and zero it out.
-    ret = rbigint([0] * (asize + bsize), 1)
+    ret = rbigint([NULLDIGIT] * (asize + bsize), 1)

# Successive slices of b are copied into bslice.
#bslice = rbigint([0] * asize, 1)
# XXX we cannot pre-allocate, see comments below!
-    bslice = rbigint([0], 1)
+    bslice = rbigint([NULLDIGIT], 1)

nbdone = 0;
while bsize > 0:
@@ -1018,12 +1050,12 @@
# XXX: this would be more efficient if we adopted CPython's
# way to store the size, instead of resizing the list!
# XXX change the implementation, encoding length via the sign.
-        bslice.digits = b.digits[nbdone : nbdone + nbtouse]
+        bslice._digits = b._digits[nbdone : nbdone + nbtouse]
product = _k_mul(a, bslice)

-        _v_iadd(ret, nbdone, ret._numdigits() - nbdone,
-                 product, product._numdigits())
+        _v_iadd(ret, nbdone, ret.numdigits() - nbdone,
+                 product, product.numdigits())
del product

bsize -= nbtouse
@@ -1038,18 +1070,18 @@
Divide bigint pin by non-zero digit n, storing quotient
in pout, and returning the remainder. It's OK for pin == pout on entry.
"""
-    rem = widen_digit(0)
+    rem = _widen_digit(0)
assert n > 0 and n <= MASK
if not size:
-        size = pin._numdigits()
+        size = pin.numdigits()
size -= 1
while size >= 0:
-        rem = (rem << SHIFT) + pin._digit(size)
+        rem = (rem << SHIFT) + pin.widedigit(size)
hi = rem // n
-        pout._setdigit(size, hi)
+        pout.setdigit(size, hi)
rem -= hi * n
size -= 1

def _divrem1(a, n):
"""
@@ -1058,8 +1090,8 @@
The sign of a is ignored; n should not be zero.
"""
assert n > 0 and n <= MASK
-    size = a._numdigits()
-    z = rbigint([0] * size, 1)
+    size = a.numdigits()
+    z = rbigint([NULLDIGIT] * size, 1)
rem = _inplace_divrem1(z, a, n)
z._normalize()
return z, rem
@@ -1071,21 +1103,21 @@
x[m-1], and the remaining carry (0 or 1) is returned.
"""
-    carry = 0;
+    carry = r_uint(0)

assert m >= n
i = xofs
iend = xofs + n
while i < iend:
-        carry += x._digit(i) + y._digit(i-xofs)
-        x._setdigit(i, carry)
+        carry += x.udigit(i) + y.udigit(i-xofs)
+        x.setdigit(i, carry)
carry >>= SHIFT
assert (carry & 1) == carry
i += 1
iend = xofs + m
while carry and i < iend:
-        carry += x._digit(i)
-        x._setdigit(i, carry)
+        carry += x.udigit(i)
+        x.setdigit(i, carry)
carry >>= SHIFT
assert (carry & 1) == carry
i += 1
@@ -1098,57 +1130,59 @@
far as x[m-1], and the remaining borrow (0 or 1) is returned.
"""
-    borrow = 0
+    borrow = r_uint(0)

assert m >= n
i = xofs
iend = xofs + n
while i < iend:
-        borrow = x._digit(i) - y._digit(i-xofs) - borrow
-        x._setdigit(i, borrow)
+        borrow = x.udigit(i) - y.udigit(i-xofs) - borrow
+        x.setdigit(i, borrow)
borrow >>= SHIFT
borrow &= 1    # keep only 1 sign bit
i += 1
iend = xofs + m
while borrow and i < iend:
-        borrow = x._digit(i) - borrow
-        x._setdigit(i, borrow)
+        borrow = x.udigit(i) - borrow
+        x.setdigit(i, borrow)
borrow >>= SHIFT
borrow &= 1
i += 1
return borrow

"""Multiply by a single digit and add a single digit, ignoring the sign.
"""
-    size_a = a._numdigits()
-    z = rbigint([0] * (size_a+1), 1)
-    carry = extra
-    assert carry & MASK == carry
+    size_a = a.numdigits()
+    z = rbigint([NULLDIGIT] * (size_a+1), 1)
+    assert extra & MASK == extra
+    carry = _widen_digit(extra)
i = 0
while i < size_a:
-        carry += a._digit(i) * n
-        z._setdigit(i, carry)
+        carry += a.widedigit(i) * n
+        z.setdigit(i, carry)
carry >>= SHIFT
i += 1
-    z._setdigit(i, carry)
+    z.setdigit(i, carry)
z._normalize()
return z

def _x_divrem(v1, w1):
""" Unsigned bigint division with remainder -- the algorithm """
-    size_w = w1._numdigits()
-    d = (widen_digit(MASK)+1) // (w1._digit(size_w-1) + 1)
+    size_w = w1.numdigits()
+    d = (r_uint(MASK)+1) // (w1.udigit(size_w-1) + 1)
+    assert d <= MASK    # because the first digit of w1 is not zero
-    size_v = v._numdigits()
-    size_w = w._numdigits()
+    size_v = v.numdigits()
+    size_w = w.numdigits()
assert size_v >= size_w and size_w > 1 # Assert checks by div()

size_a = size_v - size_w + 1
-    a = rbigint([0] * size_a, 1)
+    a = rbigint([NULLDIGIT] * size_a, 1)

j = size_v
k = size_a - 1
@@ -1156,50 +1190,50 @@
if j >= size_v:
vj = 0
else:
-            vj = v._digit(j)
+            vj = v.widedigit(j)
carry = 0

-        if vj == w._digit(size_w-1):
+        if vj == w.widedigit(size_w-1):
else:
-            q = ((vj << SHIFT) + v._digit(j-1)) // w._digit(size_w-1)
+            q = ((vj << SHIFT) + v.widedigit(j-1)) // w.widedigit(size_w-1)

-        while (w._digit(size_w-2) * q >
+        while (w.widedigit(size_w-2) * q >
((
(vj << SHIFT)
-                    + v._digit(j-1)
-                    - q * w._digit(size_w-1)
+                    + v.widedigit(j-1)
+                    - q * w.widedigit(size_w-1)
) << SHIFT)
-                + v._digit(j-2)):
+                + v.widedigit(j-2)):
q -= 1
i = 0
while i < size_w and i+k < size_v:
-            z = w._digit(i) * q
+            z = w.widedigit(i) * q
zz = z >> SHIFT
-            carry += v._digit(i+k) - z + (zz << SHIFT)
-            v._setdigit(i+k, carry)
+            carry += v.widedigit(i+k) - z + (zz << SHIFT)
+            v.setdigit(i+k, carry)
carry >>= SHIFT
carry -= zz
i += 1

if i+k < size_v:
-            carry += v._digit(i+k)
-            v._setdigit(i+k, 0)
+            carry += v.widedigit(i+k)
+            v.setdigit(i+k, 0)

if carry == 0:
-            a._setdigit(k, q)
+            a.setdigit(k, q)
assert not q >> SHIFT
else:
assert carry == -1
q -= 1
-            a._setdigit(k, q)
+            a.setdigit(k, q)
assert not q >> SHIFT

carry = 0
i = 0
while i < size_w and i+k < size_v:
-                carry += v._digit(i+k) + w._digit(i)
-                v._setdigit(i+k, carry)
+                carry += v.udigit(i+k) + w.udigit(i)
+                v.setdigit(i+k, carry)
carry >>= SHIFT
i += 1
j -= 1
@@ -1212,22 +1246,22 @@

def _divrem(a, b):
""" Long division with remainder, top-level routine """
-    size_a = a._numdigits()
-    size_b = b._numdigits()
+    size_a = a.numdigits()
+    size_b = b.numdigits()

if b.sign == 0:
raise ZeroDivisionError("long division or modulo by zero")

if (size_a < size_b or
(size_a == size_b and
-         a.digits[size_a-1] < b.digits[size_b-1])):
+         a.digit(size_a-1) < b.digit(size_b-1))):
# |a| < |b|
-        z = rbigint([0], 0)
+        z = rbigint()   # result is 0
rem = a
return z, rem
if size_b == 1:
-        z, urem = _divrem1(a, b._digit(0))
-        rem = rbigint([urem], int(urem != 0))
+        z, urem = _divrem1(a, b.digit(0))
+        rem = rbigint([_store_digit(urem)], int(urem != 0))
else:
z, rem = _x_divrem(a, b)
# Set the signs.
@@ -1257,14 +1291,14 @@
NBITS_WANTED = 57
if v.sign == 0:
return 0.0, 0
-    i = v._numdigits() - 1
+    i = v.numdigits() - 1
sign = v.sign
-    x = float(v.digits[i])
+    x = float(v.digit(i))
nbitsneeded = NBITS_WANTED - 1
# Invariant:  i Python digits remain unaccounted for.
while i > 0 and nbitsneeded > 0:
i -= 1
-        x = x * FLOAT_MULTIPLIER + float(v.digits[i])
+        x = x * FLOAT_MULTIPLIER + float(v.digit(i))
nbitsneeded -= SHIFT
# There are i digits we didn't shift in.  Pretending they're all
# zeroes, the true value is x * 2**(i*SHIFT).
@@ -1385,7 +1419,7 @@
Convert a bigint object to a string, using a given conversion base.
Return a string object.
"""
-    size_a = a._numdigits()
+    size_a = a.numdigits()

base = len(digits)
assert base >= 2 and base <= 36
@@ -1421,11 +1455,11 @@
basebits += 1

for i in range(size_a):
-            accum |= a._digit(i) << accumbits
+            accum |= a.widedigit(i) << accumbits
accumbits += SHIFT
assert accumbits >= basebits
while 1:
-                cdigit = accum & (base - 1)
+                cdigit = intmask(accum & (base - 1))
p -= 1
assert p >= 0
s[p] = digits[cdigit]
@@ -1444,7 +1478,7 @@
size = size_a
pin = a # just for similarity to C source which uses the array
# powbase <- largest power of base that fits in a digit.
-        powbase = widen_digit(base)  # powbase == base ** power
+        powbase = _widen_digit(base)  # powbase == base ** power
power = 1
while 1:
newpow = powbase * base
@@ -1454,14 +1488,14 @@
power += 1

# Get a scratch area for repeated division.
-        scratch = rbigint([0] * size, 1)
+        scratch = rbigint([NULLDIGIT] * size, 1)

# Repeatedly divide by powbase.
while 1:
ntostore = power
rem = _inplace_divrem1(scratch, pin, powbase, size)
pin = scratch  # no need to use a again
-            if pin.digits[size - 1] == 0:
+            if pin.digit(size - 1) == 0:
size -= 1

# Break rem into digits.
@@ -1538,8 +1572,8 @@
# iff one of these cases applies, and mask will be non-0 for operands
# whose length should be ignored.

-    size_a = a._numdigits()
-    size_b = b._numdigits()
+    size_a = a.numdigits()
+    size_b = b.numdigits()
if op == '&':
size_z = size_b
@@ -1551,23 +1585,23 @@
else:
size_z = max(size_a, size_b)

-    z = rbigint([0] * size_z, 1)
+    z = rbigint([NULLDIGIT] * size_z, 1)

for i in range(size_z):
if i < size_a:
-            diga = a.digits[i] ^ maska
+            diga = a.digit(i) ^ maska
else:
if i < size_b:
-            digb = b.digits[i] ^ maskb
+            digb = b.digit(i) ^ maskb
else:
if op == '&':
-            z.digits[i] = diga & digb
+            z.setdigit(i, diga & digb)
elif op == '|':
-            z.digits[i] = diga | digb
+            z.setdigit(i, diga | digb)
elif op == '^':
-            z.digits[i] = diga ^ digb
+            z.setdigit(i, diga ^ digb)

z._normalize()
if negz == 0:
@@ -1599,10 +1633,10 @@

def _AsULonglong_ignore_sign(v):
x = r_ulonglong(0)
-    i = v._numdigits() - 1
+    i = v.numdigits() - 1
while i >= 0:
prev = x
-        x = (x << SHIFT) + v._digit(i)
+        x = (x << SHIFT) + v.widedigit(i)
if (x >> SHIFT) != prev:
raise OverflowError(
"long int too large to convert to unsigned long long int")
@@ -1612,10 +1646,9 @@
x = T(0)
-        i = v._numdigits() - 1
+        i = v.numdigits() - 1
while i >= 0:
-            prev = x
-            x = (x << SHIFT) + T(v._digit(i))
+            x = (x << SHIFT) + T(v.digit(i))
i -= 1
if v.sign < 0:
x = -x
@@ -1632,18 +1665,18 @@
# to please decimal.py, we return a hash that satisfies
# hash(x) == hash(x % ULONG_MAX).  In particular, this
# implies that hash(x) == hash(x % (2**64-1)).
-    i = v._numdigits() - 1
+    i = v.numdigits() - 1
sign = v.sign
x = r_uint(0)
LONG_BIT_SHIFT = LONG_BIT - SHIFT
while i >= 0:
# Force a native long #-bits (32 or 64) circular shift
x = (x << SHIFT) | (x >> LONG_BIT_SHIFT)
-        x += r_uint(v.digits[i])
+        x += v.udigit(i)
# If the addition above overflowed we compensate by
# incrementing.  This preserves the value modulo
# ULONG_MAX.
-        if x < r_uint(v.digits[i]):
+        if x < v.udigit(i):
x += 1
i -= 1