mirror of
https://github.com/python/cpython.git
synced 2024-12-23 16:54:14 +08:00
587c2bfede
svn+ssh://pythondev@svn.python.org/python/trunk ........ r60054 | christian.heimes | 2008-01-18 20:12:56 +0100 (Fri, 18 Jan 2008) | 1 line Silence Coverity false alerts with CIDs #172, #183, #184 ........ r60057 | guido.van.rossum | 2008-01-18 21:56:30 +0100 (Fri, 18 Jan 2008) | 3 lines Fix an edge case whereby the __del__() method of a classic class could create a new weakref to the object. ........ r60058 | raymond.hettinger | 2008-01-18 22:14:58 +0100 (Fri, 18 Jan 2008) | 1 line Better variable name in an example. ........ r60063 | guido.van.rossum | 2008-01-19 00:05:40 +0100 (Sat, 19 Jan 2008) | 3 lines This got fixed for classic classes in r60057, and backported to 2.5.2 in 60056. ........ r60068 | jeffrey.yasskin | 2008-01-19 10:56:06 +0100 (Sat, 19 Jan 2008) | 4 lines Several tweaks: add construction from strings and .from_decimal(), change __init__ to __new__ to enforce immutability, and remove "rational." from repr and the parens from str. ........ r60069 | georg.brandl | 2008-01-19 11:11:27 +0100 (Sat, 19 Jan 2008) | 2 lines Fix markup. ........ r60070 | georg.brandl | 2008-01-19 11:16:09 +0100 (Sat, 19 Jan 2008) | 3 lines Amend curses docs by info how to write non-ascii characters. Thanks to Jeroen Ruigrok van der Werven. ........ r60071 | georg.brandl | 2008-01-19 11:18:07 +0100 (Sat, 19 Jan 2008) | 2 lines Indentation normalization. ........ r60073 | facundo.batista | 2008-01-19 13:32:27 +0100 (Sat, 19 Jan 2008) | 5 lines Fix issue #1822: MIMEMultipart.is_multipart() behaves correctly for a just-created (and empty) instance. Added tests for this. Thanks Jonathan Share. ........ r60074 | andrew.kuchling | 2008-01-19 14:33:20 +0100 (Sat, 19 Jan 2008) | 1 line Polish sentence ........ r60075 | christian.heimes | 2008-01-19 14:46:06 +0100 (Sat, 19 Jan 2008) | 1 line Added unit test to verify that threading.local doesn't cause ref leaks. It seems that the thread local storage always keeps the storage of the last stopped thread alive. Can anybody comment on it, please? ........ r60076 | christian.heimes | 2008-01-19 16:06:09 +0100 (Sat, 19 Jan 2008) | 1 line Update for threading.local test. ........ r60077 | andrew.kuchling | 2008-01-19 16:16:37 +0100 (Sat, 19 Jan 2008) | 1 line Polish sentence ........ r60078 | georg.brandl | 2008-01-19 16:22:16 +0100 (Sat, 19 Jan 2008) | 2 lines Fix typos. ........
345 lines
14 KiB
Python
345 lines
14 KiB
Python
"""Tests for Lib/rational.py."""
|
|
|
|
from decimal import Decimal
|
|
from test.test_support import run_unittest, verbose
|
|
import math
|
|
import operator
|
|
import rational
|
|
import unittest
|
|
R = rational.Rational
|
|
|
|
def _components(r):
|
|
return (r.numerator, r.denominator)
|
|
|
|
class RationalTest(unittest.TestCase):
|
|
|
|
def assertTypedEquals(self, expected, actual):
|
|
"""Asserts that both the types and values are the same."""
|
|
self.assertEquals(type(expected), type(actual))
|
|
self.assertEquals(expected, actual)
|
|
|
|
def assertRaisesMessage(self, exc_type, message,
|
|
callable, *args, **kwargs):
|
|
"""Asserts that callable(*args, **kwargs) raises exc_type(message)."""
|
|
try:
|
|
callable(*args, **kwargs)
|
|
except exc_type as e:
|
|
self.assertEquals(message, str(e))
|
|
else:
|
|
self.fail("%s not raised" % exc_type.__name__)
|
|
|
|
def testInit(self):
|
|
self.assertEquals((0, 1), _components(R()))
|
|
self.assertEquals((7, 1), _components(R(7)))
|
|
self.assertEquals((7, 3), _components(R(R(7, 3))))
|
|
|
|
self.assertEquals((-1, 1), _components(R(-1, 1)))
|
|
self.assertEquals((-1, 1), _components(R(1, -1)))
|
|
self.assertEquals((1, 1), _components(R(-2, -2)))
|
|
self.assertEquals((1, 2), _components(R(5, 10)))
|
|
self.assertEquals((7, 15), _components(R(7, 15)))
|
|
self.assertEquals((10**23, 1), _components(R(10**23)))
|
|
|
|
self.assertRaisesMessage(ZeroDivisionError, "Rational(12, 0)",
|
|
R, 12, 0)
|
|
self.assertRaises(TypeError, R, 1.5)
|
|
self.assertRaises(TypeError, R, 1.5 + 3j)
|
|
|
|
self.assertRaises(TypeError, R, R(1, 2), 3)
|
|
self.assertRaises(TypeError, R, "3/2", 3)
|
|
|
|
def testFromString(self):
|
|
self.assertEquals((5, 1), _components(R("5")))
|
|
self.assertEquals((3, 2), _components(R("3/2")))
|
|
self.assertEquals((3, 2), _components(R(" \n +3/2")))
|
|
self.assertEquals((-3, 2), _components(R("-3/2 ")))
|
|
self.assertEquals((3, 2), _components(R(" 03/02 \n ")))
|
|
self.assertEquals((3, 2), _components(R(" 03/02 \n ")))
|
|
|
|
self.assertRaisesMessage(
|
|
ZeroDivisionError, "Rational(3, 0)",
|
|
R, "3/0")
|
|
self.assertRaisesMessage(
|
|
ValueError, "Invalid literal for Rational: 3/",
|
|
R, "3/")
|
|
self.assertRaisesMessage(
|
|
ValueError, "Invalid literal for Rational: 3 /2",
|
|
R, "3 /2")
|
|
self.assertRaisesMessage(
|
|
# Denominators don't need a sign.
|
|
ValueError, "Invalid literal for Rational: 3/+2",
|
|
R, "3/+2")
|
|
self.assertRaisesMessage(
|
|
# Imitate float's parsing.
|
|
ValueError, "Invalid literal for Rational: + 3/2",
|
|
R, "+ 3/2")
|
|
self.assertRaisesMessage(
|
|
# Only parse fractions, not decimals.
|
|
ValueError, "Invalid literal for Rational: 3.2",
|
|
R, "3.2")
|
|
|
|
def testImmutable(self):
|
|
r = R(7, 3)
|
|
r.__init__(2, 15)
|
|
self.assertEquals((7, 3), _components(r))
|
|
|
|
def testFromFloat(self):
|
|
self.assertRaisesMessage(
|
|
TypeError, "Rational.from_float() only takes floats, not 3 (int)",
|
|
R.from_float, 3)
|
|
|
|
self.assertEquals((0, 1), _components(R.from_float(-0.0)))
|
|
self.assertEquals((10, 1), _components(R.from_float(10.0)))
|
|
self.assertEquals((-5, 2), _components(R.from_float(-2.5)))
|
|
self.assertEquals((99999999999999991611392, 1),
|
|
_components(R.from_float(1e23)))
|
|
self.assertEquals(float(10**23), float(R.from_float(1e23)))
|
|
self.assertEquals((3602879701896397, 1125899906842624),
|
|
_components(R.from_float(3.2)))
|
|
self.assertEquals(3.2, float(R.from_float(3.2)))
|
|
|
|
inf = 1e1000
|
|
nan = inf - inf
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert inf to Rational.",
|
|
R.from_float, inf)
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert -inf to Rational.",
|
|
R.from_float, -inf)
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert nan to Rational.",
|
|
R.from_float, nan)
|
|
|
|
def testFromDecimal(self):
|
|
self.assertRaisesMessage(
|
|
TypeError,
|
|
"Rational.from_decimal() only takes Decimals, not 3 (int)",
|
|
R.from_decimal, 3)
|
|
self.assertEquals(R(0), R.from_decimal(Decimal("-0")))
|
|
self.assertEquals(R(5, 10), R.from_decimal(Decimal("0.5")))
|
|
self.assertEquals(R(5, 1000), R.from_decimal(Decimal("5e-3")))
|
|
self.assertEquals(R(5000), R.from_decimal(Decimal("5e3")))
|
|
self.assertEquals(1 - R(1, 10**30),
|
|
R.from_decimal(Decimal("0." + "9" * 30)))
|
|
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert Infinity to Rational.",
|
|
R.from_decimal, Decimal("inf"))
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert -Infinity to Rational.",
|
|
R.from_decimal, Decimal("-inf"))
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert NaN to Rational.",
|
|
R.from_decimal, Decimal("nan"))
|
|
self.assertRaisesMessage(
|
|
TypeError, "Cannot convert sNaN to Rational.",
|
|
R.from_decimal, Decimal("snan"))
|
|
|
|
def testConversions(self):
|
|
self.assertTypedEquals(-1, trunc(R(-11, 10)))
|
|
self.assertTypedEquals(-2, math.floor(R(-11, 10)))
|
|
self.assertTypedEquals(-1, math.ceil(R(-11, 10)))
|
|
self.assertTypedEquals(-1, math.ceil(R(-10, 10)))
|
|
|
|
self.assertTypedEquals(0, round(R(-1, 10)))
|
|
self.assertTypedEquals(0, round(R(-5, 10)))
|
|
self.assertTypedEquals(-2, round(R(-15, 10)))
|
|
self.assertTypedEquals(-1, round(R(-7, 10)))
|
|
|
|
self.assertEquals(False, bool(R(0, 1)))
|
|
self.assertEquals(True, bool(R(3, 2)))
|
|
self.assertTypedEquals(0.1, float(R(1, 10)))
|
|
|
|
# Check that __float__ isn't implemented by converting the
|
|
# numerator and denominator to float before dividing.
|
|
self.assertRaises(OverflowError, float, int('2'*400+'7'))
|
|
self.assertAlmostEquals(2.0/3,
|
|
float(R(int('2'*400+'7'), int('3'*400+'1'))))
|
|
|
|
self.assertTypedEquals(0.1+0j, complex(R(1,10)))
|
|
|
|
def testRound(self):
|
|
self.assertTypedEquals(R(-200), round(R(-150), -2))
|
|
self.assertTypedEquals(R(-200), round(R(-250), -2))
|
|
self.assertTypedEquals(R(30), round(R(26), -1))
|
|
self.assertTypedEquals(R(-2, 10), round(R(-15, 100), 1))
|
|
self.assertTypedEquals(R(-2, 10), round(R(-25, 100), 1))
|
|
|
|
|
|
def testArithmetic(self):
|
|
self.assertEquals(R(1, 2), R(1, 10) + R(2, 5))
|
|
self.assertEquals(R(-3, 10), R(1, 10) - R(2, 5))
|
|
self.assertEquals(R(1, 25), R(1, 10) * R(2, 5))
|
|
self.assertEquals(R(1, 4), R(1, 10) / R(2, 5))
|
|
self.assertTypedEquals(2, R(9, 10) // R(2, 5))
|
|
self.assertTypedEquals(10**23, R(10**23, 1) // R(1))
|
|
self.assertEquals(R(2, 3), R(-7, 3) % R(3, 2))
|
|
self.assertEquals(R(8, 27), R(2, 3) ** R(3))
|
|
self.assertEquals(R(27, 8), R(2, 3) ** R(-3))
|
|
self.assertTypedEquals(2.0, R(4) ** R(1, 2))
|
|
z = pow(R(-1), R(1, 2))
|
|
self.assertAlmostEquals(z.real, 0)
|
|
self.assertEquals(z.imag, 1)
|
|
|
|
def testMixedArithmetic(self):
|
|
self.assertTypedEquals(R(11, 10), R(1, 10) + 1)
|
|
self.assertTypedEquals(1.1, R(1, 10) + 1.0)
|
|
self.assertTypedEquals(1.1 + 0j, R(1, 10) + (1.0 + 0j))
|
|
self.assertTypedEquals(R(11, 10), 1 + R(1, 10))
|
|
self.assertTypedEquals(1.1, 1.0 + R(1, 10))
|
|
self.assertTypedEquals(1.1 + 0j, (1.0 + 0j) + R(1, 10))
|
|
|
|
self.assertTypedEquals(R(-9, 10), R(1, 10) - 1)
|
|
self.assertTypedEquals(-0.9, R(1, 10) - 1.0)
|
|
self.assertTypedEquals(-0.9 + 0j, R(1, 10) - (1.0 + 0j))
|
|
self.assertTypedEquals(R(9, 10), 1 - R(1, 10))
|
|
self.assertTypedEquals(0.9, 1.0 - R(1, 10))
|
|
self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - R(1, 10))
|
|
|
|
self.assertTypedEquals(R(1, 10), R(1, 10) * 1)
|
|
self.assertTypedEquals(0.1, R(1, 10) * 1.0)
|
|
self.assertTypedEquals(0.1 + 0j, R(1, 10) * (1.0 + 0j))
|
|
self.assertTypedEquals(R(1, 10), 1 * R(1, 10))
|
|
self.assertTypedEquals(0.1, 1.0 * R(1, 10))
|
|
self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * R(1, 10))
|
|
|
|
self.assertTypedEquals(R(1, 10), R(1, 10) / 1)
|
|
self.assertTypedEquals(0.1, R(1, 10) / 1.0)
|
|
self.assertTypedEquals(0.1 + 0j, R(1, 10) / (1.0 + 0j))
|
|
self.assertTypedEquals(R(10, 1), 1 / R(1, 10))
|
|
self.assertTypedEquals(10.0, 1.0 / R(1, 10))
|
|
self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / R(1, 10))
|
|
|
|
self.assertTypedEquals(0, R(1, 10) // 1)
|
|
self.assertTypedEquals(0, R(1, 10) // 1.0)
|
|
self.assertTypedEquals(10, 1 // R(1, 10))
|
|
self.assertTypedEquals(10**23, 10**22 // R(1, 10))
|
|
self.assertTypedEquals(10, 1.0 // R(1, 10))
|
|
|
|
self.assertTypedEquals(R(1, 10), R(1, 10) % 1)
|
|
self.assertTypedEquals(0.1, R(1, 10) % 1.0)
|
|
self.assertTypedEquals(R(0, 1), 1 % R(1, 10))
|
|
self.assertTypedEquals(0.0, 1.0 % R(1, 10))
|
|
|
|
# No need for divmod since we don't override it.
|
|
|
|
# ** has more interesting conversion rules.
|
|
self.assertTypedEquals(R(100, 1), R(1, 10) ** -2)
|
|
self.assertTypedEquals(R(100, 1), R(10, 1) ** 2)
|
|
self.assertTypedEquals(0.1, R(1, 10) ** 1.0)
|
|
self.assertTypedEquals(0.1 + 0j, R(1, 10) ** (1.0 + 0j))
|
|
self.assertTypedEquals(4 , 2 ** R(2, 1))
|
|
z = pow(-1, R(1, 2))
|
|
self.assertAlmostEquals(0, z.real)
|
|
self.assertEquals(1, z.imag)
|
|
self.assertTypedEquals(R(1, 4) , 2 ** R(-2, 1))
|
|
self.assertTypedEquals(2.0 , 4 ** R(1, 2))
|
|
self.assertTypedEquals(0.25, 2.0 ** R(-2, 1))
|
|
self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** R(1, 10))
|
|
|
|
def testMixingWithDecimal(self):
|
|
# Decimal refuses mixed comparisons.
|
|
self.assertRaisesMessage(
|
|
TypeError,
|
|
"unsupported operand type(s) for +: 'Rational' and 'Decimal'",
|
|
operator.add, R(3,11), Decimal('3.1415926'))
|
|
self.assertNotEquals(R(5, 2), Decimal('2.5'))
|
|
|
|
def testComparisons(self):
|
|
self.assertTrue(R(1, 2) < R(2, 3))
|
|
self.assertFalse(R(1, 2) < R(1, 2))
|
|
self.assertTrue(R(1, 2) <= R(2, 3))
|
|
self.assertTrue(R(1, 2) <= R(1, 2))
|
|
self.assertFalse(R(2, 3) <= R(1, 2))
|
|
self.assertTrue(R(1, 2) == R(1, 2))
|
|
self.assertFalse(R(1, 2) == R(1, 3))
|
|
|
|
def testMixedLess(self):
|
|
self.assertTrue(2 < R(5, 2))
|
|
self.assertFalse(2 < R(4, 2))
|
|
self.assertTrue(R(5, 2) < 3)
|
|
self.assertFalse(R(4, 2) < 2)
|
|
|
|
self.assertTrue(R(1, 2) < 0.6)
|
|
self.assertFalse(R(1, 2) < 0.4)
|
|
self.assertTrue(0.4 < R(1, 2))
|
|
self.assertFalse(0.5 < R(1, 2))
|
|
|
|
def testMixedLessEqual(self):
|
|
self.assertTrue(0.5 <= R(1, 2))
|
|
self.assertFalse(0.6 <= R(1, 2))
|
|
self.assertTrue(R(1, 2) <= 0.5)
|
|
self.assertFalse(R(1, 2) <= 0.4)
|
|
self.assertTrue(2 <= R(4, 2))
|
|
self.assertFalse(2 <= R(3, 2))
|
|
self.assertTrue(R(4, 2) <= 2)
|
|
self.assertFalse(R(5, 2) <= 2)
|
|
|
|
def testBigFloatComparisons(self):
|
|
# Because 10**23 can't be represented exactly as a float:
|
|
self.assertFalse(R(10**23) == float(10**23))
|
|
# The first test demonstrates why these are important.
|
|
self.assertFalse(1e23 < float(R(trunc(1e23) + 1)))
|
|
self.assertTrue(1e23 < R(trunc(1e23) + 1))
|
|
self.assertFalse(1e23 <= R(trunc(1e23) - 1))
|
|
self.assertTrue(1e23 > R(trunc(1e23) - 1))
|
|
self.assertFalse(1e23 >= R(trunc(1e23) + 1))
|
|
|
|
def testBigComplexComparisons(self):
|
|
self.assertFalse(R(10**23) == complex(10**23))
|
|
self.assertTrue(R(10**23) > complex(10**23))
|
|
self.assertFalse(R(10**23) <= complex(10**23))
|
|
|
|
def testMixedEqual(self):
|
|
self.assertTrue(0.5 == R(1, 2))
|
|
self.assertFalse(0.6 == R(1, 2))
|
|
self.assertTrue(R(1, 2) == 0.5)
|
|
self.assertFalse(R(1, 2) == 0.4)
|
|
self.assertTrue(2 == R(4, 2))
|
|
self.assertFalse(2 == R(3, 2))
|
|
self.assertTrue(R(4, 2) == 2)
|
|
self.assertFalse(R(5, 2) == 2)
|
|
|
|
def testStringification(self):
|
|
self.assertEquals("Rational(7,3)", repr(R(7, 3)))
|
|
self.assertEquals("7/3", str(R(7, 3)))
|
|
self.assertEquals("7", str(R(7, 1)))
|
|
|
|
def testHash(self):
|
|
self.assertEquals(hash(2.5), hash(R(5, 2)))
|
|
self.assertEquals(hash(10**50), hash(R(10**50)))
|
|
self.assertNotEquals(hash(float(10**23)), hash(R(10**23)))
|
|
|
|
def testApproximatePi(self):
|
|
# Algorithm borrowed from
|
|
# http://docs.python.org/lib/decimal-recipes.html
|
|
three = R(3)
|
|
lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
|
|
while abs(s - lasts) > R(1, 10**9):
|
|
lasts = s
|
|
n, na = n+na, na+8
|
|
d, da = d+da, da+32
|
|
t = (t * n) / d
|
|
s += t
|
|
self.assertAlmostEquals(math.pi, s)
|
|
|
|
def testApproximateCos1(self):
|
|
# Algorithm borrowed from
|
|
# http://docs.python.org/lib/decimal-recipes.html
|
|
x = R(1)
|
|
i, lasts, s, fact, num, sign = 0, 0, R(1), 1, 1, 1
|
|
while abs(s - lasts) > R(1, 10**9):
|
|
lasts = s
|
|
i += 2
|
|
fact *= i * (i-1)
|
|
num *= x * x
|
|
sign *= -1
|
|
s += num / fact * sign
|
|
self.assertAlmostEquals(math.cos(1), s)
|
|
|
|
def test_main():
|
|
run_unittest(RationalTest)
|
|
|
|
if __name__ == '__main__':
|
|
test_main()
|