[pypy-commit] pypy improve-rbigint: Add some stuff regarding div. Not really working yet
stian
noreply at buildbot.pypy.org
Sat Jul 21 18:41:29 CEST 2012
Author: stian
Branch: improve-rbigint
Changeset: r56341:e401dc346887
Date: 2012-06-27 17:38 +0200
http://bitbucket.org/pypy/pypy/changeset/e401dc346887/
Log: Add some stuff regarding div. Not really working yet
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -29,15 +29,16 @@
#SHIFT = (LONG_BIT // 2) - 1
if SUPPORT_INT128:
SHIFT = 63
- MASK = long((1 << SHIFT) - 1)
+ BASE = long(1 << SHIFT)
UDIGIT_TYPE = r_ulonglong
UDIGIT_MASK = longlongmask
else:
SHIFT = 31
- MASK = int((1 << SHIFT) - 1)
+ BASE = int(1 << SHIFT)
UDIGIT_TYPE = r_uint
UDIGIT_MASK = intmask
-
+
+MASK = BASE - 1
FLOAT_MULTIPLIER = float(1 << LONG_BIT) # Because it works.
CACHE_INTS = 1024 # CPython do 256
@@ -65,6 +66,9 @@
USE_TOOMCOCK = False # WIP
TOOMCOOK_CUTOFF = 3 # Smallest possible cutoff is 3. Ideal is probably around 150+
+# Use N**2 division when number of digits are smaller than this.
+DIV_LIMIT = KARATSUBA_CUTOFF
+
# For exponentiation, use the binary left-to-right algorithm
# unless the exponent contains more than FIVEARY_CUTOFF digits.
# In that case, do 5 bits at a time. The potential drawback is that
@@ -1482,21 +1486,66 @@
z._normalize()
return z
+def _v_lshift(z, a, m, d):
+ """ Shift digit vector a[0:m] d bits left, with 0 <= d < SHIFT. Put
+ * result in z[0:m], and return the d bits shifted out of the top.
+ """
+
+ carry = 0
+ assert 0 <= d and d < SHIFT
+ for i in range(m):
+ acc = a.widedigit(i) << d | carry
+ z.setdigit(i, acc)
+ carry = acc >> SHIFT
+
+ return carry
+
+def _v_rshift(z, a, m, d):
+ """ Shift digit vector a[0:m] d bits right, with 0 <= d < PyLong_SHIFT. Put
+ * result in z[0:m], and return the d bits shifted out of the bottom.
+ """
+
+ carry = 0
+ acc = _widen_digit(0)
+ mask = (1 << d) - 1
+
+ assert 0 <= d and d < SHIFT
+ for i in range(m-1, 0, -1):
+ acc = carry << SHIFT | a.digit(i)
+ carry = acc & mask
+ z.setdigit(i, acc >> d)
+
+ return carry
+
def _x_divrem(v1, w1):
""" Unsigned bigint division with remainder -- the algorithm """
+
size_w = w1.numdigits()
d = (UDIGIT_TYPE(MASK)+1) // (w1.udigit(size_w-1) + 1)
assert d <= MASK # because the first digit of w1 is not zero
d = longlongmask(d)
v = _muladd1(v1, d)
w = _muladd1(w1, d)
- size_v = v.numdigits()
- size_w = w.numdigits()
- assert size_v >= size_w and size_w >= 1 # (stian: Adding d doesn't necessary mean it will increase by 1), Assert checks by div()
+ size_v = v1.numdigits()
+ size_w = w1.numdigits()
+ assert size_v >= size_w and size_w >= 1 # (Assert checks by div()
+ """v = rbigint([NULLDIGIT] * (size_v + 1))
+ w = rbigint([NULLDIGIT] * (size_w))
+
+ d = SHIFT - bits_in_digit(w1.digit(size_w-1))
+ carry = _v_lshift(w, w1, size_w, d)
+ assert carry == 0
+ carrt = _v_lshift(v, v1, size_v, d)
+ if carry != 0 or v.digit(size_v - 1) >= w.digit(size_w-1):
+ v.setdigit(size_v, carry)
+ size_v += 1"""
+
size_a = size_v - size_w + 1
a = rbigint([NULLDIGIT] * size_a, 1)
+ wm1 = w.widedigit(size_w-1)
+ wm2 = w.widedigit(size_w-2)
j = size_v
k = size_a - 1
while k >= 0:
@@ -1504,20 +1553,24 @@
vj = 0
else:
vj = v.widedigit(j)
+
carry = 0
-
- if vj == w.widedigit(size_w-1):
+ vj1 = v.widedigit(j-1)
+
+ if vj == wm1:
q = MASK
else:
- q = ((vj << SHIFT) + v.widedigit(j-1)) // w.widedigit(size_w-1)
+ q = ((vj << SHIFT) + vj1) // wm1
- while (w.widedigit(size_w-2) * q >
+
+ vj2 = v.widedigit(j-2)
+ while (wm2 * q >
((
(vj << SHIFT)
- + v.widedigit(j-1)
- - q * w.widedigit(size_w-1)
+ + vj1
+ - q * wm1
) << SHIFT)
- + v.widedigit(j-2)):
+ + vj2):
q -= 1
i = 0
while i < size_w and i+k < size_v:
@@ -1556,6 +1609,95 @@
rem, _ = _divrem1(v, d)
return a, rem
+ """
+ Didn't work as expected. Someone want to look over this?
+ size_v = v1.numdigits()
+ size_w = w1.numdigits()
+
+ assert size_v >= size_w and size_w >= 2
+
+ v = rbigint([NULLDIGIT] * (size_v + 1))
+ w = rbigint([NULLDIGIT] * size_w)
+
+ # Normalization
+ d = SHIFT - bits_in_digit(w1.digit(size_w-1))
+ carry = _v_lshift(w, w1, size_w, d)
+ assert carry == 0
+ carry = _v_lshift(v, v1, size_v, d)
+ if carry != 0 or v.digit(size_v-1) >= w.digit(size_w-1):
+ v.setdigit(size_v, carry)
+ size_v += 1
+
+ # Now v->ob_digit[size_v-1] < w->ob_digit[size_w-1], so quotient has
+ # at most (and usually exactly) k = size_v - size_w digits.
+
+ k = size_v - size_w
+ assert k >= 0
+
+ a = rbigint([NULLDIGIT] * k)
+
+ k -= 1
+ wm1 = w.digit(size_w-1)
+ wm2 = w.digit(size_w-2)
+
+ j = size_v
+
+ while k >= 0:
+ # inner loop: divide vk[0:size_w+1] by w[0:size_w], giving
+ # single-digit quotient q, remainder in vk[0:size_w].
+
+ vtop = v.widedigit(size_w)
+ assert vtop <= wm1
+
+ vv = vtop << SHIFT | v.digit(size_w-1)
+
+ q = vv / wm1
+ r = vv - _widen_digit(wm1) * q
+
+ # estimate quotient digit q; may overestimate by 1 (rare)
+ while wm2 * q > ((r << SHIFT) | v.digit(size_w-2)):
+ q -= 1
+
+ r+= wm1
+ if r >= SHIFT:
+ break
+
+ assert q <= BASE
+
+ # subtract q*w0[0:size_w] from vk[0:size_w+1]
+ zhi = 0
+ for i in range(size_w):
+ #invariants: -BASE <= -q <= zhi <= 0;
+ # -BASE * q <= z < ASE
+ z = v.widedigit(i+k) + zhi - (q * w.widedigit(i))
+ v.setdigit(i+k, z)
+ zhi = z >> SHIFT
+
+ # add w back if q was too large (this branch taken rarely)
+ assert vtop + zhi == -1 or vtop + zhi == 0
+ if vtop + zhi < 0:
+ carry = 0
+ for i in range(size_w):
+ carry += v.digit(i+k) + w.digit(i)
+ v.setdigit(i+k, carry)
+ carry >>= SHIFT
+
+ q -= 1
+
+ assert q < BASE
+
+ a.setdigit(k, q)
+
+ j -= 1
+ k -= 1
+
+ carry = _v_rshift(w, v, size_w, d)
+ assert carry == 0
+
+ a._normalize()
+ w._normalize()
+ return a, w"""
+
def _divrem(a, b):
""" Long division with remainder, top-level routine """
size_a = a.numdigits()
diff --git a/pypy/translator/goal/targetbigintbenchmark.py b/pypy/translator/goal/targetbigintbenchmark.py
--- a/pypy/translator/goal/targetbigintbenchmark.py
+++ b/pypy/translator/goal/targetbigintbenchmark.py
@@ -45,6 +45,15 @@
"""
t = time()
+ num = rbigint.pow(rbigint.fromint(100000000), rbigint.fromint(1024))
+ by = rbigint.pow(rbigint.fromint(2), rbigint.fromint(128))
+ for n in xrange(80000):
+ rbigint.divmod(num, by)
+
+
+ print time() - t
+
+ t = time()
num = rbigint.fromint(1000000000)
for n in xrange(160000000):
rbigint.rshift(num, 16)
More information about the pypy-commit
mailing list