[pypy-commit] pypy default: Re-enable this part of the code and fix it.

arigo noreply at buildbot.pypy.org
Sat Apr 21 08:05:21 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r54602:ebca5c7c17dd
Date: 2012-04-21 08:04 +0200
http://bitbucket.org/pypy/pypy/changeset/ebca5c7c17dd/

Log:	Re-enable this part of the code and fix it.

diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -40,7 +40,7 @@
 # In that case, do 5 bits at a time.  The potential drawback is that
 # a table of 2**5 intermediate results is computed.
 
-## FIVEARY_CUTOFF = 8   disabled for now
+FIVEARY_CUTOFF = 8
 
 
 def _mask_digit(x):
@@ -456,7 +456,7 @@
 
         # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result)
         # into helper function result = _help_mult(x, y, c)
-        if 1:   ## b.numdigits() <= FIVEARY_CUTOFF:
+        if b.numdigits() <= FIVEARY_CUTOFF:
             # Left-to-right binary exponentiation (HAC Algorithm 14.79)
             # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
             i = b.numdigits() - 1
@@ -469,30 +469,51 @@
                         z = _help_mult(z, a, c)
                     j >>= 1
                 i -= 1
-##        else:
-##            This code is disabled for now, because it assumes that
-##            SHIFT is a multiple of 5.  It could be fixed but it looks
-##            like it's more troubles than benefits...
-##
-##            # 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
-##            for i in range(1, 32):
-##                table[i] = _help_mult(table[i-1], a, c)
-##            i = b.numdigits() - 1
-##            while i >= 0:
-##                bi = b.digit(i)
-##                j = SHIFT - 5
-##                while j >= 0:
-##                    index = (bi >> j) & 0x1f
-##                    for k in range(5):
-##                        z = _help_mult(z, z, c)
-##                    if index:
-##                        z = _help_mult(z, table[index], c)
-##                    j -= 5
-##                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
+            for i in range(1, 32):
+                table[i] = _help_mult(table[i-1], a, c)
+            i = b.numdigits()
+            # Note that here SHIFT is not a multiple of 5.  The difficulty
+            # is to extract 5 bits at a time from 'b', starting from the
+            # most significant digits, so that at the end of the algorithm
+            # it falls exactly to zero.
+            # m  = max number of bits = i * SHIFT
+            # m+ = m rounded up to the next multiple of 5
+            # j  = (m+) % SHIFT = (m+) - (i * SHIFT)
+            # (computed without doing "i * SHIFT", which might overflow)
+            j = i % 5
+            if j != 0:
+                j = 5 - j
+            if not we_are_translated():
+                assert j == (i*SHIFT+4)//5*5 - i*SHIFT
+            #
+            accum = r_uint(0)
+            while True:
+                j -= 5
+                if j >= 0:
+                    index = (accum >> j) & 0x1f
+                else:
+                    # 'accum' does not have enough digit.
+                    # must get the next digit from 'b' in order to complete
+                    i -= 1
+                    if i < 0:
+                        break    # done
+                    bi = b.udigit(i)
+                    index = ((accum << (-j)) | (bi >> (j+SHIFT))) & 0x1f
+                    accum = bi
+                    j += SHIFT
+                #
+                for k in range(5):
+                    z = _help_mult(z, z, c)
+                if index:
+                    z = _help_mult(z, table[index], c)
+            #
+            assert j == -5
 
         if negativeOutput and z.sign != 0:
             z = z.sub(c)
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
@@ -379,6 +379,18 @@
         for n, expected in [(37, 9), (1291, 931), (67889, 39464)]:
             v = two.pow(t, rbigint.fromint(n))
             assert v.toint() == expected
+        #
+        # more tests, comparing against CPython's answer
+        enabled = sample(range(5*32), 10)
+        for i in range(5*32):
+            t = t.mul(two)      # add one random bit
+            if random() >= 0.5:
+                t = t.add(rbigint.fromint(1))
+            if i not in enabled:
+                continue    # don't take forever
+            n = randint(1, sys.maxint)
+            v = two.pow(t, rbigint.fromint(n))
+            assert v.toint() == pow(2, t.tolong(), n)
 
     def test_pow_lln(self):
         x = 10L


More information about the pypy-commit mailing list