[pypy-commit] pypy bigint-with-int-ops: Support add and sub ops
stian
noreply at buildbot.pypy.org
Fri Aug 9 00:11:42 CEST 2013
Author: stian
Branch: bigint-with-int-ops
Changeset: r66022:ec59bbb3636b
Date: 2013-08-08 20:35 +0200
http://bitbucket.org/pypy/pypy/changeset/ec59bbb3636b/
Log: Support add and sub ops
diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py
--- a/pypy/objspace/std/longobject.py
+++ b/pypy/objspace/std/longobject.py
@@ -192,9 +192,15 @@
def add__Long_Long(space, w_long1, w_long2):
return W_LongObject(w_long1.num.add(w_long2.num))
+def add__Long_Int(space, w_long1, w_int2):
+ return W_LongObject(w_long1.num.int_add(w_int2.intval))
+
def sub__Long_Long(space, w_long1, w_long2):
return W_LongObject(w_long1.num.sub(w_long2.num))
+def sub__Long_Int(space, w_long1, w_int2):
+ return W_LongObject(w_long1.num.int_sub(w_int2.intval))
+
def mul__Long_Long(space, w_long1, w_long2):
return W_LongObject(w_long1.num.mul(w_long2.num))
diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -44,6 +44,23 @@
MASK = int((1 << SHIFT) - 1)
FLOAT_MULTIPLIER = float(1 << SHIFT)
+# For BIGINT and INT mix.
+#
+# The VALID range of an int is different than a valid range of a bigint of length one.
+# -1 << LONG_BIT is actually TWO digits, because they are stored without the sign.
+if SHIFT == LONG_BIT - 1:
+ MIN_INT_VALUE = -1 << SHIFT
+ def int_in_valid_range(x):
+ if x == MIN_INT_VALUE:
+ return False
+ return True
+else:
+ # Means we don't have INT128 on 64bit.
+ def int_in_valid_range(x):
+ if x > MASK or x < -MASK:
+ return False
+ return True
+
# Debugging digit array access.
#
# False == no checking at all
@@ -551,6 +568,25 @@
return result
@jit.elidable
+ def int_add(self, other):
+ if not int_in_valid_range(other):
+ # Fallback to long.
+ return self.add(rbigint.fromint(other))
+ elif self.sign == 0:
+ return rbigint.fromint(other)
+ elif other == 0:
+ return self
+
+ sign = -1 if other < 0 else 1
+ if self.sign == sign:
+ result = _x_int_add(self, other)
+ else:
+ result = _x_int_sub(self, other)
+ result.sign *= -1
+ result.sign *= sign
+ return result
+
+ @jit.elidable
def sub(self, other):
if other.sign == 0:
return self
@@ -564,6 +600,22 @@
return result
@jit.elidable
+ def int_sub(self, other):
+ if not int_in_valid_range(other):
+ # Fallback to long.
+ return self.sub(rbigint.fromint(other))
+ elif other == 0:
+ return self
+ elif self.sign == 0:
+ return rbigint.fromint(-other)
+ elif self.sign == (-1 if other < 0 else 1):
+ result = _x_int_sub(self, other)
+ else:
+ result = _x_int_add(self, other)
+ result.sign *= self.sign
+ return result
+
+ @jit.elidable
def mul(self, b):
asize = self.numdigits()
bsize = b.numdigits()
@@ -1129,6 +1181,25 @@
z._normalize()
return z
+def _x_int_add(a, b):
+ """ Add the absolute values of one bigint and one integer. """
+ size_a = a.numdigits()
+
+ z = rbigint([NULLDIGIT] * (size_a + 1), 1)
+ i = UDIGIT_TYPE(1)
+ carry = a.udigit(0) + abs(b)
+ z.setdigit(0, carry)
+ carry >>= SHIFT
+
+ while i < size_a:
+ carry += a.udigit(i)
+ z.setdigit(i, carry)
+ carry >>= SHIFT
+ i += 1
+ z.setdigit(i, carry)
+ z._normalize()
+ return z
+
def _x_sub(a, b):
""" Subtract the absolute values of two integers. """
@@ -1175,6 +1246,42 @@
z._normalize()
return z
+def _x_int_sub(a, b):
+ """ Subtract the absolute values of two integers. """
+
+ size_a = a.numdigits()
+
+ bdigit = abs(b)
+
+ if size_a == 1:
+ # Find highest digit where a and b differ:
+ adigit = a.digit(0)
+
+ if adigit == bdigit:
+ return NULLRBIGINT
+
+ return rbigint.fromint(adigit - bdigit)
+
+ z = rbigint([NULLDIGIT] * size_a, 1, size_a)
+ i = _load_unsigned_digit(1)
+ # The following assumes unsigned arithmetic
+ # works modulo 2**N for some N>SHIFT.
+ borrow = a.udigit(0) - bdigit
+ z.setdigit(0, borrow)
+ borrow >>= SHIFT
+ #borrow &= 1 # Keep only one sign bit
+
+ while i < size_a:
+ borrow = a.udigit(i) - borrow
+ z.setdigit(i, borrow)
+ borrow >>= SHIFT
+ #borrow &= 1
+ i += 1
+
+ assert borrow == 0
+ z._normalize()
+ return z
+
# A neat little table of power of twos.
ptwotable = {}
for x in range(SHIFT-1):
diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py
--- a/rpython/rlib/test/test_rbigint.py
+++ b/rpython/rlib/test/test_rbigint.py
@@ -237,6 +237,16 @@
result = f1.add(f2)
assert result.tolong() == x * i + y * j
+ def test_int_add(self):
+ x = 123456789123456789000000L
+ y = 9999
+ for i in [-1, 1]:
+ for j in [-1, 1]:
+ f1 = rbigint.fromlong(x * i)
+ f2 = y * j
+ result = f1.int_add(f2)
+ assert result.tolong() == x * i + y * j
+
def test_sub(self):
x = 12378959520302182384345L
y = 88961284756491823819191823L
@@ -247,6 +257,16 @@
result = f1.sub(f2)
assert result.tolong() == x * i - y * j
+ def test_int_sub(self):
+ x = 12378959520302182384345L
+ y = 8888
+ for i in [-1, 1]:
+ for j in [-1, 1]:
+ f1 = rbigint.fromlong(x * i)
+ f2 = y * j
+ result = f1.int_sub(f2)
+ assert result.tolong() == x * i - y * j
+
def test_subzz(self):
w_l0 = rbigint.fromint(0)
assert w_l0.sub(w_l0).tolong() == 0
More information about the pypy-commit
mailing list