[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