test_fractions.py 28.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 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
import warnings
12
from copy import copy, deepcopy
Neal Norwitz's avatar
Neal Norwitz committed
13
from pickle import dumps, loads
Christian Heimes's avatar
Christian Heimes committed
14
F = fractions.Fraction
Christian Heimes's avatar
Christian Heimes committed
15
gcd = fractions.gcd
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
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):
53
        g = math.gcd(num, den)
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 79
        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"
80

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

84 85 86
class GcdTest(unittest.TestCase):

    def testMisc(self):
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
        # fractions.gcd() is deprecated
        with self.assertWarnsRegex(DeprecationWarning, r'fractions\.gcd'):
            gcd(1, 1)
        with warnings.catch_warnings():
            warnings.filterwarnings('ignore', r'fractions\.gcd',
                                    DeprecationWarning)
            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))
            self.assertEqual(gcd(120.0, 84), 12.0)
            self.assertEqual(gcd(120, 84.0), 12.0)
            self.assertEqual(gcd(F(120), F(84)), F(12))
            self.assertEqual(gcd(F(120, 77), F(84, 55)), F(12, 385))
107

108 109 110 111

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

112

Christian Heimes's avatar
Christian Heimes committed
113
class FractionTest(unittest.TestCase):
114 115 116

    def assertTypedEquals(self, expected, actual):
        """Asserts that both the types and values are the same."""
117 118
        self.assertEqual(type(expected), type(actual))
        self.assertEqual(expected, actual)
119

120 121 122 123 124
    def assertTypedTupleEquals(self, expected, actual):
        """Asserts that both the types and values in the tuples are the same."""
        self.assertTupleEqual(expected, actual)
        self.assertListEqual(list(map(type, expected)), list(map(type, actual)))

125 126 127 128 129 130
    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:
131
            self.assertEqual(message, str(e))
132 133 134 135
        else:
            self.fail("%s not raised" % exc_type.__name__)

    def testInit(self):
136 137 138
        self.assertEqual((0, 1), _components(F()))
        self.assertEqual((7, 1), _components(F(7)))
        self.assertEqual((7, 3), _components(F(F(7, 3))))
139

140 141 142 143 144 145
        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)))
146

147 148 149
        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))))
150

Christian Heimes's avatar
Christian Heimes committed
151
        self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)",
Christian Heimes's avatar
Christian Heimes committed
152
                                 F, 12, 0)
Georg Brandl's avatar
Georg Brandl committed
153
        self.assertRaises(TypeError, F, 1.5 + 3j)
154

Georg Brandl's avatar
Georg Brandl committed
155
        self.assertRaises(TypeError, F, "3/2", 3)
156 157
        self.assertRaises(TypeError, F, 3, 0j)
        self.assertRaises(TypeError, F, 3, 1j)
158
        self.assertRaises(TypeError, F, 1, 2, 3)
159

160 161
    @requires_IEEE_754
    def testInitFromFloat(self):
162 163 164 165
        self.assertEqual((5, 2), _components(F(2.5)))
        self.assertEqual((0, 1), _components(F(-0.0)))
        self.assertEqual((3602879701896397, 36028797018963968),
                         _components(F(0.1)))
166 167 168 169
        # bug 16469: error types should be consistent with float -> int
        self.assertRaises(ValueError, F, float('nan'))
        self.assertRaises(OverflowError, F, float('inf'))
        self.assertRaises(OverflowError, F, float('-inf'))
170 171

    def testInitFromDecimal(self):
172 173 174 175 176 177
        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'))))
178 179 180 181 182
        # bug 16469: error types should be consistent with decimal -> int
        self.assertRaises(ValueError, F, Decimal('nan'))
        self.assertRaises(ValueError, F, Decimal('snan'))
        self.assertRaises(OverflowError, F, Decimal('inf'))
        self.assertRaises(OverflowError, F, Decimal('-inf'))
183 184

    def testFromString(self):
185 186 187 188 189 190 191 192 193 194 195 196 197 198
        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")))
199 200

        self.assertRaisesMessage(
Christian Heimes's avatar
Christian Heimes committed
201
            ZeroDivisionError, "Fraction(3, 0)",
Christian Heimes's avatar
Christian Heimes committed
202
            F, "3/0")
203
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
204
            ValueError, "Invalid literal for Fraction: '3/'",
Christian Heimes's avatar
Christian Heimes committed
205
            F, "3/")
206 207 208
        self.assertRaisesMessage(
            ValueError, "Invalid literal for Fraction: '/2'",
            F, "/2")
209
        self.assertRaisesMessage(
Benjamin Peterson's avatar
Benjamin Peterson committed
210
            ValueError, "Invalid literal for Fraction: '3 /2'",
Christian Heimes's avatar
Christian Heimes committed
211
            F, "3 /2")
212 213
        self.assertRaisesMessage(
            # Denominators don't need a sign.
Benjamin Peterson's avatar
Benjamin Peterson committed
214
            ValueError, "Invalid literal for Fraction: '3/+2'",
Christian Heimes's avatar
Christian Heimes committed
215
            F, "3/+2")
216 217
        self.assertRaisesMessage(
            # Imitate float's parsing.
Benjamin Peterson's avatar
Benjamin Peterson committed
218
            ValueError, "Invalid literal for Fraction: '+ 3/2'",
Christian Heimes's avatar
Christian Heimes committed
219
            F, "+ 3/2")
220
        self.assertRaisesMessage(
221
            # Avoid treating '.' as a regex special character.
Benjamin Peterson's avatar
Benjamin Peterson committed
222
            ValueError, "Invalid literal for Fraction: '3a2'",
Christian Heimes's avatar
Christian Heimes committed
223
            F, "3a2")
224 225
        self.assertRaisesMessage(
            # Don't accept combinations of decimals and rationals.
Benjamin Peterson's avatar
Benjamin Peterson committed
226
            ValueError, "Invalid literal for Fraction: '3/7.2'",
Christian Heimes's avatar
Christian Heimes committed
227
            F, "3/7.2")
228 229
        self.assertRaisesMessage(
            # Don't accept combinations of decimals and rationals.
Benjamin Peterson's avatar
Benjamin Peterson committed
230
            ValueError, "Invalid literal for Fraction: '3.2/7'",
Christian Heimes's avatar
Christian Heimes committed
231
            F, "3.2/7")
Christian Heimes's avatar
Christian Heimes committed
232 233
        self.assertRaisesMessage(
            # Allow 3. and .3, but not .
Benjamin Peterson's avatar
Benjamin Peterson committed
234
            ValueError, "Invalid literal for Fraction: '.'",
Christian Heimes's avatar
Christian Heimes committed
235
            F, ".")
236 237

    def testImmutable(self):
Christian Heimes's avatar
Christian Heimes committed
238
        r = F(7, 3)
239
        r.__init__(2, 15)
240
        self.assertEqual((7, 3), _components(r))
241

242 243
        self.assertRaises(AttributeError, setattr, r, 'numerator', 12)
        self.assertRaises(AttributeError, setattr, r, 'denominator', 6)
244
        self.assertEqual((7, 3), _components(r))
245 246 247 248

        # But if you _really_ need to:
        r._numerator = 4
        r._denominator = 2
249
        self.assertEqual((4, 2), _components(r))
250
        # Which breaks some important operations:
251
        self.assertNotEqual(F(4, 2), r)
252

253
    def testFromFloat(self):
Georg Brandl's avatar
Georg Brandl committed
254
        self.assertRaises(TypeError, F.from_float, 3+4j)
255
        self.assertEqual((10, 1), _components(F.from_float(10)))
256
        bigint = 1234567890123456789
257 258 259 260 261 262 263 264 265 266
        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)))
267 268 269

        inf = 1e1000
        nan = inf - inf
270
        # bug 16469: error types should be consistent with float -> int
271
        self.assertRaisesMessage(
272
            OverflowError, "cannot convert Infinity to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
273
            F.from_float, inf)
274
        self.assertRaisesMessage(
275
            OverflowError, "cannot convert Infinity to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
276
            F.from_float, -inf)
277
        self.assertRaisesMessage(
278
            ValueError, "cannot convert NaN to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
279
            F.from_float, nan)
280

281
    def testFromDecimal(self):
Georg Brandl's avatar
Georg Brandl committed
282
        self.assertRaises(TypeError, F.from_decimal, 3+4j)
283 284 285 286 287 288 289
        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)))
290

291
        # bug 16469: error types should be consistent with decimal -> int
292
        self.assertRaisesMessage(
293
            OverflowError, "cannot convert Infinity to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
294
            F.from_decimal, Decimal("inf"))
295
        self.assertRaisesMessage(
296
            OverflowError, "cannot convert Infinity to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
297
            F.from_decimal, Decimal("-inf"))
298
        self.assertRaisesMessage(
299
            ValueError, "cannot convert NaN to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
300
            F.from_decimal, Decimal("nan"))
301
        self.assertRaisesMessage(
302
            ValueError, "cannot convert NaN to integer ratio",
Christian Heimes's avatar
Christian Heimes committed
303 304 305 306 307 308 309 310 311 312 313
            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))
314 315 316 317
        for i in (0, -1):
            self.assertRaisesMessage(
                ValueError, "max_denominator should be at least 1",
                F(1).limit_denominator, i)
318

319
    def testConversions(self):
Christian Heimes's avatar
Christian Heimes committed
320
        self.assertTypedEquals(-1, math.trunc(F(-11, 10)))
321
        self.assertTypedEquals(1, math.trunc(F(11, 10)))
Christian Heimes's avatar
Christian Heimes committed
322 323 324 325 326 327 328 329 330
        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)))

331 332
        self.assertEqual(False, bool(F(0, 1)))
        self.assertEqual(True, bool(F(3, 2)))
Christian Heimes's avatar
Christian Heimes committed
333
        self.assertTypedEquals(0.1, float(F(1, 10)))
334 335 336 337

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

Christian Heimes's avatar
Christian Heimes committed
341
        self.assertTypedEquals(0.1+0j, complex(F(1,10)))
342 343

    def testRound(self):
Christian Heimes's avatar
Christian Heimes committed
344 345 346 347 348
        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))
349 350

    def testArithmetic(self):
351 352 353 354
        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
355 356
        self.assertTypedEquals(2, F(9, 10) // F(2, 5))
        self.assertTypedEquals(10**23, F(10**23, 1) // F(1))
357
        self.assertEqual(F(5, 6), F(7, 3) % F(3, 2))
358
        self.assertEqual(F(2, 3), F(-7, 3) % F(3, 2))
359 360
        self.assertEqual((F(1), F(5, 6)), divmod(F(7, 3), F(3, 2)))
        self.assertEqual((F(-2), F(2, 3)), divmod(F(-7, 3), F(3, 2)))
361 362
        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
363
        self.assertTypedEquals(2.0, F(4) ** F(1, 2))
364
        self.assertEqual(F(1, 1), +F(1, 1))
Christian Heimes's avatar
Christian Heimes committed
365
        z = pow(F(-1), F(1, 2))
366 367
        self.assertAlmostEqual(z.real, 0)
        self.assertEqual(z.imag, 1)
368 369 370 371 372 373 374 375 376 377 378 379 380
        # Regression test for #27539.
        p = F(-1, 2) ** 0
        self.assertEqual(p, F(1, 1))
        self.assertEqual(p.numerator, 1)
        self.assertEqual(p.denominator, 1)
        p = F(-1, 2) ** -1
        self.assertEqual(p, F(-2, 1))
        self.assertEqual(p.numerator, -2)
        self.assertEqual(p.denominator, 1)
        p = F(-1, 2) ** -2
        self.assertEqual(p, F(4, 1))
        self.assertEqual(p.numerator, 4)
        self.assertEqual(p.denominator, 1)
381

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    def testLargeArithmetic(self):
        self.assertTypedEquals(
            F(10101010100808080808080808101010101010000000000000000,
              1010101010101010101010101011111111101010101010101010101010101),
            F(10**35+1, 10**27+1) % F(10**27+1, 10**35-1)
        )
        self.assertTypedEquals(
            F(7, 1901475900342344102245054808064),
            F(-2**100, 3) % F(5, 2**100)
        )
        self.assertTypedTupleEquals(
            (9999999999999999,
             F(10101010100808080808080808101010101010000000000000000,
               1010101010101010101010101011111111101010101010101010101010101)),
            divmod(F(10**35+1, 10**27+1), F(10**27+1, 10**35-1))
        )
        self.assertTypedEquals(
            -2 ** 200 // 15,
            F(-2**100, 3) // F(5, 2**100)
        )
        self.assertTypedEquals(
            1,
            F(5, 2**100) // F(3, 2**100)
        )
        self.assertTypedEquals(
            (1, F(2, 2**100)),
            divmod(F(5, 2**100), F(3, 2**100))
        )
        self.assertTypedTupleEquals(
            (-2 ** 200 // 15,
             F(7, 1901475900342344102245054808064)),
            divmod(F(-2**100, 3), F(5, 2**100))
        )

416
    def testMixedArithmetic(self):
Christian Heimes's avatar
Christian Heimes committed
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
        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)
446
        self.assertTypedEquals(0.0, F(1, 10) // 1.0)
Christian Heimes's avatar
Christian Heimes committed
447 448
        self.assertTypedEquals(10, 1 // F(1, 10))
        self.assertTypedEquals(10**23, 10**22 // F(1, 10))
449
        self.assertTypedEquals(1.0 // 0.1, 1.0 // F(1, 10))
Christian Heimes's avatar
Christian Heimes committed
450 451 452 453

        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))
454 455 456 457 458
        self.assertTypedEquals(1.0 % 0.1, 1.0 % F(1, 10))
        self.assertTypedEquals(0.1, F(1, 10) % float('inf'))
        self.assertTypedEquals(float('-inf'), F(1, 10) % float('-inf'))
        self.assertTypedEquals(float('inf'), F(-1, 10) % float('inf'))
        self.assertTypedEquals(-0.1, F(-1, 10) % float('-inf'))
459

460 461 462 463 464 465 466 467
        self.assertTypedTupleEquals((0, F(1, 10)), divmod(F(1, 10), 1))
        self.assertTypedTupleEquals(divmod(0.1, 1.0), divmod(F(1, 10), 1.0))
        self.assertTypedTupleEquals((10, F(0)), divmod(1, F(1, 10)))
        self.assertTypedTupleEquals(divmod(1.0, 0.1), divmod(1.0, F(1, 10)))
        self.assertTypedTupleEquals(divmod(0.1, float('inf')), divmod(F(1, 10), float('inf')))
        self.assertTypedTupleEquals(divmod(0.1, float('-inf')), divmod(F(1, 10), float('-inf')))
        self.assertTypedTupleEquals(divmod(-0.1, float('inf')), divmod(F(-1, 10), float('inf')))
        self.assertTypedTupleEquals(divmod(-0.1, float('-inf')), divmod(F(-1, 10), float('-inf')))
468 469

        # ** has more interesting conversion rules.
Christian Heimes's avatar
Christian Heimes committed
470 471 472 473 474 475
        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))
476 477
        self.assertAlmostEqual(0, z.real)
        self.assertEqual(1, z.imag)
Christian Heimes's avatar
Christian Heimes committed
478 479 480 481
        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))
482 483
        self.assertRaises(ZeroDivisionError, operator.pow,
                          F(0, 1), -2)
484 485

    def testMixingWithDecimal(self):
486
        # Decimal refuses mixed arithmetic (but not mixed comparisons)
487 488 489 490
        self.assertRaises(TypeError, operator.add,
                          F(3,11), Decimal('3.1415926'))
        self.assertRaises(TypeError, operator.add,
                          Decimal('3.1415926'), F(3,11))
491 492

    def testComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
493 494 495 496 497 498 499 500 501
        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))
502

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
    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)

547
    def testMixedLess(self):
Christian Heimes's avatar
Christian Heimes committed
548 549 550 551
        self.assertTrue(2 < F(5, 2))
        self.assertFalse(2 < F(4, 2))
        self.assertTrue(F(5, 2) < 3)
        self.assertFalse(F(4, 2) < 2)
552

Christian Heimes's avatar
Christian Heimes committed
553 554 555 556
        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))
557

558 559 560 561 562 563 564
        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'))

565
    def testMixedLessEqual(self):
Christian Heimes's avatar
Christian Heimes committed
566 567 568 569 570 571 572 573
        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)
574

575 576 577 578 579 580 581
        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'))

582 583
    def testBigFloatComparisons(self):
        # Because 10**23 can't be represented exactly as a float:
Christian Heimes's avatar
Christian Heimes committed
584
        self.assertFalse(F(10**23) == float(10**23))
585
        # The first test demonstrates why these are important.
Christian Heimes's avatar
Christian Heimes committed
586 587 588 589 590
        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))
591 592

    def testBigComplexComparisons(self):
Christian Heimes's avatar
Christian Heimes committed
593
        self.assertFalse(F(10**23) == complex(10**23))
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
        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)
609 610

    def testMixedEqual(self):
Christian Heimes's avatar
Christian Heimes committed
611 612 613 614 615 616 617 618
        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)
619 620 621 622
        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))
623 624

    def testStringification(self):
625 626 627 628 629 630 631
        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)))
632 633

    def testHash(self):
634 635
        hmod = sys.hash_info.modulus
        hinf = sys.hash_info.inf
636 637 638
        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)))
639
        self.assertEqual(hinf, hash(F(1, hmod)))
640 641
        # Check that __hash__ produces the same value as hash(), for
        # consistency with int and Decimal.  (See issue #10356.)
642
        self.assertEqual(hash(F(-1)), F(-1).__hash__())
643 644 645 646

    def testApproximatePi(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
647
        three = F(3)
648
        lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
Christian Heimes's avatar
Christian Heimes committed
649
        while abs(s - lasts) > F(1, 10**9):
650 651 652 653 654
            lasts = s
            n, na = n+na, na+8
            d, da = d+da, da+32
            t = (t * n) / d
            s += t
655
        self.assertAlmostEqual(math.pi, s)
656 657 658 659

    def testApproximateCos1(self):
        # Algorithm borrowed from
        # http://docs.python.org/lib/decimal-recipes.html
Christian Heimes's avatar
Christian Heimes committed
660 661 662
        x = F(1)
        i, lasts, s, fact, num, sign = 0, 0, F(1), 1, 1, 1
        while abs(s - lasts) > F(1, 10**9):
663 664 665 666 667 668
            lasts = s
            i += 2
            fact *= i * (i-1)
            num *= x * x
            sign *= -1
            s += num / fact * sign
669
        self.assertAlmostEqual(math.cos(1), s)
670

671
    def test_copy_deepcopy_pickle(self):
Christian Heimes's avatar
Christian Heimes committed
672
        r = F(13, 7)
673
        dr = DummyFraction(13, 7)
674 675 676
        self.assertEqual(r, loads(dumps(r)))
        self.assertEqual(id(r), id(copy(r)))
        self.assertEqual(id(r), id(deepcopy(r)))
677 678 679 680
        self.assertNotEqual(id(dr), id(copy(dr)))
        self.assertNotEqual(id(dr), id(deepcopy(dr)))
        self.assertTypedEquals(dr, copy(dr))
        self.assertTypedEquals(dr, deepcopy(dr))
681

682 683 684 685 686
    def test_slots(self):
        # Issue 4998
        r = F(13, 7)
        self.assertRaises(AttributeError, setattr, r, 'a', 10)

687
if __name__ == '__main__':
688
    unittest.main()