| import unittest |
| from test import test_support |
| |
| import random |
| |
| # Used for lazy formatting of failure messages |
| class Frm(object): |
| def __init__(self, format, *args): |
| self.format = format |
| self.args = args |
| |
| def __str__(self): |
| return self.format % self.args |
| |
| # SHIFT should match the value in longintrepr.h for best testing. |
| SHIFT = 15 |
| BASE = 2 ** SHIFT |
| MASK = BASE - 1 |
| KARATSUBA_CUTOFF = 70 # from longobject.c |
| |
| # Max number of base BASE digits to use in test cases. Doubling |
| # this will more than double the runtime. |
| MAXDIGITS = 15 |
| |
| # build some special values |
| special = map(int, [0, 1, 2, BASE, BASE >> 1]) |
| special.append(0x5555555555555555) |
| special.append(0xaaaaaaaaaaaaaaaa) |
| # some solid strings of one bits |
| p2 = 4 # 0 and 1 already added |
| for i in range(2*SHIFT): |
| special.append(p2 - 1) |
| p2 = p2 << 1 |
| del p2 |
| # add complements & negations |
| special = special + map(lambda x: ~x, special) + \ |
| map(lambda x: -x, special) |
| |
| |
| class LongTest(unittest.TestCase): |
| |
| # Get quasi-random long consisting of ndigits digits (in base BASE). |
| # quasi == the most-significant digit will not be 0, and the number |
| # is constructed to contain long strings of 0 and 1 bits. These are |
| # more likely than random bits to provoke digit-boundary errors. |
| # The sign of the number is also random. |
| |
| def getran(self, ndigits): |
| self.assert_(ndigits > 0) |
| nbits_hi = ndigits * SHIFT |
| nbits_lo = nbits_hi - SHIFT + 1 |
| answer = 0 |
| nbits = 0 |
| r = int(random.random() * (SHIFT * 2)) | 1 # force 1 bits to start |
| while nbits < nbits_lo: |
| bits = (r >> 1) + 1 |
| bits = min(bits, nbits_hi - nbits) |
| self.assert_(1 <= bits <= SHIFT) |
| nbits = nbits + bits |
| answer = answer << bits |
| if r & 1: |
| answer = answer | ((1 << bits) - 1) |
| r = int(random.random() * (SHIFT * 2)) |
| self.assert_(nbits_lo <= nbits <= nbits_hi) |
| if random.random() < 0.5: |
| answer = -answer |
| return answer |
| |
| # Get random long consisting of ndigits random digits (relative to base |
| # BASE). The sign bit is also random. |
| |
| def getran2(ndigits): |
| answer = 0 |
| for i in xrange(ndigits): |
| answer = (answer << SHIFT) | random.randint(0, MASK) |
| if random.random() < 0.5: |
| answer = -answer |
| return answer |
| |
| def check_division(self, x, y): |
| eq = self.assertEqual |
| q, r = divmod(x, y) |
| q2, r2 = x//y, x%y |
| pab, pba = x*y, y*x |
| eq(pab, pba, Frm("multiplication does not commute for %r and %r", x, y)) |
| eq(q, q2, Frm("divmod returns different quotient than / for %r and %r", x, y)) |
| eq(r, r2, Frm("divmod returns different mod than %% for %r and %r", x, y)) |
| eq(x, q*y + r, Frm("x != q*y + r after divmod on x=%r, y=%r", x, y)) |
| if y > 0: |
| self.assert_(0 <= r < y, Frm("bad mod from divmod on %r and %r", x, y)) |
| else: |
| self.assert_(y < r <= 0, Frm("bad mod from divmod on %r and %r", x, y)) |
| |
| def test_division(self): |
| digits = range(1, MAXDIGITS+1) + range(KARATSUBA_CUTOFF, |
| KARATSUBA_CUTOFF + 14) |
| digits.append(KARATSUBA_CUTOFF * 3) |
| for lenx in digits: |
| x = self.getran(lenx) |
| for leny in digits: |
| y = self.getran(leny) or 1 |
| self.check_division(x, y) |
| |
| def test_karatsuba(self): |
| digits = range(1, 5) + range(KARATSUBA_CUTOFF, KARATSUBA_CUTOFF + 10) |
| digits.extend([KARATSUBA_CUTOFF * 10, KARATSUBA_CUTOFF * 100]) |
| |
| bits = [digit * SHIFT for digit in digits] |
| |
| # Test products of long strings of 1 bits -- (2**x-1)*(2**y-1) == |
| # 2**(x+y) - 2**x - 2**y + 1, so the proper result is easy to check. |
| for abits in bits: |
| a = (1 << abits) - 1 |
| for bbits in bits: |
| if bbits < abits: |
| continue |
| b = (1 << bbits) - 1 |
| x = a * b |
| y = ((1 << (abits + bbits)) - |
| (1 << abits) - |
| (1 << bbits) + |
| 1) |
| self.assertEqual(x, y, |
| Frm("bad result for a*b: a=%r, b=%r, x=%r, y=%r", a, b, x, y)) |
| |
| def check_bitop_identities_1(self, x): |
| eq = self.assertEqual |
| eq(x & 0, 0, Frm("x & 0 != 0 for x=%r", x)) |
| eq(x | 0, x, Frm("x | 0 != x for x=%r", x)) |
| eq(x ^ 0, x, Frm("x ^ 0 != x for x=%r", x)) |
| eq(x & -1, x, Frm("x & -1 != x for x=%r", x)) |
| eq(x | -1, -1, Frm("x | -1 != -1 for x=%r", x)) |
| eq(x ^ -1, ~x, Frm("x ^ -1 != ~x for x=%r", x)) |
| eq(x, ~~x, Frm("x != ~~x for x=%r", x)) |
| eq(x & x, x, Frm("x & x != x for x=%r", x)) |
| eq(x | x, x, Frm("x | x != x for x=%r", x)) |
| eq(x ^ x, 0, Frm("x ^ x != 0 for x=%r", x)) |
| eq(x & ~x, 0, Frm("x & ~x != 0 for x=%r", x)) |
| eq(x | ~x, -1, Frm("x | ~x != -1 for x=%r", x)) |
| eq(x ^ ~x, -1, Frm("x ^ ~x != -1 for x=%r", x)) |
| eq(-x, 1 + ~x, Frm("not -x == 1 + ~x for x=%r", x)) |
| eq(-x, ~(x-1), Frm("not -x == ~(x-1) forx =%r", x)) |
| for n in xrange(2*SHIFT): |
| p2 = 2 ** n |
| eq(x << n >> n, x, |
| Frm("x << n >> n != x for x=%r, n=%r", (x, n))) |
| eq(x // p2, x >> n, |
| Frm("x // p2 != x >> n for x=%r n=%r p2=%r", (x, n, p2))) |
| eq(x * p2, x << n, |
| Frm("x * p2 != x << n for x=%r n=%r p2=%r", (x, n, p2))) |
| eq(x & -p2, x >> n << n, |
| Frm("not x & -p2 == x >> n << n for x=%r n=%r p2=%r", (x, n, p2))) |
| eq(x & -p2, x & ~(p2 - 1), |
| Frm("not x & -p2 == x & ~(p2 - 1) for x=%r n=%r p2=%r", (x, n, p2))) |
| |
| def check_bitop_identities_2(self, x, y): |
| eq = self.assertEqual |
| eq(x & y, y & x, Frm("x & y != y & x for x=%r, y=%r", (x, y))) |
| eq(x | y, y | x, Frm("x | y != y | x for x=%r, y=%r", (x, y))) |
| eq(x ^ y, y ^ x, Frm("x ^ y != y ^ x for x=%r, y=%r", (x, y))) |
| eq(x ^ y ^ x, y, Frm("x ^ y ^ x != y for x=%r, y=%r", (x, y))) |
| eq(x & y, ~(~x | ~y), Frm("x & y != ~(~x | ~y) for x=%r, y=%r", (x, y))) |
| eq(x | y, ~(~x & ~y), Frm("x | y != ~(~x & ~y) for x=%r, y=%r", (x, y))) |
| eq(x ^ y, (x | y) & ~(x & y), |
| Frm("x ^ y != (x | y) & ~(x & y) for x=%r, y=%r", (x, y))) |
| eq(x ^ y, (x & ~y) | (~x & y), |
| Frm("x ^ y == (x & ~y) | (~x & y) for x=%r, y=%r", (x, y))) |
| eq(x ^ y, (x | y) & (~x | ~y), |
| Frm("x ^ y == (x | y) & (~x | ~y) for x=%r, y=%r", (x, y))) |
| |
| def check_bitop_identities_3(self, x, y, z): |
| eq = self.assertEqual |
| eq((x & y) & z, x & (y & z), |
| Frm("(x & y) & z != x & (y & z) for x=%r, y=%r, z=%r", (x, y, z))) |
| eq((x | y) | z, x | (y | z), |
| Frm("(x | y) | z != x | (y | z) for x=%r, y=%r, z=%r", (x, y, z))) |
| eq((x ^ y) ^ z, x ^ (y ^ z), |
| Frm("(x ^ y) ^ z != x ^ (y ^ z) for x=%r, y=%r, z=%r", (x, y, z))) |
| eq(x & (y | z), (x & y) | (x & z), |
| Frm("x & (y | z) != (x & y) | (x & z) for x=%r, y=%r, z=%r", (x, y, z))) |
| eq(x | (y & z), (x | y) & (x | z), |
| Frm("x | (y & z) != (x | y) & (x | z) for x=%r, y=%r, z=%r", (x, y, z))) |
| |
| def test_bitop_identities(self): |
| for x in special: |
| self.check_bitop_identities_1(x) |
| digits = xrange(1, MAXDIGITS+1) |
| for lenx in digits: |
| x = self.getran(lenx) |
| self.check_bitop_identities_1(x) |
| for leny in digits: |
| y = self.getran(leny) |
| self.check_bitop_identities_2(x, y) |
| self.check_bitop_identities_3(x, y, self.getran((lenx + leny)//2)) |
| |
| def slow_format(self, x, base): |
| if (x, base) == (0, 8): |
| # this is an oddball! |
| return "0" |
| digits = [] |
| sign = 0 |
| if x < 0: |
| sign, x = 1, -x |
| while x: |
| x, r = divmod(x, base) |
| digits.append(int(r)) |
| digits.reverse() |
| digits = digits or [0] |
| return '-'[:sign] + \ |
| {8: '0', 10: '', 16: '0x'}[base] + \ |
| "".join(map(lambda i: "0123456789abcdef"[i], digits)) |
| |
| def check_format_1(self, x): |
| for base, mapper in (8, oct), (10, repr), (16, hex): |
| got = mapper(x) |
| expected = self.slow_format(x, base) |
| msg = Frm("%s returned %r but expected %r for %r", |
| mapper.__name__, got, expected, x) |
| self.assertEqual(got, expected, msg) |
| self.assertEqual(int(got, 0), x, Frm('long("%s", 0) != %r', got, x)) |
| # str() has to be checked a little differently since there's no |
| # trailing "L" |
| got = str(x) |
| expected = self.slow_format(x, 10) |
| msg = Frm("%s returned %r but expected %r for %r", |
| mapper.__name__, got, expected, x) |
| self.assertEqual(got, expected, msg) |
| |
| def test_format(self): |
| for x in special: |
| self.check_format_1(x) |
| for i in xrange(10): |
| for lenx in xrange(1, MAXDIGITS+1): |
| x = self.getran(lenx) |
| self.check_format_1(x) |
| |
| def test_misc(self): |
| import sys |
| |
| # check the extremes in int<->long conversion |
| hugepos = sys.maxint |
| hugeneg = -hugepos - 1 |
| hugepos_aslong = int(hugepos) |
| hugeneg_aslong = int(hugeneg) |
| self.assertEqual(hugepos, hugepos_aslong, "long(sys.maxint) != sys.maxint") |
| self.assertEqual(hugeneg, hugeneg_aslong, |
| "long(-sys.maxint-1) != -sys.maxint-1") |
| |
| # long -> int should not fail for hugepos_aslong or hugeneg_aslong |
| x = int(hugepos_aslong) |
| try: |
| self.assertEqual(x, hugepos, |
| "converting sys.maxint to long and back to int fails") |
| except OverflowError: |
| self.fail("int(long(sys.maxint)) overflowed!") |
| if not isinstance(x, int): |
| raise TestFailed("int(long(sys.maxint)) should have returned int") |
| x = int(hugeneg_aslong) |
| try: |
| self.assertEqual(x, hugeneg, |
| "converting -sys.maxint-1 to long and back to int fails") |
| except OverflowError: |
| self.fail("int(long(-sys.maxint-1)) overflowed!") |
| if not isinstance(x, int): |
| raise TestFailed("int(long(-sys.maxint-1)) should have " |
| "returned int") |
| # but long -> int should overflow for hugepos+1 and hugeneg-1 |
| x = hugepos_aslong + 1 |
| try: |
| y = int(x) |
| except OverflowError: |
| self.fail("int(long(sys.maxint) + 1) mustn't overflow") |
| self.assert_(isinstance(y, int), |
| "int(long(sys.maxint) + 1) should have returned long") |
| |
| x = hugeneg_aslong - 1 |
| try: |
| y = int(x) |
| except OverflowError: |
| self.fail("int(long(-sys.maxint-1) - 1) mustn't overflow") |
| self.assert_(isinstance(y, int), |
| "int(long(-sys.maxint-1) - 1) should have returned long") |
| |
| class long2(int): |
| pass |
| x = long2(1<<100) |
| y = int(x) |
| self.assert_(type(y) is int, |
| "overflowing int conversion must return long not long subtype") |
| |
| # long -> Py_ssize_t conversion |
| class X(object): |
| def __getslice__(self, i, j): |
| return i, j |
| |
| self.assertEqual(X()[-5:7], (-5, 7)) |
| # use the clamping effect to test the smallest and largest longs |
| # that fit a Py_ssize_t |
| slicemin, slicemax = X()[-2**100:2**100] |
| self.assertEqual(X()[slicemin:slicemax], (slicemin, slicemax)) |
| |
| # ----------------------------------- tests of auto int->long conversion |
| |
| def test_auto_overflow(self): |
| import math, sys |
| |
| special = [0, 1, 2, 3, sys.maxint-1, sys.maxint, sys.maxint+1] |
| sqrt = int(math.sqrt(sys.maxint)) |
| special.extend([sqrt-1, sqrt, sqrt+1]) |
| special.extend([-i for i in special]) |
| |
| def checkit(*args): |
| # Heavy use of nested scopes here! |
| self.assertEqual(got, expected, |
| Frm("for %r expected %r got %r", args, expected, got)) |
| |
| for x in special: |
| longx = int(x) |
| |
| expected = -longx |
| got = -x |
| checkit('-', x) |
| |
| for y in special: |
| longy = int(y) |
| |
| expected = longx + longy |
| got = x + y |
| checkit(x, '+', y) |
| |
| expected = longx - longy |
| got = x - y |
| checkit(x, '-', y) |
| |
| expected = longx * longy |
| got = x * y |
| checkit(x, '*', y) |
| |
| if y: |
| expected = longx / longy |
| got = x / y |
| checkit(x, '/', y) |
| |
| expected = longx // longy |
| got = x // y |
| checkit(x, '//', y) |
| |
| expected = divmod(longx, longy) |
| got = divmod(longx, longy) |
| checkit(x, 'divmod', y) |
| |
| if abs(y) < 5 and not (x == 0 and y < 0): |
| expected = longx ** longy |
| got = x ** y |
| checkit(x, '**', y) |
| |
| for z in special: |
| if z != 0 : |
| if y >= 0: |
| expected = pow(longx, longy, int(z)) |
| got = pow(x, y, z) |
| checkit('pow', x, y, '%', z) |
| else: |
| self.assertRaises(TypeError, pow,longx, longy, int(z)) |
| |
| def test_float_overflow(self): |
| import math |
| |
| for x in -2.0, -1.0, 0.0, 1.0, 2.0: |
| self.assertEqual(float(int(x)), x) |
| |
| shuge = '12345' * 120 |
| huge = 1 << 30000 |
| mhuge = -huge |
| namespace = {'huge': huge, 'mhuge': mhuge, 'shuge': shuge, 'math': math} |
| for test in ["float(huge)", "float(mhuge)", |
| "complex(huge)", "complex(mhuge)", |
| "complex(huge, 1)", "complex(mhuge, 1)", |
| "complex(1, huge)", "complex(1, mhuge)", |
| "1. + huge", "huge + 1.", "1. + mhuge", "mhuge + 1.", |
| "1. - huge", "huge - 1.", "1. - mhuge", "mhuge - 1.", |
| "1. * huge", "huge * 1.", "1. * mhuge", "mhuge * 1.", |
| "1. // huge", "huge // 1.", "1. // mhuge", "mhuge // 1.", |
| "1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.", |
| "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.", |
| "math.sin(huge)", "math.sin(mhuge)", |
| "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better |
| "math.floor(huge)", "math.floor(mhuge)"]: |
| |
| self.assertRaises(OverflowError, eval, test, namespace) |
| |
| # XXX Perhaps float(shuge) can raise OverflowError on some box? |
| # The comparison should not. |
| self.assertNotEqual(float(shuge), int(shuge), |
| "float(shuge) should not equal int(shuge)") |
| |
| def test_logs(self): |
| import math |
| |
| LOG10E = math.log10(math.e) |
| |
| for exp in range(10) + [100, 1000, 10000]: |
| value = 10 ** exp |
| log10 = math.log10(value) |
| self.assertAlmostEqual(log10, exp) |
| |
| # log10(value) == exp, so log(value) == log10(value)/log10(e) == |
| # exp/LOG10E |
| expected = exp / LOG10E |
| log = math.log(value) |
| self.assertAlmostEqual(log, expected) |
| |
| for bad in -(1 << 10000), -2, 0: |
| self.assertRaises(ValueError, math.log, bad) |
| self.assertRaises(ValueError, math.log10, bad) |
| |
| def test_mixed_compares(self): |
| eq = self.assertEqual |
| import math |
| import sys |
| |
| # We're mostly concerned with that mixing floats and longs does the |
| # right stuff, even when longs are too large to fit in a float. |
| # The safest way to check the results is to use an entirely different |
| # method, which we do here via a skeletal rational class (which |
| # represents all Python ints, longs and floats exactly). |
| class Rat: |
| def __init__(self, value): |
| if isinstance(value, int): |
| self.n = value |
| self.d = 1 |
| elif isinstance(value, float): |
| # Convert to exact rational equivalent. |
| f, e = math.frexp(abs(value)) |
| assert f == 0 or 0.5 <= f < 1.0 |
| # |value| = f * 2**e exactly |
| |
| # Suck up CHUNK bits at a time; 28 is enough so that we suck |
| # up all bits in 2 iterations for all known binary double- |
| # precision formats, and small enough to fit in an int. |
| CHUNK = 28 |
| top = 0 |
| # invariant: |value| = (top + f) * 2**e exactly |
| while f: |
| f = math.ldexp(f, CHUNK) |
| digit = int(f) |
| assert digit >> CHUNK == 0 |
| top = (top << CHUNK) | digit |
| f -= digit |
| assert 0.0 <= f < 1.0 |
| e -= CHUNK |
| |
| # Now |value| = top * 2**e exactly. |
| if e >= 0: |
| n = top << e |
| d = 1 |
| else: |
| n = top |
| d = 1 << -e |
| if value < 0: |
| n = -n |
| self.n = n |
| self.d = d |
| assert float(n) / float(d) == value |
| else: |
| raise TypeError("can't deal with %r" % val) |
| |
| def __cmp__(self, other): |
| if not isinstance(other, Rat): |
| other = Rat(other) |
| return cmp(self.n * other.d, self.d * other.n) |
| |
| cases = [0, 0.001, 0.99, 1.0, 1.5, 1e20, 1e200] |
| # 2**48 is an important boundary in the internals. 2**53 is an |
| # important boundary for IEEE double precision. |
| for t in 2.0**48, 2.0**50, 2.0**53: |
| cases.extend([t - 1.0, t - 0.3, t, t + 0.3, t + 1.0, |
| int(t-1), int(t), int(t+1)]) |
| cases.extend([0, 1, 2, sys.maxint, float(sys.maxint)]) |
| # 1L<<20000 should exceed all double formats. long(1e200) is to |
| # check that we get equality with 1e200 above. |
| t = int(1e200) |
| cases.extend([0, 1, 2, 1 << 20000, t-1, t, t+1]) |
| cases.extend([-x for x in cases]) |
| for x in cases: |
| Rx = Rat(x) |
| for y in cases: |
| Ry = Rat(y) |
| Rcmp = cmp(Rx, Ry) |
| xycmp = cmp(x, y) |
| eq(Rcmp, xycmp, Frm("%r %r %d %d", x, y, Rcmp, xycmp)) |
| eq(x == y, Rcmp == 0, Frm("%r == %r %d", x, y, Rcmp)) |
| eq(x != y, Rcmp != 0, Frm("%r != %r %d", x, y, Rcmp)) |
| eq(x < y, Rcmp < 0, Frm("%r < %r %d", x, y, Rcmp)) |
| eq(x <= y, Rcmp <= 0, Frm("%r <= %r %d", x, y, Rcmp)) |
| eq(x > y, Rcmp > 0, Frm("%r > %r %d", x, y, Rcmp)) |
| eq(x >= y, Rcmp >= 0, Frm("%r >= %r %d", x, y, Rcmp)) |
| |
| def test_main(): |
| test_support.run_unittest(LongTest) |
| |
| if __name__ == "__main__": |
| test_main() |