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 ):
9293return (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):
593775self .assertTypedEquals (0.9 ,1.0 - F (1 ,10 ))
594776self .assertTypedEquals (0.9 + 0j , (1.0 + 0j )- F (1 ,10 ))
595777
778+ def testMixedMultiplication (self ):
596779self .assertTypedEquals (F (1 ,10 ),F (1 ,10 )* 1 )
597780self .assertTypedEquals (0.1 ,F (1 ,10 )* 1.0 )
598781self .assertTypedEquals (0.1 + 0j ,F (1 ,10 )* (1.0 + 0j ))
599782self .assertTypedEquals (F (1 ,10 ),1 * F (1 ,10 ))
600783self .assertTypedEquals (0.1 ,1.0 * F (1 ,10 ))
601784self .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 ):
603804self .assertTypedEquals (F (1 ,10 ),F (1 ,10 )/ 1 )
604805self .assertTypedEquals (0.1 ,F (1 ,10 )/ 1.0 )
605806self .assertTypedEquals (0.1 + 0j ,F (1 ,10 )/ (1.0 + 0j ))
606807self .assertTypedEquals (F (10 ,1 ),1 / F (1 ,10 ))
607808self .assertTypedEquals (10.0 ,1.0 / F (1 ,10 ))
608809self .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 ):
610829self .assertTypedEquals (0 ,F (1 ,10 )// 1 )
611830self .assertTypedEquals (0.0 ,F (1 ,10 )// 1.0 )
612831self .assertTypedEquals (10 ,1 // F (1 ,10 ))
@@ -631,6 +850,21 @@ def testMixedArithmetic(self):
631850self .assertTypedTupleEquals (divmod (- 0.1 ,float ('inf' )),divmod (F (- 1 ,10 ),float ('inf' )))
632851self .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.
635869self .assertTypedEquals (F (100 ,1 ),F (1 ,10 )** - 2 )
636870self .assertTypedEquals (F (100 ,1 ),F (10 ,1 )** 2 )
@@ -647,6 +881,35 @@ def testMixedArithmetic(self):
647881self .assertRaises (ZeroDivisionError ,operator .pow ,
648882F (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+
650913def testMixingWithDecimal (self ):
651914# Decimal refuses mixed arithmetic (but not mixed comparisons)
652915self .assertRaises (TypeError ,operator .add ,