Author: stian
Branch: math-improvements
Changeset: r92797:6eedf6a4b154
Date: 2017-10-19 12:04 +0200
http://bitbucket.org/pypy/pypy/changeset/6eedf6a4b154/

Log:    Add one more case to divmod, add tests to int_divmod, add int_pow
        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
@@ -309,27 +309,45 @@
     @unwrap_spec(w_modulus=WrappedDefault(None))
     def descr_pow(self, space, w_exponent, w_modulus=None):
         if isinstance(w_exponent, W_AbstractIntObject):
-            w_exponent = w_exponent.descr_long(space)
+            exp_int = w_exponent.int_w(space)
+            sign = 0
+            use_int = True
+            if exp_int > 0:
+                sign = 1
+            elif exp_int < 0:
+                sign = -1
         elif not isinstance(w_exponent, W_AbstractLongObject):
             return space.w_NotImplemented
-
+        else:
+            exp_bigint = w_exponent.asbigint()
+            sign = exp_bigint.sign
+            use_int = False
+            
         if space.is_none(w_modulus):
-            if w_exponent.asbigint().sign < 0:
+            if sign < 0:
                 self = self.descr_float(space)
                 w_exponent = w_exponent.descr_float(space)
                 return space.pow(self, w_exponent, space.w_None)
-            return W_LongObject(self.num.pow(w_exponent.asbigint()))
+            if use_int:
+                return W_LongObject(self.num.int_pow(exp_int))
+            else:
+                return W_LongObject(self.num.pow(exp_bigint))
+                
         elif isinstance(w_modulus, W_AbstractIntObject):
             w_modulus = w_modulus.descr_long(space)
+            
         elif not isinstance(w_modulus, W_AbstractLongObject):
             return space.w_NotImplemented
 
-        if w_exponent.asbigint().sign < 0:
+        if sign < 0:
             raise oefmt(space.w_TypeError,
                         "pow() 2nd argument cannot be negative when 3rd "
                         "argument specified")
         try:
-            result = self.num.pow(w_exponent.asbigint(), w_modulus.asbigint())
+            if use_int:
+                result = self.num.int_pow(exp_int, w_modulus.asbigint())
+            else:
+                result = self.num.pow(exp_bigint, w_modulus.asbigint())
         except ValueError:
             raise oefmt(space.w_ValueError, "pow 3rd argument cannot be 0")
         return W_LongObject(result)
diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -927,20 +927,29 @@
             raise ZeroDivisionError("long division or modulo by zero")
 
         wsign = (-1 if w < 0 else 1)
-        if not int_in_valid_range(w) or v.sign != wsign:
-            # Divrem1 doesn't deal with the sign difference. Instead of having 
yet another copy,
+        if not int_in_valid_range(w) or (wsign == -1 and v.sign != wsign):
             # Just fallback.
             return v.divmod(rbigint.fromint(w))
-
+            
+        
         digit = abs(w)
         assert digit > 0
 
         div, mod = _divrem1(v, digit)
-        mod = rbigint.fromint(mod * wsign)
+
+        div.sign = v.sign * wsign
+            
         
-        #mod.sign = wsign
-        div.sign = v.sign * wsign
-
+        
+        if v.sign != wsign:
+            if div.sign == 0:
+                div = NEGATIVERBIGINT
+            else:
+                div = div.int_sub(1)
+            mod = w - mod
+            
+        mod = rbigint.fromint(wsign * mod)
+        
         return div, mod
         
     @jit.elidable
@@ -994,9 +1003,7 @@
         elif a.sign == 0:
             return NULLRBIGINT
         elif size_b == 1:
-            if b._digits[0] == NULLDIGIT:
-                return ONERBIGINT if a.sign == 1 else ONENEGATIVERBIGINT
-            elif b._digits[0] == ONEDIGIT:
+            if b._digits[0] == ONEDIGIT:
                 return a
             elif a.numdigits() == 1:
                 adigit = a.digit(0)
@@ -1084,6 +1091,89 @@
         return z
 
     @jit.elidable
+    def int_pow(a, b, c=None):
+        negativeOutput = False  # if x<0 return negative output
+
+        # 5-ary values.  If the exponent is large enough, table is
+        # precomputed so that table[i] == a**i % c for i in range(32).
+        # python translation: the table is computed when needed.
+
+        if b < 0:  # if exponent is negative
+            if c is not None:
+                raise TypeError(
+                    "pow() 2nd argument "
+                    "cannot be negative when 3rd argument specified")
+            # XXX failed to implement
+            raise ValueError("bigint pow() too negative")
+
+        if c is not None:
+            if c.sign == 0:
+                raise ValueError("pow() 3rd argument cannot be 0")
+
+            # if modulus < 0:
+            #     negativeOutput = True
+            #     modulus = -modulus
+            if c.sign < 0:
+                negativeOutput = True
+                c = c.neg()
+
+            # if modulus == 1:
+            #     return 0
+            if c.numdigits() == 1 and c._digits[0] == ONEDIGIT:
+                return NULLRBIGINT
+
+            # Reduce base by modulus in some cases:
+            # 1. If base < 0.  Forcing the base non-neg makes things easier.
+            # 2. If base is obviously larger than the modulus.  The "small
+            #    exponent" case later can multiply directly by base repeatedly,
+            #    while the "large exponent" case multiplies directly by base 31
+            #    times.  It can be unboundedly faster to multiply by
+            #    base % modulus instead.
+            # We could _always_ do this reduction, but mod() isn't cheap,
+            # so we only do it when it buys something.
+            if a.sign < 0 or a.numdigits() > c.numdigits():
+                a = a.mod(c)
+
+        elif b == 0:
+            return ONERBIGINT
+        elif a.sign == 0:
+            return NULLRBIGINT
+
+        if b == 1:
+            return a
+        elif a.numdigits() == 1:
+            adigit = a.digit(0)
+            if adigit == 1:
+                if a.sign == -1 and b % 2:
+                    return ONENEGATIVERBIGINT
+                return ONERBIGINT
+            elif adigit & (adigit - 1) == 0:
+                ret = a.lshift(((b-1)*(ptwotable[adigit]-1)) + b-1)
+                if a.sign == -1 and not b % 2:
+                    ret.sign = 1
+                return ret
+
+        # At this point a, b, and c are guaranteed non-negative UNLESS
+        # c is NULL, in which case a may be negative. */
+
+        z = rbigint([ONEDIGIT], 1, 1)
+
+        # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result)
+        # into helper function result = _help_mult(x, y, c)
+        # Left-to-right binary exponentiation (HAC Algorithm 14.79)
+        # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
+        j = 1 << (SHIFT-1)
+        while j != 0:
+            z = _help_mult(z, z, c)
+            if b & j:
+                z = _help_mult(z, a, c)
+            j >>= 1
+
+        if negativeOutput and z.sign != 0:
+            z = z.sub(c)
+        return z
+        
+    @jit.elidable
     def neg(self):
         return rbigint(self._digits, -self.sign, self.numdigits())
 
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
@@ -164,6 +164,13 @@
                 r2 = op1 ** op2
                 assert r1.tolong() == r2
 
+    def test_int_pow(self):
+        for op1 in gen_signs(long_vals_not_too_big[:-2]):
+            for op2 in [0, 1, 2, 8, 9, 10, 11]:
+                rl_op1 = rbigint.fromlong(op1)
+                r1 = rl_op1.int_pow(op2)
+                r2 = op1 ** op2
+                assert r1.tolong() == r2
     def test_touint(self):
         result = r_uint(sys.maxint + 42)
         rl = rbigint.fromint(sys.maxint).add(rbigint.fromint(42))
@@ -781,6 +788,20 @@
                 assert div.tolong() == _div
                 assert rem.tolong() == _rem
 
+    def test_int_divmod(self):
+         x = 12345678901234567890L
+         for i in range(10):
+             y = randint(0, 1 << 12)
+             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, " | ", div.tolong(), rem.tolong()
+                 assert div.tolong() == _div
+                 assert rem.tolong() == _rem
+         
     # testing Karatsuba stuff
     def test__v_iadd(self):
         f1 = bigint([lobj.MASK] * 10, 1)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to