[pypy-commit] pypy improve-rbigint: Remove toom-cook (since it didn't pass own-linux-x86-32), fix divmod test.
stian
noreply at buildbot.pypy.org
Thu Jul 26 18:01:29 CEST 2012
Author: stian
Branch: improve-rbigint
Changeset: r56474:e0eaeb5fd308
Date: 2012-07-26 17:59 +0200
http://bitbucket.org/pypy/pypy/changeset/e0eaeb5fd308/
Log: Remove toom-cook (since it didn't pass own-linux-x86-32), fix divmod
test.
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -66,9 +66,6 @@
KARATSUBA_SQUARE_CUTOFF = 2 * KARATSUBA_CUTOFF
-USE_TOOMCOCK = False
-TOOMCOOK_CUTOFF = 10000 # Smallest possible cutoff is 3. Ideal is probably around 150+
-
# 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
@@ -441,8 +438,6 @@
return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1)
result = _x_mul(a, b, a.digit(0))
- elif USE_TOOMCOCK and asize >= TOOMCOOK_CUTOFF:
- result = _tc_mul(a, b)
elif USE_KARATSUBA:
if a is b:
i = KARATSUBA_SQUARE_CUTOFF
@@ -1109,94 +1104,6 @@
return z
-def _tcmul_split(n):
- """
- A helper for Karatsuba multiplication (k_mul).
- Takes a bigint "n" and an integer "size" representing the place to
- split, and sets low and high such that abs(n) == (high << (size * 2) + (mid << size) + low,
- viewing the shift as being by digits. The sign bit is ignored, and
- the return values are >= 0.
- """
- size_n = n.numdigits() // 3
- lo = rbigint(n._digits[:size_n], 1)
- mid = rbigint(n._digits[size_n:size_n * 2], 1)
- hi = rbigint(n._digits[size_n *2:], 1)
- lo._normalize()
- mid._normalize()
- hi._normalize()
- return hi, mid, lo
-
-THREERBIGINT = rbigint.fromint(3)
-def _tc_mul(a, b):
- """
- Toom Cook
- """
- asize = a.numdigits()
- bsize = b.numdigits()
-
- # Split a & b into hi, mid and lo pieces.
- shift = bsize // 3
- ah, am, al = _tcmul_split(a)
- assert ah.sign == 1 # the split isn't degenerate
-
- if a is b:
- bh = ah
- bm = am
- bl = al
- else:
- bh, bm, bl = _tcmul_split(b)
-
- # 2. ahl, bhl
- ahl = al.add(ah)
- bhl = bl.add(bh)
-
- # Points
- v0 = al.mul(bl)
- v1 = ahl.add(bm).mul(bhl.add(bm))
-
- vn1 = ahl.sub(bm).mul(bhl.sub(bm))
- v2 = al.add(am.lqshift(1)).add(ah.lshift(2)).mul(bl.add(bm.lqshift(1)).add(bh.lqshift(2)))
-
- vinf = ah.mul(bh)
-
- # Construct
- t1 = v0.mul(THREERBIGINT).add(vn1.lqshift(1)).add(v2)
- _inplace_divrem1(t1, t1, 6)
- t1 = t1.sub(vinf.lqshift(1))
- t2 = v1
- _v_iadd(t2, 0, t2.numdigits(), vn1, vn1.numdigits())
- _v_rshift(t2, t2, t2.numdigits(), 1)
-
- r1 = v1.sub(t1)
- r2 = t2
- _v_isub(r2, 0, r2.numdigits(), v0, v0.numdigits())
- r2 = r2.sub(vinf)
- r3 = t1
- _v_isub(r3, 0, r3.numdigits(), t2, t2.numdigits())
-
- # Now we fit t+ t2 + t4 into the new string.
- # Now we got to add the r1 and r3 in the mid shift.
- # Allocate result space.
- ret = rbigint([NULLDIGIT] * (4 * shift + vinf.numdigits() + 1), 1) # This is because of the size of vinf
-
- ret._digits[:v0.numdigits()] = v0._digits
- assert t2.sign >= 0
- assert 2*shift + t2.numdigits() < ret.numdigits()
- ret._digits[shift * 2:shift * 2+r2.numdigits()] = r2._digits
- assert vinf.sign >= 0
- assert 4*shift + vinf.numdigits() <= ret.numdigits()
- ret._digits[shift*4:shift*4+vinf.numdigits()] = vinf._digits
-
-
- i = ret.numdigits() - shift
- _v_iadd(ret, shift * 3, i, r3, r3.numdigits())
- _v_iadd(ret, shift, i, r1, r1.numdigits())
-
-
- ret._normalize()
- return ret
-
-
def _kmul_split(n, size):
"""
A helper for Karatsuba multiplication (k_mul).
@@ -1556,20 +1463,9 @@
size_v = v.numdigits()
size_w = w.numdigits()
assert 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
- assert size_a >= 0
+ assert size_a > 0
a = rbigint([NULLDIGIT] * size_a, 1, size_a)
wm1 = w.widedigit(abs(size_w-1))
@@ -1635,95 +1531,6 @@
_inplace_divrem1(v, v, d, size_v)
v._normalize()
return a, v
-
- """
- 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 """
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
@@ -3,7 +3,7 @@
import operator, sys, array
from random import random, randint, sample
from pypy.rlib.rbigint import rbigint, SHIFT, MASK, KARATSUBA_CUTOFF
-from pypy.rlib.rbigint import _store_digit, _mask_digit, _tc_mul
+from pypy.rlib.rbigint import _store_digit, _mask_digit
from pypy.rlib import rbigint as lobj
from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong, intmask
from pypy.rpython.test.test_llinterp import interpret
@@ -462,12 +462,6 @@
assert x.format('.!') == (
'-!....!!..!!..!.!!.!......!...!...!!!........!')
assert x.format('abcdefghijkl', '<<', '>>') == '-<<cakdkgdijffjf>>'
-
- def test_tc_mul(self):
- a = rbigint.fromlong(1<<200)
- b = rbigint.fromlong(1<<300)
- print _tc_mul(a, b)
- assert _tc_mul(a, b).tolong() == ((1<<300)*(1<<200))
def test_overzelous_assertion(self):
a = rbigint.fromlong(-1<<10000)
@@ -536,17 +530,17 @@
def test__x_divrem(self):
x = 12345678901234567890L
for i in range(100):
- y = long(randint(0, 1 << 60))
+ y = long(randint(1, 1 << 60))
y <<= 60
- y += randint(0, 1 << 60)
+ y += randint(1, 1 << 60)
f1 = rbigint.fromlong(x)
f2 = rbigint.fromlong(y)
div, rem = lobj._x_divrem(f1, f2)
_div, _rem = divmod(x, y)
- print div.tolong() == _div
- print rem.tolong() == _rem
+ assert div.tolong() == _div
+ assert rem.tolong() == _rem
- def test__divrem(self):
+ def test_divmod(self):
x = 12345678901234567890L
for i in range(100):
y = long(randint(0, 1 << 60))
@@ -557,10 +551,10 @@
sy *= y
f1 = rbigint.fromlong(sx)
f2 = rbigint.fromlong(sy)
- div, rem = lobj._x_divrem(f1, f2)
+ div, rem = f1.divmod(f2)
_div, _rem = divmod(sx, sy)
- print div.tolong() == _div
- print rem.tolong() == _rem
+ assert div.tolong() == _div
+ assert rem.tolong() == _rem
# testing Karatsuba stuff
def test__v_iadd(self):
More information about the pypy-commit
mailing list