11"""Tests for Lib/fractions.py."""
22
3+ import cmath
34from decimal import Decimal
45from test .support import requires_IEEE_754
56import math
@@ -91,6 +92,187 @@ class DummyFraction(fractions.Fraction):
9192def _components (r ):
9293 return (r .numerator , r .denominator )
9394
95+ def typed_approx_eq (a , b ):
96+ return type (a ) == type (b ) and (a == b or math .isclose (a , b ))
97+
98+ class Symbolic :
99+ """Simple non-numeric class for testing mixed arithmetic.
100+ It is not Integral, Rational, Real or Complex, and cannot be conveted
101+ to int, float or complex. but it supports some arithmetic operations.
102+ """
103+ def __init__ (self , value ):
104+ self .value = value
105+ def __mul__ (self , other ):
106+ if isinstance (other , F ):
107+ return NotImplemented
108+ return self .__class__ (f'{ self } * { other } ' )
109+ def __rmul__ (self , other ):
110+ return self .__class__ (f'{ other } * { self } ' )
111+ def __truediv__ (self , other ):
112+ if isinstance (other , F ):
113+ return NotImplemented
114+ return self .__class__ (f'{ self } / { other } ' )
115+ def __rtruediv__ (self , other ):
116+ return self .__class__ (f'{ other } / { self } ' )
117+ def __mod__ (self , other ):
118+ if isinstance (other , F ):
119+ return NotImplemented
120+ return self .__class__ (f'{ self } % { other } ' )
121+ def __rmod__ (self , other ):
122+ return self .__class__ (f'{ other } % { self } ' )
123+ def __pow__ (self , other ):
124+ if isinstance (other , F ):
125+ return NotImplemented
126+ return self .__class__ (f'{ self } ** { other } ' )
127+ def __rpow__ (self , other ):
128+ return self .__class__ (f'{ other } ** { self } ' )
129+ def __eq__ (self , other ):
130+ if other .__class__ != self .__class__ :
131+ return NotImplemented
132+ return self .value == other .value
133+ def __str__ (self ):
134+ return f'{ self .value } '
135+ def __repr__ (self ):
136+ return f'{ self .__class__ .__name__ } ({ self .value !r} )'
137+
138+ class Rat :
139+ """Simple Rational class for testing mixed arithmetic."""
140+ def __init__ (self , n , d ):
141+ self .numerator = n
142+ self .denominator = d
143+ def __mul__ (self , other ):
144+ if isinstance (other , F ):
145+ return NotImplemented
146+ return self .__class__ (self .numerator * other .numerator ,
147+ self .denominator * other .denominator )
148+ def __rmul__ (self , other ):
149+ return self .__class__ (other .numerator * self .numerator ,
150+ other .denominator * self .denominator )
151+ def __truediv__ (self , other ):
152+ if isinstance (other , F ):
153+ return NotImplemented
154+ return self .__class__ (self .numerator * other .denominator ,
155+ self .denominator * other .numerator )
156+ def __rtruediv__ (self , other ):
157+ return self .__class__ (other .numerator * self .denominator ,
158+ other .denominator * self .numerator )
159+ def __mod__ (self , other ):
160+ if isinstance (other , F ):
161+ return NotImplemented
162+ d = self .denominator * other .numerator
163+ return self .__class__ (self .numerator * other .denominator % d , d )
164+ def __rmod__ (self , other ):
165+ d = other .denominator * self .numerator
166+ return self .__class__ (other .numerator * self .denominator % d , d )
167+
168+ return self .__class__ (other .numerator / self .numerator ,
169+ other .denominator / self .denominator )
170+ def __pow__ (self , other ):
171+ if isinstance (other , F ):
172+ return NotImplemented
173+ return self .__class__ (self .numerator ** other ,
174+ self .denominator ** other )
175+ def __float__ (self ):
176+ return self .numerator / self .denominator
177+ def __eq__ (self , other ):
178+ if self .__class__ != other .__class__ :
179+ return NotImplemented
180+ return (typed_approx_eq (self .numerator , other .numerator ) and
181+ typed_approx_eq (self .denominator , other .denominator ))
182+ def __repr__ (self ):
183+ return f'{ self .__class__ .__name__ } ({ self .numerator !r} , { self .denominator !r} )'
184+ numbers .Rational .register (Rat )
185+
186+ class Root :
187+ """Simple Real class for testing mixed arithmetic."""
188+ def __init__ (self , v , n = F (2 )):
189+ self .base = v
190+ self .degree = n
191+ def __mul__ (self , other ):
192+ if isinstance (other , F ):
193+ return NotImplemented
194+ return self .__class__ (self .base * other ** self .degree , self .degree )
195+ def __rmul__ (self , other ):
196+ return self .__class__ (other ** self .degree * self .base , self .degree )
197+ def __truediv__ (self , other ):
198+ if isinstance (other , F ):
199+ return NotImplemented
200+ return self .__class__ (self .base / other ** self .degree , self .degree )
201+ def __rtruediv__ (self , other ):
202+ return self .__class__ (other ** self .degree / self .base , self .degree )
203+ def __pow__ (self , other ):
204+ if isinstance (other , F ):
205+ return NotImplemented
206+ return self .__class__ (self .base , self .degree / other )
207+ def __float__ (self ):
208+ return float (self .base ) ** (1 / float (self .degree ))
209+ def __eq__ (self , other ):
210+ if self .__class__ != other .__class__ :
211+ return NotImplemented
212+ return typed_approx_eq (self .base , other .base ) and typed_approx_eq (self .degree , other .degree )
213+ def __repr__ (self ):
214+ return f'{ self .__class__ .__name__ } ({ self .base !r} , { self .degree !r} )'
215+ numbers .Real .register (Root )
216+
217+ class Polar :
218+ """Simple Complex class for testing mixed arithmetic."""
219+ def __init__ (self , r , phi ):
220+ self .r = r
221+ self .phi = phi
222+ def __mul__ (self , other ):
223+ if isinstance (other , F ):
224+ return NotImplemented
225+ return self .__class__ (self .r * other , self .phi )
226+ def __rmul__ (self , other ):
227+ return self .__class__ (other * self .r , self .phi )
228+ def __truediv__ (self , other ):
229+ if isinstance (other , F ):
230+ return NotImplemented
231+ return self .__class__ (self .r / other , self .phi )
232+ def __rtruediv__ (self , other ):
233+ return self .__class__ (other / self .r , - self .phi )
234+ def __pow__ (self , other ):
235+ if isinstance (other , F ):
236+ return NotImplemented
237+ return self .__class__ (self .r ** other , self .phi * other )
238+ def __eq__ (self , other ):
239+ if self .__class__ != other .__class__ :
240+ return NotImplemented
241+ return typed_approx_eq (self .r , other .r ) and typed_approx_eq (self .phi , other .phi )
242+ def __repr__ (self ):
243+ return f'{ self .__class__ .__name__ } ({ self .r !r} , { self .phi !r} )'
244+ numbers .Complex .register (Polar )
245+
246+ class Rect :
247+ """Other simple Complex class for testing mixed arithmetic."""
248+ def __init__ (self , x , y ):
249+ self .x = x
250+ self .y = y
251+ def __mul__ (self , other ):
252+ if isinstance (other , F ):
253+ return NotImplemented
254+ return self .__class__ (self .x * other , self .y * other )
255+ def __rmul__ (self , other ):
256+ return self .__class__ (other * self .x , other * self .y )
257+ def __truediv__ (self , other ):
258+ if isinstance (other , F ):
259+ return NotImplemented
260+ return self .__class__ (self .x / other , self .y / other )
261+ def __rtruediv__ (self , other ):
262+ r = self .x * self .x + self .y * self .y
263+ return self .__class__ (other * (self .x / r ), other * (self .y / r ))
264+ def __rpow__ (self , other ):
265+ return Polar (other ** self .x , math .log (other ) * self .y )
266+ def __complex__ (self ):
267+ return complex (self .x , self .y )
268+ def __eq__ (self , other ):
269+ if self .__class__ != other .__class__ :
270+ return NotImplemented
271+ return typed_approx_eq (self .x , other .x ) and typed_approx_eq (self .y , other .y )
272+ def __repr__ (self ):
273+ return f'{ self .__class__ .__name__ } ({ self .x !r} , { self .y !r} )'
274+ numbers .Complex .register (Rect )
275+
94276
95277class FractionTest (unittest .TestCase ):
96278
@@ -593,20 +775,57 @@ def testMixedArithmetic(self):
593775 self .assertTypedEquals (0.9 , 1.0 - F (1 , 10 ))
594776 self .assertTypedEquals (0.9 + 0j , (1.0 + 0j ) - F (1 , 10 ))
595777
778+ def testMixedMultiplication (self ):
596779 self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) * 1 )
597780 self .assertTypedEquals (0.1 , F (1 , 10 ) * 1.0 )
598781 self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) * (1.0 + 0j ))
599782 self .assertTypedEquals (F (1 , 10 ), 1 * F (1 , 10 ))
600783 self .assertTypedEquals (0.1 , 1.0 * F (1 , 10 ))
601784 self .assertTypedEquals (0.1 + 0j , (1.0 + 0j ) * F (1 , 10 ))
602785
786+ self .assertTypedEquals (F (3 , 2 ) * DummyFraction (5 , 3 ), F (5 , 2 ))
787+ self .assertTypedEquals (DummyFraction (5 , 3 ) * F (3 , 2 ), F (5 , 2 ))
788+ self .assertTypedEquals (F (3 , 2 ) * Rat (5 , 3 ), Rat (15 , 6 ))
789+ self .assertTypedEquals (Rat (5 , 3 ) * F (3 , 2 ), F (5 , 2 ))
790+
791+ self .assertTypedEquals (F (3 , 2 ) * Root (4 ), Root (F (9 , 1 )))
792+ self .assertTypedEquals (Root (4 ) * F (3 , 2 ), 3.0 )
793+
794+ self .assertTypedEquals (F (3 , 2 ) * Polar (4 , 2 ), Polar (F (6 , 1 ), 2 ))
795+ self .assertTypedEquals (F (3 , 2 ) * Polar (4.0 , 2 ), Polar (6.0 , 2 ))
796+ self .assertTypedEquals (F (3 , 2 ) * Rect (4 , 3 ), Rect (F (6 , 1 ), F (9 , 2 )))
797+ self .assertRaises (TypeError , operator .mul , Polar (4 , 2 ), F (3 , 2 ))
798+ self .assertTypedEquals (Rect (4 , 3 ) * F (3 , 2 ), 6.0 + 4.5j )
799+
800+ self .assertEqual (F (3 , 2 ) * Symbolic ('X' ), Symbolic ('3/2 * X' ))
801+ self .assertRaises (TypeError , operator .mul , Symbolic ('X' ), F (3 , 2 ))
802+
803+ def testMixedDivision (self ):
603804 self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) / 1 )
604805 self .assertTypedEquals (0.1 , F (1 , 10 ) / 1.0 )
605806 self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) / (1.0 + 0j ))
606807 self .assertTypedEquals (F (10 , 1 ), 1 / F (1 , 10 ))
607808 self .assertTypedEquals (10.0 , 1.0 / F (1 , 10 ))
608809 self .assertTypedEquals (10.0 + 0j , (1.0 + 0j ) / F (1 , 10 ))
609810
811+ self .assertTypedEquals (F (3 , 2 ) / DummyFraction (3 , 5 ), F (5 , 2 ))
812+ self .assertTypedEquals (DummyFraction (5 , 3 ) / F (2 , 3 ), F (5 , 2 ))
813+ self .assertTypedEquals (F (3 , 2 ) / Rat (3 , 5 ), Rat (15 , 6 ))
814+ self .assertTypedEquals (Rat (5 , 3 ) / F (2 , 3 ), F (5 , 2 ))
815+
816+ self .assertTypedEquals (F (2 , 3 ) / Root (4 ), Root (F (1 , 9 )))
817+ self .assertTypedEquals (Root (4 ) / F (2 , 3 ), 3.0 )
818+
819+ self .assertTypedEquals (F (3 , 2 ) / Polar (4 , 2 ), Polar (F (3 , 8 ), - 2 ))
820+ self .assertTypedEquals (F (3 , 2 ) / Polar (4.0 , 2 ), Polar (0.375 , - 2 ))
821+ self .assertTypedEquals (F (3 , 2 ) / Rect (4 , 3 ), Rect (0.24 , 0.18 ))
822+ self .assertRaises (TypeError , operator .truediv , Polar (4 , 2 ), F (2 , 3 ))
823+ self .assertTypedEquals (Rect (4 , 3 ) / F (2 , 3 ), 6.0 + 4.5j )
824+
825+ self .assertEqual (F (3 , 2 ) / Symbolic ('X' ), Symbolic ('3/2 / X' ))
826+ self .assertRaises (TypeError , operator .truediv , Symbolic ('X' ), F (2 , 3 ))
827+
828+ def testMixedIntegerDivision (self ):
610829 self .assertTypedEquals (0 , F (1 , 10 ) // 1 )
611830 self .assertTypedEquals (0.0 , F (1 , 10 ) // 1.0 )
612831 self .assertTypedEquals (10 , 1 // F (1 , 10 ))
@@ -631,6 +850,21 @@ def testMixedArithmetic(self):
631850 self .assertTypedTupleEquals (divmod (- 0.1 , float ('inf' )), divmod (F (- 1 , 10 ), float ('inf' )))
632851 self .assertTypedTupleEquals (divmod (- 0.1 , float ('-inf' )), divmod (F (- 1 , 10 ), float ('-inf' )))
633852
853+ self .assertTypedEquals (F (3 , 2 ) % DummyFraction (3 , 5 ), F (3 , 10 ))
854+ self .assertTypedEquals (DummyFraction (5 , 3 ) % F (2 , 3 ), F (1 , 3 ))
855+ self .assertTypedEquals (F (3 , 2 ) % Rat (3 , 5 ), Rat (3 , 6 ))
856+ self .assertTypedEquals (Rat (5 , 3 ) % F (2 , 3 ), F (1 , 3 ))
857+
858+ self .assertRaises (TypeError , operator .mod , F (2 , 3 ), Root (4 ))
859+ self .assertTypedEquals (Root (4 ) % F (3 , 2 ), 0.5 )
860+
861+ self .assertRaises (TypeError , operator .mod , F (3 , 2 ), Polar (4 , 2 ))
862+ self .assertRaises (TypeError , operator .mod , Rect (4 , 3 ), F (2 , 3 ))
863+
864+ self .assertEqual (F (3 , 2 ) % Symbolic ('X' ), Symbolic ('3/2 % X' ))
865+ self .assertRaises (TypeError , operator .mod , Symbolic ('X' ), F (2 , 3 ))
866+
867+ def testMixedPower (self ):
634868 # ** has more interesting conversion rules.
635869 self .assertTypedEquals (F (100 , 1 ), F (1 , 10 ) ** - 2 )
636870 self .assertTypedEquals (F (100 , 1 ), F (10 , 1 ) ** 2 )
@@ -647,6 +881,35 @@ def testMixedArithmetic(self):
647881 self .assertRaises (ZeroDivisionError , operator .pow ,
648882 F (0 , 1 ), - 2 )
649883
884+ self .assertTypedEquals (F (3 , 2 ) ** Rat (3 , 1 ), F (27 , 8 ))
885+ self .assertTypedEquals (F (3 , 2 ) ** Rat (- 3 , 1 ), F (8 , 27 ))
886+ self .assertTypedEquals (F (- 3 , 2 ) ** Rat (- 3 , 1 ), F (- 8 , 27 ))
887+ self .assertTypedEquals (F (9 , 4 ) ** Rat (3 , 2 ), 3.375 )
888+ self .assertIsInstance (F (4 , 9 ) ** Rat (- 3 , 2 ), float )
889+ self .assertAlmostEqual (F (4 , 9 ) ** Rat (- 3 , 2 ), 3.375 )
890+ self .assertAlmostEqual (F (- 4 , 9 ) ** Rat (- 3 , 2 ), 3.375j )
891+
892+ self .assertTypedEquals (Rat (9 , 4 ) ** F (3 , 2 ), 3.375 )
893+ self .assertTypedEquals (Rat (3 , 2 ) ** F (3 , 1 ), Rat (27 , 8 ))
894+ self .assertTypedEquals (Rat (3 , 2 ) ** F (- 3 , 1 ), F (8 , 27 ))
895+ self .assertIsInstance (Rat (4 , 9 ) ** F (- 3 , 2 ), float )
896+ self .assertAlmostEqual (Rat (4 , 9 ) ** F (- 3 , 2 ), 3.375 )
897+
898+ self .assertTypedEquals (Root (4 ) ** F (2 , 3 ), Root (4 , 3.0 ))
899+ self .assertTypedEquals (Root (4 ) ** F (2 , 1 ), Root (4 , F (1 )))
900+ self .assertTypedEquals (Root (4 ) ** F (- 2 , 1 ), Root (4 , - F (1 )))
901+ self .assertTypedEquals (Root (4 ) ** F (- 2 , 3 ), Root (4 , - 3.0 ))
902+
903+ self .assertTypedEquals (F (3 , 2 ) ** Rect (2 , 0 ), Polar (2.25 , 0.0 ))
904+ self .assertTypedEquals (F (1 , 1 ) ** Rect (2 , 3 ), Polar (1.0 , 0.0 ))
905+ self .assertTypedEquals (Polar (4 , 2 ) ** F (3 , 2 ), Polar (8.0 , 3.0 ))
906+ self .assertTypedEquals (Polar (4 , 2 ) ** F (3 , 1 ), Polar (64 , 6 ))
907+ self .assertTypedEquals (Polar (4 , 2 ) ** F (- 3 , 1 ), Polar (0.015625 , - 6 ))
908+ self .assertTypedEquals (Polar (4 , 2 ) ** F (- 3 , 2 ), Polar (0.125 , - 3.0 ))
909+
910+ self .assertTypedEquals (F (3 , 2 ) ** Symbolic ('X' ), Symbolic ('1.5 ** X' ))
911+ self .assertTypedEquals (Symbolic ('X' ) ** F (3 , 2 ), Symbolic ('X ** 1.5' ))
912+
650913 def testMixingWithDecimal (self ):
651914 # Decimal refuses mixed arithmetic (but not mixed comparisons)
652915 self .assertRaises (TypeError , operator .add ,
0 commit comments