[pypy-commit] pypy bigint-with-int: More progress, more tests.
stian
noreply at buildbot.pypy.org
Sat Aug 3 00:15:42 CEST 2013
Author: stian
Branch: bigint-with-int
Changeset: r65916:5754c8cfb244
Date: 2013-08-02 22:33 +0200
http://bitbucket.org/pypy/pypy/changeset/5754c8cfb244/
Log: More progress, more tests.
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
@@ -174,7 +174,7 @@
def eq__Int_Long(space, w_int1, w_long2):
return space.newbool(w_long2.num.int_ne(w_int1.intval))
def ne__Int_Long(space, w_int1, w_long2):
- return space.newbool(w_long2.num.int_eg(w_int1.intval))
+ return space.newbool(w_long2.num.int_eq(w_int1.intval))
def gt__Int_Long(space, w_int1, w_long2):
return space.newbool(w_long2.num.int_lt(w_int1.intval))
def ge__Int_Long(space, w_int1, w_long2):
@@ -192,12 +192,21 @@
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))
+def mul__Long_Int(space, w_long1, w_int2):
+ return W_LongObject(w_long1.num.int_mul(w_int2.intval))
+
def truediv__Long_Long(space, w_long1, w_long2):
try:
f = w_long1.num.truediv(w_long2.num)
@@ -228,6 +237,14 @@
space.wrap("long division or modulo by zero"))
return newlong(space, z)
+def mod__Long_Int(space, w_long1, w_int2):
+ try:
+ z = w_long1.num.int_mod(w_int2.intval)
+ except ZeroDivisionError:
+ raise OperationError(space.w_ZeroDivisionError,
+ space.wrap("long division or modulo by zero"))
+ return newlong(space, z)
+
def divmod__Long_Long(space, w_long1, w_long2):
try:
div, mod = w_long1.num.divmod(w_long2.num)
@@ -285,6 +302,14 @@
space.wrap("shift count too large"))
return W_LongObject(w_long1.num.lshift(shift))
+def lshift__Long_Int(space, w_long1, w_int2):
+ # XXX need to replicate some of the logic, to get the errors right
+ if w_int2.intval < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("negative shift counnt"))
+
+ return W_LongObject(w_long1.num.lshift(w_int2.intval))
+
def rshift__Long_Long(space, w_long1, w_long2):
# XXX need to replicate some of the logic, to get the errors right
if w_long2.num.sign < 0:
@@ -297,6 +322,14 @@
space.wrap("shift count too large"))
return newlong(space, w_long1.num.rshift(shift))
+def rshift__Long_Int(space, w_long1, w_int2):
+ # XXX need to replicate some of the logic, to get the errors right
+ if w_int2.intval < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("negative shift count"))
+
+ return newlong(space, w_long1.num.rshift(w_int2.intval))
+
def and__Long_Long(space, w_long1, w_long2):
return newlong(space, w_long1.num.and_(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
@@ -493,6 +493,11 @@
def ne(self, other):
return not self.eq(other)
+ @jit.look_inside
+ def int_ne(self, other):
+ """ ne with int """
+ return not self.int_eq(other)
+
@jit.elidable
def lt(self, other):
if self.sign > other.sign:
@@ -592,6 +597,21 @@
return result
@jit.elidable
+ def int_add(self, other):
+ if other == 0:
+ return self
+ if self.sign == 0:
+ return rbigint.fromint(other)
+ if (self.sign > 0 and other > 0) or (self.sign < 0 and other < 0):
+ result = _x_int_add(self, abs(other))
+ else:
+ # XXX: Improve.
+ result = _x_sub(rbigint.fromint(other), self)
+ if other < 0:
+ result.sign *= -1
+ return result
+
+ @jit.elidable
def sub(self, other):
if other.sign == 0:
return self
@@ -605,6 +625,20 @@
return result
@jit.elidable
+ def int_sub(self, other):
+ if other == 0:
+ return self
+ if self.sign == 0:
+ return rbigint.fromint(-1 * other)
+ if (self.sign > 0 and other > 0) or (self.sign < 0 and other < 0):
+ # Improve
+ result = _x_sub(self, rbigint.fromint(other))
+ else:
+ result = _x_int_add(self, abs(other))
+ result.sign *= self.sign
+ return result
+
+ @jit.elidable
def mul(self, b):
asize = self.numdigits()
bsize = b.numdigits()
@@ -650,6 +684,33 @@
return result
@jit.elidable
+ def int_mul(self, b):
+ """ Mul with int. """
+ asize = self.numdigits()
+
+ if self.sign == 0 or b == 0:
+ return NULLRBIGINT
+
+ if asize == 1:
+ if self._digits[0] == NULLDIGIT:
+ return NULLRBIGINT
+ elif self._digits[0] == ONEDIGIT:
+ return rbigint.fromint(self.sign * b)
+
+ res = self.widedigit(0) * b
+ carry = res >> SHIFT
+ if carry:
+ return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * (-1 if b < 0 else 1), 2)
+ else:
+ return rbigint([_store_digit(res & MASK)], self.sign * (-1 if b < 0 else 1), 1)
+
+ else:
+ result = _x_int_mul(self, abs(b))
+
+ result.sign = self.sign * (-1 if b < 0 else 1)
+ return result
+
+ @jit.elidable
def truediv(self, other):
div = _bigint_true_divide(self, other)
return div
@@ -659,7 +720,7 @@
if self.sign == 1 and other.numdigits() == 1 and other.sign == 1:
digit = other.digit(0)
if digit == 1:
- return rbigint(self._digits[:self.size], 1, self.size)
+ return self
elif digit and digit & (digit - 1) == 0:
return self.rshift(ptwotable[digit])
@@ -667,14 +728,36 @@
if mod.sign * other.sign == -1:
if div.sign == 0:
return ONENEGATIVERBIGINT
- div = div.sub(ONERBIGINT)
+ div = div.int_sub(1)
return div
+ @jit.elidable
+ def int_floordiv(self, other):
+ digit = abs(other)
+ if self.sign == 1 and other > 0:
+ if digit == 1:
+ return self
+ elif digit and digit & (digit - 1) == 0:
+ return self.rshift(ptwotable[digit])
+
+ div, mod = _divrem1(self, digit)
+
+ if mod != 0 and self.sign * (-1 if other < 0 else 1) == -1:
+ if div.sign == 0:
+ return ONENEGATIVERBIGINT
+ div = div.int_add(1)
+ div.sign = self.sign * (-1 if other < 0 else 1)
+ return div
+
@jit.look_inside
def div(self, other):
return self.floordiv(other)
+ @jit.look_inside
+ def int_div(self, other):
+ return self.int_floordiv(other)
+
@jit.elidable
def mod(self, other):
if self.sign == 0:
@@ -713,6 +796,46 @@
return mod
@jit.elidable
+ def int_mod(self, other):
+ if self.sign == 0:
+ return NULLRBIGINT
+
+ digit = abs(other)
+
+ if digit != 0:
+ if digit == 1:
+ return NULLRBIGINT
+ elif digit == 2:
+ modm = self.digit(0) & 1
+ if modm:
+ return ONENEGATIVERBIGINT if other < 0 else ONERBIGINT
+ return NULLRBIGINT
+ elif digit & (digit - 1) == 0:
+ mod = self.and_(rbigint([_store_digit(digit - 1)], 1, 1))
+ else:
+ # Perform
+ size = self.numdigits() - 1
+ if size > 0:
+ rem = self.widedigit(size)
+ size -= 1
+ while size >= 0:
+ rem = ((rem << SHIFT) + self.widedigit(size)) % digit
+ size -= 1
+ else:
+ rem = self.digit(0) % digit
+
+ if rem == 0:
+ return NULLRBIGINT
+ mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1, 1)
+ else:
+ # Raise
+ raise ZeroDivisionError("long division or modulo by zero")
+
+ if mod.sign * (-1 if other < 0 else 1) == -1:
+ mod = mod.int_add(other)
+ return mod
+
+ @jit.elidable
def divmod(v, w):
"""
The / and % operators are now defined in terms of divmod().
@@ -735,7 +858,28 @@
mod = mod.add(w)
if div.sign == 0:
return ONENEGATIVERBIGINT, mod
- div = div.sub(ONERBIGINT)
+ div = div.int_sub(1)
+ return div, mod
+
+ @jit.elidable
+ def int_divmod(v, w):
+ """ Divmod with int """
+ if v.sign != (-1 if w < 0 else 1):
+ # TODO, fix.
+ return v.divmod(rbigint.fromint(w))
+ div, mod = _divrem1(v, abs(w))
+ if v.sign != (-1 if w < 0 else 1):
+ mod = rbigint.fromint(mod)
+ mod.sign = -1 if w < 0 else 1
+ mod = mod.int_add(w)
+
+ if div.sign == 0:
+ return ONENEGATIVERBIGINT, mod
+ div = div.int_add(1)
+ else:
+ mod = rbigint.fromint(mod)
+ mod.sign = -1 if w < 0 else 1
+ div.sign = v.sign * (-1 if w < 0 else 1)
return div, mod
@jit.elidable
@@ -1170,6 +1314,26 @@
z._normalize()
return z
+def _x_int_add(a, b):
+ """ Add the absolute values of one bigint and one int. """
+ size_a = a.numdigits()
+
+ z = rbigint([NULLDIGIT] * (size_a + 1), 1)
+ i = UDIGIT_TYPE(0)
+
+ carry = a.udigit(0) + b
+ z.setdigit(0, carry)
+ carry >>= SHIFT
+ i += 1
+ 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. """
@@ -1216,6 +1380,40 @@
z._normalize()
return z
+def _x_int_sub(a, b):
+ """ Subtract the absolute values of one rbigint and one integer. """
+
+ size_a = a.numdigits()
+ sign = 1
+
+ if size_a == 1:
+ # Find highest digit where a and b differ:
+ if a.digit(0) == b:
+ return NULLRBIGINT
+ elif a.digit(0) < b:
+ sign = -1
+ b *= -1
+ size_a = size_b = 1
+
+ z = rbigint([NULLDIGIT] * size_a, sign, size_a)
+ borrow = UDIGIT_TYPE(0)
+ i = _load_unsigned_digit(1)
+ # The following assumes unsigned arithmetic
+ # works modulo 2**N for some N>SHIFT.
+ borrow = a.udigit(0) - b
+ z.setdigit(0, borrow)
+ borrow >>= SHIFT
+ 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):
@@ -1301,6 +1499,18 @@
z._normalize()
return z
+def _x_int_mul(a, digit):
+ """
+ Grade school multiplication, ignoring the signs.
+ Returns the absolute value of the product, or None if error.
+ """
+
+ if digit & (digit - 1) == 0:
+ return a.lqshift(ptwotable[digit])
+
+ return _muladd1(a, digit)
+
+
def _kmul_split(n, size):
"""
A helper for Karatsuba multiplication (k_mul).
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
@@ -50,6 +50,15 @@
r2 = op1 // op2
assert r1.tolong() == r2
+ def test_int_floordiv(self):
+ for op1 in [-12, -2, -1, 1, 2, 50]:
+ for op2 in [-4, -2, -1, 1, 2, 8]:
+ rl_op1 = rbigint.fromint(op1)
+ r1 = rl_op1.int_floordiv(op2)
+ r2 = op1 // op2
+ print op1, op2
+ assert r1.tolong() == r2
+
def test_truediv(self):
for op1 in [-12, -2, -1, 1, 2, 50]:
for op2 in [-4, -2, -1, 1, 2, 8]:
@@ -106,6 +115,15 @@
print op1, op2
assert r1.tolong() == r2
+ def test_int_mod(self):
+ for op1 in [-50, -12, -2, -1, 1, 2, 50, 52]:
+ for op2 in [-4, -2, -1, 1, 2, 8]:
+ rl_op1 = rbigint.fromint(op1)
+ r1 = rl_op1.int_mod(op2)
+ r2 = op1 % op2
+ print op1, op2
+ assert r1.tolong() == r2
+
def test_pow(self):
for op1 in [-50, -12, -2, -1, 1, 2, 50, 52]:
for op2 in [0, 1, 2, 8, 9, 10, 11]:
@@ -237,6 +255,17 @@
result = f1.add(f2)
assert result.tolong() == x * i + y * j
+ def test_int_add(self):
+ x = 123456789123456789000000L
+ y = 1238
+ 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 +276,16 @@
result = f1.sub(f2)
assert result.tolong() == x * i - y * j
+ def test_int_sub(self):
+ x = 12378959520302182384345L
+ y = 8896
+ 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
@@ -262,6 +301,13 @@
result = f1.mul(f1)
assert result.tolong() == x * x
+ def test_int_mul(self):
+ x = -1238585838347L
+ y = 585839
+ f1 = rbigint.fromlong(x)
+ result = f1.int_mul(y)
+ assert result.tolong() == x * y
+
def test_tofloat(self):
x = 12345678901234567890L ** 10
f1 = rbigint.fromlong(x)
@@ -667,6 +713,20 @@
assert div.tolong() == _div
assert rem.tolong() == _rem
+ def test_int_divmod(self):
+ x = 12345678901234567890L
+ for i in range(100):
+ y = randint(0, 1 << 60)
+ for sx, sy in (1, 1), (1, -1), (-1, -1), (-1, 1):
+ sx *= x
+ sy *= y
+ f1 = rbigint.fromlong(sx)
+ div, rem = f1.int_divmod(sy)
+ _div, _rem = divmod(sx, sy)
+ print sx, sy
+ assert div.tolong() == _div
+ assert rem.tolong() == _rem
+
# testing Karatsuba stuff
def test__v_iadd(self):
f1 = bigint([lobj.MASK] * 10, 1)
More information about the pypy-commit
mailing list