test_fractions.py 23.7 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 unittest
10
from copy import copy, deepcopy
Neal Norwitz's avatar
Neal Norwitz committed
11
from pickle import dumps, loads
Christian Heimes's avatar
Christian Heimes committed
12
F = fractions.Fraction
Christian Heimes's avatar
Christian Heimes committed
13
gcd = fractions.gcd
14

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
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"
78 79 80 81

class GcdTest(unittest.TestCase):

    def testMisc(self):
82 83 84 85 86 87 88 89 90 91
        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))
92

93 94 95 96

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

97

Christian Heimes's avatar
Christian Heimes committed
98
class FractionTest(unittest.TestCase):
99 100 101

    def assertTypedEquals(self, expected, actual):
        """Asserts that both the types and values are the same."""
102 103
        self.assertEqual(type(expected), type(actual))
        self.assertEqual(expected, actual)
104 105 106 107 108 109 110

    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:
111
            self.assertEqual(message, str(e))
112 113 114 115
        else:
            self.fail("%s not raised" % exc_type.__name__)

    def testInit(self):
116 117 118
        self.assertEqual((0, 1), _components(F()))
        self.assertEqual((7, 1), _components(F(7)))
        self.assertEqual((7, 3), _components(F(F(7, 3))))
119

120 121 122 123 124 125
        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)))
126

127 128 129
        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))))
130

Christian Heimes's avatar
Christian Heimes committed
131
        self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)",
Christian Heimes's avatar
Christian Heimes committed
132
                                 F, 12, 0)
Georg Brandl's avatar
Georg Brandl committed
133
        self.assertRaises(TypeError, F, 1.5 + 3j)
134

Georg Brandl's avatar
Georg Brandl committed
135
        self.assertRaises(TypeError, F, "3/2", 3)
136 137 138
        self.assertRaises(TypeError, F, 3, 0j)
        self.assertRaises(TypeError, F, 3, 1j)

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

    def testInitFromDecimal(self):
150 151 152 153 154 155
        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'))))
156 157 158 159
        self.assertRaises(TypeError, F, Decimal('nan'))
        self.assertRaises(TypeError, F, Decimal('snan'))
        self.assertRaises(TypeError, F, Decimal('inf'))
        self.assertRaises(TypeError, F, Decimal('-inf'))
160 161

    def testFromString(self):
162 163 164 165 166 167 168 169 170 171 172 173 174 175
        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")))
176 177

        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
178
            ZeroDivisionError, "Fraction(3, 0)",
Christian Heimes's avatar
Christian Heimes committed
179
            F, "3/0")
180
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
181
            ValueError, "Invalid literal for Fraction: '3/'",
Christian Heimes's avatar
Christian Heimes committed
182
            F, "3/")
183 184 185
        self.assertRaisesMessage(
            ValueError, "Invalid literal for Fraction: '/2'",
            F, "/2")
186
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
187
            ValueError, "Invalid literal for Fraction: '3 /2'",
Christian Heimes's avatar
Christian Heimes committed
188
            F, "3 /2")
189 190
        self.assertRaisesMessage(
            # Denominators don't need a sign.
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(
            # Imitate float's parsing.
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
        self.assertRaisesMessage(
198
            # Avoid treating '.' as a regex special character.
Benjamin Peterson's avatar
Benjamin Peterson committed
199
            ValueError, "Invalid literal for Fraction: '3a2'",
Christian Heimes's avatar
Christian Heimes committed
200
            F, "3a2")
201 202
        self.assertRaisesMessage(
            # Don't accept combinations of decimals and rationals.
Benjamin Peterson's avatar
Benjamin Peterson committed
203
            ValueError, "Invalid literal for Fraction: '3/7.2'",
Christian Heimes's avatar
Christian Heimes committed
204
            F, "3/7.2")
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.2/7'",
Christian Heimes's avatar
Christian Heimes committed
208
            F, "3.2/7")
Christian Heimes's avatar
Christian Heimes committed
209 210
        self.assertRaisesMessage(
            # Allow 3. and .3, but not .
Benjamin Peterson's avatar
Benjamin Peterson committed
211
            ValueError, "Invalid literal for Fraction: '.'",
Christian Heimes's avatar
Christian Heimes committed
212
            F, ".")
213 214

    def testImmutable(self):
Christian Heimes's avatar
Christian Heimes committed
215
        r = F(7, 3)
216
        r.__init__(2, 15)
217
        self.assertEqual((7, 3), _components(r))
218

219 220
        self.assertRaises(AttributeError, setattr, r, 'numerator', 12)
        self.assertRaises(AttributeError, setattr, r, 'denominator', 6)
221
        self.assertEqual((7, 3), _components(r))
222 223 224 225

        # But if you _really_ need to:
        r._numerator = 4
        r._denominator = 2
226
        self.assertEqual((4, 2), _components(r))
227
        # Which breaks some important operations:
228
        self.assertNotEqual(F(4, 2), r)
229

230
    def testFromFloat(self):
Georg Brandl's avatar
Georg Brandl committed
231
        self.assertRaises(TypeError, F.from_float, 3+4j)
232
        self.assertEqual((10, 1), _components(F.from_float(10)))
233
        bigint = 1234567890123456789
234 235 236 237 238 239 240 241 242 243
        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)))
244 245 246 247

        inf = 1e1000
        nan = inf - inf
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
248
            TypeError, "Cannot convert inf to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
249
            F.from_float, inf)
250
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
251
            TypeError, "Cannot convert -inf to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
252
            F.from_float, -inf)
253
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
254
            TypeError, "Cannot convert nan to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
255
            F.from_float, nan)
256

257
    def testFromDecimal(self):
Georg Brandl's avatar
Georg Brandl committed
258
        self.assertRaises(TypeError, F.from_decimal, 3+4j)
259 260 261 262 263 264 265
        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)))
266 267

        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
268
            TypeError, "Cannot convert Infinity to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
269
            F.from_decimal, Decimal("inf"))
270
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
271
            TypeError, "Cannot convert -Infinity to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
272
            F.from_decimal, Decimal("-inf"))
273
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
274
            TypeError, "Cannot convert NaN to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
275
            F.from_decimal, Decimal("nan"))
276
        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
277
            TypeError, "Cannot convert sNaN to Fraction.",
Christian Heimes's avatar
Christian Heimes committed
278 279 280 281 282 283 284 285 286 287 288
            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))
289

290
    def testConversions(self):
Christian Heimes's avatar
Christian Heimes committed
291 292 293 294 295 296 297 298 299 300
        self.assertTypedEquals(-1, math.trunc(F(-11, 10)))
        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)))

301 302
        self.assertEqual(False, bool(F(0, 1)))
        self.assertEqual(True, bool(F(3, 2)))
Christian Heimes's avatar
Christian Heimes committed
303
        self.assertTypedEquals(0.1, float(F(1, 10)))
304 305 306 307

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

Christian Heimes's avatar
Christian Heimes committed
311
        self.assertTypedEquals(0.1+0j, complex(F(1,10)))
312 313

    def testRound(self):
Christian Heimes's avatar
Christian Heimes committed
314 315 316 317 318
        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))
319 320 321


    def testArithmetic(self):
322 323 324 325
        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
326 327
        self.assertTypedEquals(2, F(9, 10) // F(2, 5))
        self.assertTypedEquals(10**23, F(10**23, 1) // F(1))
328 329 330
        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
331 332
        self.assertTypedEquals(2.0, F(4) ** F(1, 2))
        z = pow(F(-1), F(1, 2))
333 334
        self.assertAlmostEqual(z.real, 0)
        self.assertEqual(z.imag, 1)
335 336

    def testMixedArithmetic(self):
Christian Heimes's avatar
Christian Heimes committed
337 338 339 340 341 342 343 344 345 346 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
        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))
375 376 377 378

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

        # ** has more interesting conversion rules.
Christian Heimes's avatar
Christian Heimes committed
379 380 381 382 383 384
        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))
385 386
        self.assertAlmostEqual(0, z.real)
        self.assertEqual(1, z.imag)
Christian Heimes's avatar
Christian Heimes committed
387 388 389 390
        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))
391 392

    def testMixingWithDecimal(self):
393
        # Decimal refuses mixed arithmetic (but not mixed comparisons)
394 395
        self.assertRaisesMessage(
            TypeError,
Christian Heimes's avatar
Christian Heimes committed
396
            "unsupported operand type(s) for +: 'Fraction' and 'Decimal'",
Christian Heimes's avatar
Christian Heimes committed
397
            operator.add, F(3,11), Decimal('3.1415926'))
398 399

    def testComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
400 401 402 403 404 405 406 407 408
        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))
409

410 411 412 413 414 415 416 417 418 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
    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)

454
    def testMixedLess(self):
Christian Heimes's avatar
Christian Heimes committed
455 456 457 458
        self.assertTrue(2 < F(5, 2))
        self.assertFalse(2 < F(4, 2))
        self.assertTrue(F(5, 2) < 3)
        self.assertFalse(F(4, 2) < 2)
459

Christian Heimes's avatar
Christian Heimes committed
460 461 462 463
        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))
464

465 466 467 468 469 470 471
        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'))

472
    def testMixedLessEqual(self):
Christian Heimes's avatar
Christian Heimes committed
473 474 475 476 477 478 479 480
        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)
481

482 483 484 485 486 487 488
        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'))

489 490
    def testBigFloatComparisons(self):
        # Because 10**23 can't be represented exactly as a float:
Christian Heimes's avatar
Christian Heimes committed
491
        self.assertFalse(F(10**23) == float(10**23))
492
        # The first test demonstrates why these are important.
Christian Heimes's avatar
Christian Heimes committed
493 494 495 496 497
        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))
498 499

    def testBigComplexComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
500
        self.assertFalse(F(10**23) == complex(10**23))
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
        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)
516 517

    def testMixedEqual(self):
Christian Heimes's avatar
Christian Heimes committed
518 519 520 521 522 523 524 525
        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)
526 527 528 529
        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))
530 531

    def testStringification(self):
532 533 534 535 536 537 538
        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)))
539 540

    def testHash(self):
541 542 543
        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)))
544 545
        # Check that __hash__ produces the same value as hash(), for
        # consistency with int and Decimal.  (See issue #10356.)
546
        self.assertEqual(hash(F(-1)), F(-1).__hash__())
547 548 549 550

    def testApproximatePi(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
551
        three = F(3)
552
        lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
Christian Heimes's avatar
Christian Heimes committed
553
        while abs(s - lasts) > F(1, 10**9):
554 555 556 557 558
            lasts = s
            n, na = n+na, na+8
            d, da = d+da, da+32
            t = (t * n) / d
            s += t
559
        self.assertAlmostEqual(math.pi, s)
560 561 562 563

    def testApproximateCos1(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
564 565 566
        x = F(1)
        i, lasts, s, fact, num, sign = 0, 0, F(1), 1, 1, 1
        while abs(s - lasts) > F(1, 10**9):
567 568 569 570 571 572
            lasts = s
            i += 2
            fact *= i * (i-1)
            num *= x * x
            sign *= -1
            s += num / fact * sign
573
        self.assertAlmostEqual(math.cos(1), s)
574

575
    def test_copy_deepcopy_pickle(self):
Christian Heimes's avatar
Christian Heimes committed
576
        r = F(13, 7)
577 578 579 580
        self.assertEqual(r, loads(dumps(r)))
        self.assertEqual(id(r), id(copy(r)))
        self.assertEqual(id(r), id(deepcopy(r)))

581 582 583 584 585
    def test_slots(self):
        # Issue 4998
        r = F(13, 7)
        self.assertRaises(AttributeError, setattr, r, 'a', 10)

586
def test_main():
Christian Heimes's avatar
Christian Heimes committed
587
    run_unittest(FractionTest, GcdTest)
588 589 590

if __name__ == '__main__':
    test_main()