33from decimal import Decimal
44from test .support import run_unittest
55import math
6+ import numbers
67import operator
78import fractions
89import unittest
1112F = fractions .Fraction
1213gcd = fractions .gcd
1314
15+ class DummyFloat (object ):
16+ """Dummy float class for testing comparisons with Fractions"""
17+
18+ def __init__ (self , value ):
19+ if not isinstance (value , float ):
20+ raise TypeError ("DummyFloat can only be initialized from float" )
21+ self .value = value
22+
23+ def _richcmp (self , other , op ):
24+ if isinstance (other , numbers .Rational ):
25+ return op (F .from_float (self .value ), other )
26+ elif isinstance (other , DummyFloat ):
27+ return op (self .value , other .value )
28+ else :
29+ return NotImplemented
30+
31+ def __eq__ (self , other ): return self ._richcmp (other , operator .eq )
32+ def __le__ (self , other ): return self ._richcmp (other , operator .le )
33+ def __lt__ (self , other ): return self ._richcmp (other , operator .lt )
34+ def __ge__ (self , other ): return self ._richcmp (other , operator .ge )
35+ def __gt__ (self , other ): return self ._richcmp (other , operator .gt )
36+
37+ # shouldn't be calling __float__ at all when doing comparisons
38+ def __float__ (self ):
39+ assert False , "__float__ should not be invoked for comparisons"
40+
41+ # same goes for subtraction
42+ def __sub__ (self , other ):
43+ assert False , "__sub__ should not be invoked for comparisons"
44+ __rsub__ = __sub__
45+
46+
47+ class DummyRational (object ):
48+ """Test comparison of Fraction with a naive rational implementation."""
49+
50+ def __init__ (self , num , den ):
51+ g = gcd (num , den )
52+ self .num = num // g
53+ self .den = den // g
54+
55+ def __eq__ (self , other ):
56+ if isinstance (other , fractions .Fraction ):
57+ return (self .num == other ._numerator and
58+ self .den == other ._denominator )
59+ else :
60+ return NotImplemented
61+
62+ def __lt__ (self , other ):
63+ return (self .num * other ._denominator < self .den * other ._numerator )
64+
65+ def __gt__ (self , other ):
66+ return (self .num * other ._denominator > self .den * other ._numerator )
67+
68+ def __le__ (self , other ):
69+ return (self .num * other ._denominator <= self .den * other ._numerator )
70+
71+ def __ge__ (self , other ):
72+ return (self .num * other ._denominator >= self .den * other ._numerator )
73+
74+ # this class is for testing comparisons; conversion to float
75+ # should never be used for a comparison, since it loses accuracy
76+ def __float__ (self ):
77+ assert False , "__float__ should not be invoked"
1478
1579class GcdTest (unittest .TestCase ):
1680
@@ -324,6 +388,50 @@ def testComparisons(self):
324388 self .assertFalse (F (1 , 2 ) != F (1 , 2 ))
325389 self .assertTrue (F (1 , 2 ) != F (1 , 3 ))
326390
391+ def testComparisonsDummyRational (self ):
392+ self .assertTrue (F (1 , 2 ) == DummyRational (1 , 2 ))
393+ self .assertTrue (DummyRational (1 , 2 ) == F (1 , 2 ))
394+ self .assertFalse (F (1 , 2 ) == DummyRational (3 , 4 ))
395+ self .assertFalse (DummyRational (3 , 4 ) == F (1 , 2 ))
396+
397+ self .assertTrue (F (1 , 2 ) < DummyRational (3 , 4 ))
398+ self .assertFalse (F (1 , 2 ) < DummyRational (1 , 2 ))
399+ self .assertFalse (F (1 , 2 ) < DummyRational (1 , 7 ))
400+ self .assertFalse (F (1 , 2 ) > DummyRational (3 , 4 ))
401+ self .assertFalse (F (1 , 2 ) > DummyRational (1 , 2 ))
402+ self .assertTrue (F (1 , 2 ) > DummyRational (1 , 7 ))
403+ self .assertTrue (F (1 , 2 ) <= DummyRational (3 , 4 ))
404+ self .assertTrue (F (1 , 2 ) <= DummyRational (1 , 2 ))
405+ self .assertFalse (F (1 , 2 ) <= DummyRational (1 , 7 ))
406+ self .assertFalse (F (1 , 2 ) >= DummyRational (3 , 4 ))
407+ self .assertTrue (F (1 , 2 ) >= DummyRational (1 , 2 ))
408+ self .assertTrue (F (1 , 2 ) >= DummyRational (1 , 7 ))
409+
410+ self .assertTrue (DummyRational (1 , 2 ) < F (3 , 4 ))
411+ self .assertFalse (DummyRational (1 , 2 ) < F (1 , 2 ))
412+ self .assertFalse (DummyRational (1 , 2 ) < F (1 , 7 ))
413+ self .assertFalse (DummyRational (1 , 2 ) > F (3 , 4 ))
414+ self .assertFalse (DummyRational (1 , 2 ) > F (1 , 2 ))
415+ self .assertTrue (DummyRational (1 , 2 ) > F (1 , 7 ))
416+ self .assertTrue (DummyRational (1 , 2 ) <= F (3 , 4 ))
417+ self .assertTrue (DummyRational (1 , 2 ) <= F (1 , 2 ))
418+ self .assertFalse (DummyRational (1 , 2 ) <= F (1 , 7 ))
419+ self .assertFalse (DummyRational (1 , 2 ) >= F (3 , 4 ))
420+ self .assertTrue (DummyRational (1 , 2 ) >= F (1 , 2 ))
421+ self .assertTrue (DummyRational (1 , 2 ) >= F (1 , 7 ))
422+
423+ def testComparisonsDummyFloat (self ):
424+ x = DummyFloat (1. / 3. )
425+ y = F (1 , 3 )
426+ self .assertTrue (x != y )
427+ self .assertTrue (x < y or x > y )
428+ self .assertFalse (x == y )
429+ self .assertFalse (x <= y and x >= y )
430+ self .assertTrue (y != x )
431+ self .assertTrue (y < x or y > x )
432+ self .assertFalse (y == x )
433+ self .assertFalse (y <= x and y >= x )
434+
327435 def testMixedLess (self ):
328436 self .assertTrue (2 < F (5 , 2 ))
329437 self .assertFalse (2 < F (4 , 2 ))
@@ -335,6 +443,13 @@ def testMixedLess(self):
335443 self .assertTrue (0.4 < F (1 , 2 ))
336444 self .assertFalse (0.5 < F (1 , 2 ))
337445
446+ self .assertFalse (float ('inf' ) < F (1 , 2 ))
447+ self .assertTrue (float ('-inf' ) < F (0 , 10 ))
448+ self .assertFalse (float ('nan' ) < F (- 3 , 7 ))
449+ self .assertTrue (F (1 , 2 ) < float ('inf' ))
450+ self .assertFalse (F (17 , 12 ) < float ('-inf' ))
451+ self .assertFalse (F (144 , - 89 ) < float ('nan' ))
452+
338453 def testMixedLessEqual (self ):
339454 self .assertTrue (0.5 <= F (1 , 2 ))
340455 self .assertFalse (0.6 <= F (1 , 2 ))
@@ -345,6 +460,13 @@ def testMixedLessEqual(self):
345460 self .assertTrue (F (4 , 2 ) <= 2 )
346461 self .assertFalse (F (5 , 2 ) <= 2 )
347462
463+ self .assertFalse (float ('inf' ) <= F (1 , 2 ))
464+ self .assertTrue (float ('-inf' ) <= F (0 , 10 ))
465+ self .assertFalse (float ('nan' ) <= F (- 3 , 7 ))
466+ self .assertTrue (F (1 , 2 ) <= float ('inf' ))
467+ self .assertFalse (F (17 , 12 ) <= float ('-inf' ))
468+ self .assertFalse (F (144 , - 89 ) <= float ('nan' ))
469+
348470 def testBigFloatComparisons (self ):
349471 # Because 10**23 can't be represented exactly as a float:
350472 self .assertFalse (F (10 ** 23 ) == float (10 ** 23 ))
@@ -369,6 +491,10 @@ def testMixedEqual(self):
369491 self .assertFalse (2 == F (3 , 2 ))
370492 self .assertTrue (F (4 , 2 ) == 2 )
371493 self .assertFalse (F (5 , 2 ) == 2 )
494+ self .assertFalse (F (5 , 2 ) == float ('nan' ))
495+ self .assertFalse (float ('nan' ) == F (3 , 7 ))
496+ self .assertFalse (F (5 , 2 ) == float ('inf' ))
497+ self .assertFalse (float ('-inf' ) == F (2 , 5 ))
372498
373499 def testStringification (self ):
374500 self .assertEquals ("Fraction(7, 3)" , repr (F (7 , 3 )))
0 commit comments