test_fractions.py 24.4 KB
Newer Older
Christian Heimes's avatar
Christian Heimes committed
1
"""Tests for Lib/fractions.py."""
2 3

from decimal import Decimal
4
from test.support import run_unittest, requires_IEEE_754
5
import math
6
import numbers
7
import operator
Christian Heimes's avatar
Christian Heimes committed
8
import fractions
9
import sys
10
import unittest
11
from copy import copy, deepcopy
Neal Norwitz's avatar
Neal Norwitz committed
12
from pickle import dumps, loads
Christian Heimes's avatar
Christian Heimes committed
13
F = fractions.Fraction
Christian Heimes's avatar
Christian Heimes committed
14
gcd = fractions.gcd
15

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
class DummyFloat(object):
    """Dummy float class for testing comparisons with Fractions"""

    def __init__(self, value):
        if not isinstance(value, float):
            raise TypeError("DummyFloat can only be initialized from float")
        self.value = value

    def _richcmp(self, other, op):
        if isinstance(other, numbers.Rational):
            return op(F.from_float(self.value), other)
        elif isinstance(other, DummyFloat):
            return op(self.value, other.value)
        else:
            return NotImplemented

    def __eq__(self, other): return self._richcmp(other, operator.eq)
    def __le__(self, other): return self._richcmp(other, operator.le)
    def __lt__(self, other): return self._richcmp(other, operator.lt)
    def __ge__(self, other): return self._richcmp(other, operator.ge)
    def __gt__(self, other): return self._richcmp(other, operator.gt)

    # shouldn't be calling __float__ at all when doing comparisons
    def __float__(self):
        assert False, "__float__ should not be invoked for comparisons"

    # same goes for subtraction
    def __sub__(self, other):
        assert False, "__sub__ should not be invoked for comparisons"
    __rsub__ = __sub__


class DummyRational(object):
    """Test comparison of Fraction with a naive rational implementation."""

    def __init__(self, num, den):
        g = gcd(num, den)
        self.num = num // g
        self.den = den // g

    def __eq__(self, other):
        if isinstance(other, fractions.Fraction):
            return (self.num == other._numerator and
                    self.den == other._denominator)
        else:
            return NotImplemented

    def __lt__(self, other):
        return(self.num * other._denominator < self.den * other._numerator)

    def __gt__(self, other):
        return(self.num * other._denominator > self.den * other._numerator)

    def __le__(self, other):
        return(self.num * other._denominator <= self.den * other._numerator)

    def __ge__(self, other):
        return(self.num * other._denominator >= self.den * other._numerator)

    # this class is for testing comparisons; conversion to float
    # should never be used for a comparison, since it loses accuracy
    def __float__(self):
        assert False, "__float__ should not be invoked"
79

80 81 82
class DummyFraction(fractions.Fraction):
    """Dummy Fraction subclass for copy and deepcopy testing."""

83 84 85
class GcdTest(unittest.TestCase):

    def testMisc(self):
86 87 88 89 90 91 92 93 94 95
        self.assertEqual(0, gcd(0, 0))
        self.assertEqual(1, gcd(1, 0))
        self.assertEqual(-1, gcd(-1, 0))
        self.assertEqual(1, gcd(0, 1))
        self.assertEqual(-1, gcd(0, -1))
        self.assertEqual(1, gcd(7, 1))
        self.assertEqual(-1, gcd(7, -1))
        self.assertEqual(1, gcd(-23, 15))
        self.assertEqual(12, gcd(120, 84))
        self.assertEqual(-12, gcd(84, -120))
96

97 98 99 100

def _components(r):
    return (r.numerator, r.denominator)

101

Christian Heimes's avatar
Christian Heimes committed
102
class FractionTest(unittest.TestCase):
103 104 105

    def assertTypedEquals(self, expected, actual):
        """Asserts that both the types and values are the same."""
106 107
        self.assertEqual(type(expected), type(actual))
        self.assertEqual(expected, actual)
108 109 110 111 112 113 114

    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:
115
            self.assertEqual(message, str(e))
116 117 118 119
        else:
            self.fail("%s not raised" % exc_type.__name__)

    def testInit(self):
120 121 122
        self.assertEqual((0, 1), _components(F()))
        self.assertEqual((7, 1), _components(F(7)))
        self.assertEqual((7, 3), _components(F(F(7, 3))))
123

124 125 126 127 128 129
        self.assertEqual((-1, 1), _components(F(-1, 1)))
        self.assertEqual((-1, 1), _components(F(1, -1)))
        self.assertEqual((1, 1), _components(F(-2, -2)))
        self.assertEqual((1, 2), _components(F(5, 10)))
        self.assertEqual((7, 15), _components(F(7, 15)))
        self.assertEqual((10**23, 1), _components(F(10**23)))
130

131 132 133
        self.assertEqual((3, 77), _components(F(F(3, 7), 11)))
        self.assertEqual((-9, 5), _components(F(2, F(-10, 9))))
        self.assertEqual((2486, 2485), _components(F(F(22, 7), F(355, 113))))
134

Christian Heimes's avatar
Christian Heimes committed
135
        self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)",
Christian Heimes's avatar
Christian Heimes committed
136
                                 F, 12, 0)
Georg Brandl's avatar
Georg Brandl committed
137
        self.assertRaises(TypeError, F, 1.5 + 3j)
138

Georg Brandl's avatar
Georg Brandl committed
139
        self.assertRaises(TypeError, F, "3/2", 3)
140 141 142
        self.assertRaises(TypeError, F, 3, 0j)
        self.assertRaises(TypeError, F, 3, 1j)

143 144
    @requires_IEEE_754
    def testInitFromFloat(self):
145 146 147 148
        self.assertEqual((5, 2), _components(F(2.5)))
        self.assertEqual((0, 1), _components(F(-0.0)))
        self.assertEqual((3602879701896397, 36028797018963968),
                         _components(F(0.1)))
149 150 151 152 153
        self.assertRaises(TypeError, F, float('nan'))
        self.assertRaises(TypeError, F, float('inf'))
        self.assertRaises(TypeError, F, float('-inf'))

    def testInitFromDecimal(self):
154 155 156 157 158 159
        self.assertEqual((11, 10),
                         _components(F(Decimal('1.1'))))
        self.assertEqual((7, 200),
                         _components(F(Decimal('3.5e-2'))))
        self.assertEqual((0, 1),
                         _components(F(Decimal('.000e20'))))
160 161 162 163
        self.assertRaises(TypeError, F, Decimal('nan'))
        self.assertRaises(TypeError, F, Decimal('snan'))
        self.assertRaises(TypeError, F, Decimal('inf'))
        self.assertRaises(TypeError, F, Decimal('-inf'))
164 165

    def testFromString(self):
166 167 168 169 170 171 172 173 174 175 176 177 178 179
        self.assertEqual((5, 1), _components(F("5")))
        self.assertEqual((3, 2), _components(F("3/2")))
        self.assertEqual((3, 2), _components(F(" \n  +3/2")))
        self.assertEqual((-3, 2), _components(F("-3/2  ")))
        self.assertEqual((13, 2), _components(F("    013/02 \n  ")))
        self.assertEqual((16, 5), _components(F(" 3.2 ")))
        self.assertEqual((-16, 5), _components(F(" -3.2 ")))
        self.assertEqual((-3, 1), _components(F(" -3. ")))
        self.assertEqual((3, 5), _components(F(" .6 ")))
        self.assertEqual((1, 3125), _components(F("32.e-5")))
        self.assertEqual((1000000, 1), _components(F("1E+06")))
        self.assertEqual((-12300, 1), _components(F("-1.23e4")))
        self.assertEqual((0, 1), _components(F(" .0e+0\t")))
        self.assertEqual((0, 1), _components(F("-0.000e0")))
180 181

        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
182
            ZeroDivisionError, "Fraction(3, 0)",
Christian Heimes's avatar
Christian Heimes committed
183
            F, "3/0")
184
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
185
            ValueError, "Invalid literal for Fraction: '3/'",
Christian Heimes's avatar
Christian Heimes committed
186
            F, "3/")
187 188 189
        self.assertRaisesMessage(
            ValueError, "Invalid literal for Fraction: '/2'",
            F, "/2")
190
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
191
            ValueError, "Invalid literal for Fraction: '3 /2'",
Christian Heimes's avatar
Christian Heimes committed
192
            F, "3 /2")
193 194
        self.assertRaisesMessage(
            # Denominators don't need a sign.
Benjamin Peterson's avatar
Benjamin Peterson committed
195
            ValueError, "Invalid literal for Fraction: '3/+2'",
Christian Heimes's avatar
Christian Heimes committed
196
            F, "3/+2")
197 198
        self.assertRaisesMessage(
            # Imitate float's parsing.
Benjamin Peterson's avatar
Benjamin Peterson committed
199
            ValueError, "Invalid literal for Fraction: '+ 3/2'",
Christian Heimes's avatar
Christian Heimes committed
200
            F, "+ 3/2")
201
        self.assertRaisesMessage(
202
            # Avoid treating '.' as a regex special character.
Benjamin Peterson's avatar
Benjamin Peterson committed
203
            ValueError, "Invalid literal for Fraction: '3a2'",
Christian Heimes's avatar
Christian Heimes committed
204
            F, "3a2")
205 206
        self.assertRaisesMessage(
            # Don't accept combinations of decimals and rationals.
Benjamin Peterson's avatar
Benjamin Peterson committed
207
            ValueError, "Invalid literal for Fraction: '3/7.2'",
Christian Heimes's avatar
Christian Heimes committed
208
            F, "3/7.2")
209 210
        self.assertRaisesMessage(
            # Don't accept combinations of decimals and rationals.
Benjamin Peterson's avatar
Benjamin Peterson committed
211
            ValueError, "Invalid literal for Fraction: '3.2/7'",
Christian Heimes's avatar
Christian Heimes committed
212
            F, "3.2/7")
Christian Heimes's avatar
Christian Heimes committed
213 214
        self.assertRaisesMessage(
            # Allow 3. and .3, but not .
Benjamin Peterson's avatar
Benjamin Peterson committed
215
            ValueError, "Invalid literal for Fraction: '.'",
Christian Heimes's avatar
Christian Heimes committed
216
            F, ".")
217 218

    def testImmutable(self):
Christian Heimes's avatar
Christian Heimes committed
219
        r = F(7, 3)
220
        r.__init__(2, 15)
221
        self.assertEqual((7, 3), _components(r))
222

223 224
        self.assertRaises(AttributeError, setattr, r, 'numerator', 12)
        self.assertRaises(AttributeError, setattr, r, 'denominator', 6)
225
        self.assertEqual((7, 3), _components(r))
226 227 228 229

        # But if you _really_ need to:
        r._numerator = 4
        r._denominator = 2
230
        self.assertEqual((4, 2), _components(r))
231
        # Which breaks some important operations:
232
        self.assertNotEqual(F(4, 2), r)
233

234
    def testFromFloat(self):
Georg Brandl's avatar
Georg Brandl committed
235
        self.assertRaises(TypeError, F.from_float, 3+4j)
236
        self.assertEqual((10, 1), _components(F.from_float(10)))
237
        bigint = 1234567890123456789
238 239 240 241 242 243 244 245 246 247
        self.assertEqual((bigint, 1), _components(F.from_float(bigint)))
        self.assertEqual((0, 1), _components(F.from_float(-0.0)))
        self.assertEqual((10, 1), _components(F.from_float(10.0)))
        self.assertEqual((-5, 2), _components(F.from_float(-2.5)))
        self.assertEqual((99999999999999991611392, 1),
                         _components(F.from_float(1e23)))
        self.assertEqual(float(10**23), float(F.from_float(1e23)))
        self.assertEqual((3602879701896397, 1125899906842624),
                         _components(F.from_float(3.2)))
        self.assertEqual(3.2, float(F.from_float(3.2)))
248 249 250 251

        inf = 1e1000
        nan = inf - inf
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
252
            TypeError, "Cannot convert inf to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
253
            F.from_float, inf)
254
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
255
            TypeError, "Cannot convert -inf to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
256
            F.from_float, -inf)
257
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
258
            TypeError, "Cannot convert nan to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
259
            F.from_float, nan)
260

261
    def testFromDecimal(self):
Georg Brandl's avatar
Georg Brandl committed
262
        self.assertRaises(TypeError, F.from_decimal, 3+4j)
263 264 265 266 267 268 269
        self.assertEqual(F(10, 1), F.from_decimal(10))
        self.assertEqual(F(0), F.from_decimal(Decimal("-0")))
        self.assertEqual(F(5, 10), F.from_decimal(Decimal("0.5")))
        self.assertEqual(F(5, 1000), F.from_decimal(Decimal("5e-3")))
        self.assertEqual(F(5000), F.from_decimal(Decimal("5e3")))
        self.assertEqual(1 - F(1, 10**30),
                         F.from_decimal(Decimal("0." + "9" * 30)))
270 271

        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
272
            TypeError, "Cannot convert Infinity to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
273
            F.from_decimal, Decimal("inf"))
274
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
275
            TypeError, "Cannot convert -Infinity to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
276
            F.from_decimal, Decimal("-inf"))
277
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
278
            TypeError, "Cannot convert NaN to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
279
            F.from_decimal, Decimal("nan"))
280
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
281
            TypeError, "Cannot convert sNaN to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
282 283 284 285 286 287 288 289 290 291 292
            F.from_decimal, Decimal("snan"))

    def testLimitDenominator(self):
        rpi = F('3.1415926535897932')
        self.assertEqual(rpi.limit_denominator(10000), F(355, 113))
        self.assertEqual(-rpi.limit_denominator(10000), F(-355, 113))
        self.assertEqual(rpi.limit_denominator(113), F(355, 113))
        self.assertEqual(rpi.limit_denominator(112), F(333, 106))
        self.assertEqual(F(201, 200).limit_denominator(100), F(1))
        self.assertEqual(F(201, 200).limit_denominator(101), F(102, 101))
        self.assertEqual(F(0).limit_denominator(10000), F(0))
293 294 295 296
        for i in (0, -1):
            self.assertRaisesMessage(
                ValueError, "max_denominator should be at least 1",
                F(1).limit_denominator, i)
297

298
    def testConversions(self):
Christian Heimes's avatar
Christian Heimes committed
299
        self.assertTypedEquals(-1, math.trunc(F(-11, 10)))
300
        self.assertTypedEquals(1, math.trunc(F(11, 10)))
Christian Heimes's avatar
Christian Heimes committed
301 302 303 304 305 306 307 308 309
        self.assertTypedEquals(-2, math.floor(F(-11, 10)))
        self.assertTypedEquals(-1, math.ceil(F(-11, 10)))
        self.assertTypedEquals(-1, math.ceil(F(-10, 10)))
        self.assertTypedEquals(-1, int(F(-11, 10)))
        self.assertTypedEquals(0, round(F(-1, 10)))
        self.assertTypedEquals(0, round(F(-5, 10)))
        self.assertTypedEquals(-2, round(F(-15, 10)))
        self.assertTypedEquals(-1, round(F(-7, 10)))

310 311
        self.assertEqual(False, bool(F(0, 1)))
        self.assertEqual(True, bool(F(3, 2)))
Christian Heimes's avatar
Christian Heimes committed
312
        self.assertTypedEquals(0.1, float(F(1, 10)))
313 314 315 316

        # Check that __float__ isn't implemented by converting the
        # numerator and denominator to float before dividing.
        self.assertRaises(OverflowError, float, int('2'*400+'7'))
317 318
        self.assertAlmostEqual(2.0/3,
                               float(F(int('2'*400+'7'), int('3'*400+'1'))))
319

Christian Heimes's avatar
Christian Heimes committed
320
        self.assertTypedEquals(0.1+0j, complex(F(1,10)))
321 322

    def testRound(self):
Christian Heimes's avatar
Christian Heimes committed
323 324 325 326 327
        self.assertTypedEquals(F(-200), round(F(-150), -2))
        self.assertTypedEquals(F(-200), round(F(-250), -2))
        self.assertTypedEquals(F(30), round(F(26), -1))
        self.assertTypedEquals(F(-2, 10), round(F(-15, 100), 1))
        self.assertTypedEquals(F(-2, 10), round(F(-25, 100), 1))
328 329 330


    def testArithmetic(self):
331 332 333 334
        self.assertEqual(F(1, 2), F(1, 10) + F(2, 5))
        self.assertEqual(F(-3, 10), F(1, 10) - F(2, 5))
        self.assertEqual(F(1, 25), F(1, 10) * F(2, 5))
        self.assertEqual(F(1, 4), F(1, 10) / F(2, 5))
Christian Heimes's avatar
Christian Heimes committed
335 336
        self.assertTypedEquals(2, F(9, 10) // F(2, 5))
        self.assertTypedEquals(10**23, F(10**23, 1) // F(1))
337 338 339
        self.assertEqual(F(2, 3), F(-7, 3) % F(3, 2))
        self.assertEqual(F(8, 27), F(2, 3) ** F(3))
        self.assertEqual(F(27, 8), F(2, 3) ** F(-3))
Christian Heimes's avatar
Christian Heimes committed
340
        self.assertTypedEquals(2.0, F(4) ** F(1, 2))
341
        self.assertEqual(F(1, 1), +F(1, 1))
Christian Heimes's avatar
Christian Heimes committed
342
        z = pow(F(-1), F(1, 2))
343 344
        self.assertAlmostEqual(z.real, 0)
        self.assertEqual(z.imag, 1)
345 346

    def testMixedArithmetic(self):
Christian Heimes's avatar
Christian Heimes committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
        self.assertTypedEquals(F(11, 10), F(1, 10) + 1)
        self.assertTypedEquals(1.1, F(1, 10) + 1.0)
        self.assertTypedEquals(1.1 + 0j, F(1, 10) + (1.0 + 0j))
        self.assertTypedEquals(F(11, 10), 1 + F(1, 10))
        self.assertTypedEquals(1.1, 1.0 + F(1, 10))
        self.assertTypedEquals(1.1 + 0j, (1.0 + 0j) + F(1, 10))

        self.assertTypedEquals(F(-9, 10), F(1, 10) - 1)
        self.assertTypedEquals(-0.9, F(1, 10) - 1.0)
        self.assertTypedEquals(-0.9 + 0j, F(1, 10) - (1.0 + 0j))
        self.assertTypedEquals(F(9, 10), 1 - F(1, 10))
        self.assertTypedEquals(0.9, 1.0 - F(1, 10))
        self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - F(1, 10))

        self.assertTypedEquals(F(1, 10), F(1, 10) * 1)
        self.assertTypedEquals(0.1, F(1, 10) * 1.0)
        self.assertTypedEquals(0.1 + 0j, F(1, 10) * (1.0 + 0j))
        self.assertTypedEquals(F(1, 10), 1 * F(1, 10))
        self.assertTypedEquals(0.1, 1.0 * F(1, 10))
        self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * F(1, 10))

        self.assertTypedEquals(F(1, 10), F(1, 10) / 1)
        self.assertTypedEquals(0.1, F(1, 10) / 1.0)
        self.assertTypedEquals(0.1 + 0j, F(1, 10) / (1.0 + 0j))
        self.assertTypedEquals(F(10, 1), 1 / F(1, 10))
        self.assertTypedEquals(10.0, 1.0 / F(1, 10))
        self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / F(1, 10))

        self.assertTypedEquals(0, F(1, 10) // 1)
        self.assertTypedEquals(0, F(1, 10) // 1.0)
        self.assertTypedEquals(10, 1 // F(1, 10))
        self.assertTypedEquals(10**23, 10**22 // F(1, 10))
        self.assertTypedEquals(10, 1.0 // F(1, 10))

        self.assertTypedEquals(F(1, 10), F(1, 10) % 1)
        self.assertTypedEquals(0.1, F(1, 10) % 1.0)
        self.assertTypedEquals(F(0, 1), 1 % F(1, 10))
        self.assertTypedEquals(0.0, 1.0 % F(1, 10))
385 386 387 388

        # No need for divmod since we don't override it.

        # ** has more interesting conversion rules.
Christian Heimes's avatar
Christian Heimes committed
389 390 391 392 393 394
        self.assertTypedEquals(F(100, 1), F(1, 10) ** -2)
        self.assertTypedEquals(F(100, 1), F(10, 1) ** 2)
        self.assertTypedEquals(0.1, F(1, 10) ** 1.0)
        self.assertTypedEquals(0.1 + 0j, F(1, 10) ** (1.0 + 0j))
        self.assertTypedEquals(4 , 2 ** F(2, 1))
        z = pow(-1, F(1, 2))
395 396
        self.assertAlmostEqual(0, z.real)
        self.assertEqual(1, z.imag)
Christian Heimes's avatar
Christian Heimes committed
397 398 399 400
        self.assertTypedEquals(F(1, 4) , 2 ** F(-2, 1))
        self.assertTypedEquals(2.0 , 4 ** F(1, 2))
        self.assertTypedEquals(0.25, 2.0 ** F(-2, 1))
        self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** F(1, 10))
401 402

    def testMixingWithDecimal(self):
403
        # Decimal refuses mixed arithmetic (but not mixed comparisons)
404 405 406 407
        self.assertRaises(TypeError, operator.add,
                          F(3,11), Decimal('3.1415926'))
        self.assertRaises(TypeError, operator.add,
                          Decimal('3.1415926'), F(3,11))
408 409

    def testComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
410 411 412 413 414 415 416 417 418
        self.assertTrue(F(1, 2) < F(2, 3))
        self.assertFalse(F(1, 2) < F(1, 2))
        self.assertTrue(F(1, 2) <= F(2, 3))
        self.assertTrue(F(1, 2) <= F(1, 2))
        self.assertFalse(F(2, 3) <= F(1, 2))
        self.assertTrue(F(1, 2) == F(1, 2))
        self.assertFalse(F(1, 2) == F(1, 3))
        self.assertFalse(F(1, 2) != F(1, 2))
        self.assertTrue(F(1, 2) != F(1, 3))
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
    def testComparisonsDummyRational(self):
        self.assertTrue(F(1, 2) == DummyRational(1, 2))
        self.assertTrue(DummyRational(1, 2) == F(1, 2))
        self.assertFalse(F(1, 2) == DummyRational(3, 4))
        self.assertFalse(DummyRational(3, 4) == F(1, 2))

        self.assertTrue(F(1, 2) < DummyRational(3, 4))
        self.assertFalse(F(1, 2) < DummyRational(1, 2))
        self.assertFalse(F(1, 2) < DummyRational(1, 7))
        self.assertFalse(F(1, 2) > DummyRational(3, 4))
        self.assertFalse(F(1, 2) > DummyRational(1, 2))
        self.assertTrue(F(1, 2) > DummyRational(1, 7))
        self.assertTrue(F(1, 2) <= DummyRational(3, 4))
        self.assertTrue(F(1, 2) <= DummyRational(1, 2))
        self.assertFalse(F(1, 2) <= DummyRational(1, 7))
        self.assertFalse(F(1, 2) >= DummyRational(3, 4))
        self.assertTrue(F(1, 2) >= DummyRational(1, 2))
        self.assertTrue(F(1, 2) >= DummyRational(1, 7))

        self.assertTrue(DummyRational(1, 2) < F(3, 4))
        self.assertFalse(DummyRational(1, 2) < F(1, 2))
        self.assertFalse(DummyRational(1, 2) < F(1, 7))
        self.assertFalse(DummyRational(1, 2) > F(3, 4))
        self.assertFalse(DummyRational(1, 2) > F(1, 2))
        self.assertTrue(DummyRational(1, 2) > F(1, 7))
        self.assertTrue(DummyRational(1, 2) <= F(3, 4))
        self.assertTrue(DummyRational(1, 2) <= F(1, 2))
        self.assertFalse(DummyRational(1, 2) <= F(1, 7))
        self.assertFalse(DummyRational(1, 2) >= F(3, 4))
        self.assertTrue(DummyRational(1, 2) >= F(1, 2))
        self.assertTrue(DummyRational(1, 2) >= F(1, 7))

    def testComparisonsDummyFloat(self):
        x = DummyFloat(1./3.)
        y = F(1, 3)
        self.assertTrue(x != y)
        self.assertTrue(x < y or x > y)
        self.assertFalse(x == y)
        self.assertFalse(x <= y and x >= y)
        self.assertTrue(y != x)
        self.assertTrue(y < x or y > x)
        self.assertFalse(y == x)
        self.assertFalse(y <= x and y >= x)

464
    def testMixedLess(self):
Christian Heimes's avatar
Christian Heimes committed
465 466 467 468
        self.assertTrue(2 < F(5, 2))
        self.assertFalse(2 < F(4, 2))
        self.assertTrue(F(5, 2) < 3)
        self.assertFalse(F(4, 2) < 2)
469

Christian Heimes's avatar
Christian Heimes committed
470 471 472 473
        self.assertTrue(F(1, 2) < 0.6)
        self.assertFalse(F(1, 2) < 0.4)
        self.assertTrue(0.4 < F(1, 2))
        self.assertFalse(0.5 < F(1, 2))
474

475 476 477 478 479 480 481
        self.assertFalse(float('inf') < F(1, 2))
        self.assertTrue(float('-inf') < F(0, 10))
        self.assertFalse(float('nan') < F(-3, 7))
        self.assertTrue(F(1, 2) < float('inf'))
        self.assertFalse(F(17, 12) < float('-inf'))
        self.assertFalse(F(144, -89) < float('nan'))

482
    def testMixedLessEqual(self):
Christian Heimes's avatar
Christian Heimes committed
483 484 485 486 487 488 489 490
        self.assertTrue(0.5 <= F(1, 2))
        self.assertFalse(0.6 <= F(1, 2))
        self.assertTrue(F(1, 2) <= 0.5)
        self.assertFalse(F(1, 2) <= 0.4)
        self.assertTrue(2 <= F(4, 2))
        self.assertFalse(2 <= F(3, 2))
        self.assertTrue(F(4, 2) <= 2)
        self.assertFalse(F(5, 2) <= 2)
491

492 493 494 495 496 497 498
        self.assertFalse(float('inf') <= F(1, 2))
        self.assertTrue(float('-inf') <= F(0, 10))
        self.assertFalse(float('nan') <= F(-3, 7))
        self.assertTrue(F(1, 2) <= float('inf'))
        self.assertFalse(F(17, 12) <= float('-inf'))
        self.assertFalse(F(144, -89) <= float('nan'))

499 500
    def testBigFloatComparisons(self):
        # Because 10**23 can't be represented exactly as a float:
Christian Heimes's avatar
Christian Heimes committed
501
        self.assertFalse(F(10**23) == float(10**23))
502
        # The first test demonstrates why these are important.
Christian Heimes's avatar
Christian Heimes committed
503 504 505 506 507
        self.assertFalse(1e23 < float(F(math.trunc(1e23) + 1)))
        self.assertTrue(1e23 < F(math.trunc(1e23) + 1))
        self.assertFalse(1e23 <= F(math.trunc(1e23) - 1))
        self.assertTrue(1e23 > F(math.trunc(1e23) - 1))
        self.assertFalse(1e23 >= F(math.trunc(1e23) + 1))
508 509

    def testBigComplexComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
510
        self.assertFalse(F(10**23) == complex(10**23))
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
        self.assertRaises(TypeError, operator.gt, F(10**23), complex(10**23))
        self.assertRaises(TypeError, operator.le, F(10**23), complex(10**23))

        x = F(3, 8)
        z = complex(0.375, 0.0)
        w = complex(0.375, 0.2)
        self.assertTrue(x == z)
        self.assertFalse(x != z)
        self.assertFalse(x == w)
        self.assertTrue(x != w)
        for op in operator.lt, operator.le, operator.gt, operator.ge:
            self.assertRaises(TypeError, op, x, z)
            self.assertRaises(TypeError, op, z, x)
            self.assertRaises(TypeError, op, x, w)
            self.assertRaises(TypeError, op, w, x)
526 527

    def testMixedEqual(self):
Christian Heimes's avatar
Christian Heimes committed
528 529 530 531 532 533 534 535
        self.assertTrue(0.5 == F(1, 2))
        self.assertFalse(0.6 == F(1, 2))
        self.assertTrue(F(1, 2) == 0.5)
        self.assertFalse(F(1, 2) == 0.4)
        self.assertTrue(2 == F(4, 2))
        self.assertFalse(2 == F(3, 2))
        self.assertTrue(F(4, 2) == 2)
        self.assertFalse(F(5, 2) == 2)
536 537 538 539
        self.assertFalse(F(5, 2) == float('nan'))
        self.assertFalse(float('nan') == F(3, 7))
        self.assertFalse(F(5, 2) == float('inf'))
        self.assertFalse(float('-inf') == F(2, 5))
540 541

    def testStringification(self):
542 543 544 545 546 547 548
        self.assertEqual("Fraction(7, 3)", repr(F(7, 3)))
        self.assertEqual("Fraction(6283185307, 2000000000)",
                         repr(F('3.1415926535')))
        self.assertEqual("Fraction(-1, 100000000000000000000)",
                         repr(F(1, -10**20)))
        self.assertEqual("7/3", str(F(7, 3)))
        self.assertEqual("7", str(F(7, 1)))
549 550

    def testHash(self):
551 552
        hmod = sys.hash_info.modulus
        hinf = sys.hash_info.inf
553 554 555
        self.assertEqual(hash(2.5), hash(F(5, 2)))
        self.assertEqual(hash(10**50), hash(F(10**50)))
        self.assertNotEqual(hash(float(10**23)), hash(F(10**23)))
556
        self.assertEqual(hinf, hash(F(1, hmod)))
557 558
        # Check that __hash__ produces the same value as hash(), for
        # consistency with int and Decimal.  (See issue #10356.)
559
        self.assertEqual(hash(F(-1)), F(-1).__hash__())
560 561 562 563

    def testApproximatePi(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
564
        three = F(3)
565
        lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
Christian Heimes's avatar
Christian Heimes committed
566
        while abs(s - lasts) > F(1, 10**9):
567 568 569 570 571
            lasts = s
            n, na = n+na, na+8
            d, da = d+da, da+32
            t = (t * n) / d
            s += t
572
        self.assertAlmostEqual(math.pi, s)
573 574 575 576

    def testApproximateCos1(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
577 578 579
        x = F(1)
        i, lasts, s, fact, num, sign = 0, 0, F(1), 1, 1, 1
        while abs(s - lasts) > F(1, 10**9):
580 581 582 583 584 585
            lasts = s
            i += 2
            fact *= i * (i-1)
            num *= x * x
            sign *= -1
            s += num / fact * sign
586
        self.assertAlmostEqual(math.cos(1), s)
587

588
    def test_copy_deepcopy_pickle(self):
Christian Heimes's avatar
Christian Heimes committed
589
        r = F(13, 7)
590
        dr = DummyFraction(13, 7)
591 592 593
        self.assertEqual(r, loads(dumps(r)))
        self.assertEqual(id(r), id(copy(r)))
        self.assertEqual(id(r), id(deepcopy(r)))
594 595 596 597
        self.assertNotEqual(id(dr), id(copy(dr)))
        self.assertNotEqual(id(dr), id(deepcopy(dr)))
        self.assertTypedEquals(dr, copy(dr))
        self.assertTypedEquals(dr, deepcopy(dr))
598

599 600 601 602 603
    def test_slots(self):
        # Issue 4998
        r = F(13, 7)
        self.assertRaises(AttributeError, setattr, r, 'a', 10)

604
def test_main():
Christian Heimes's avatar
Christian Heimes committed
605
    run_unittest(FractionTest, GcdTest)
606 607 608

if __name__ == '__main__':
    test_main()