1
1
"""Tests for Lib/fractions.py."""
2
2
3
+ import cmath
3
4
from decimal import Decimal
4
5
from test .support import requires_IEEE_754
5
6
import math
@@ -91,6 +92,187 @@ class DummyFraction(fractions.Fraction):
91
92
def _components (r ):
92
93
return (r .numerator , r .denominator )
93
94
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
+
94
276
95
277
class FractionTest (unittest .TestCase ):
96
278
@@ -593,20 +775,57 @@ def testMixedArithmetic(self):
593
775
self .assertTypedEquals (0.9 , 1.0 - F (1 , 10 ))
594
776
self .assertTypedEquals (0.9 + 0j , (1.0 + 0j ) - F (1 , 10 ))
595
777
778
+ def testMixedMultiplication (self ):
596
779
self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) * 1 )
597
780
self .assertTypedEquals (0.1 , F (1 , 10 ) * 1.0 )
598
781
self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) * (1.0 + 0j ))
599
782
self .assertTypedEquals (F (1 , 10 ), 1 * F (1 , 10 ))
600
783
self .assertTypedEquals (0.1 , 1.0 * F (1 , 10 ))
601
784
self .assertTypedEquals (0.1 + 0j , (1.0 + 0j ) * F (1 , 10 ))
602
785
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 ):
603
804
self .assertTypedEquals (F (1 , 10 ), F (1 , 10 ) / 1 )
604
805
self .assertTypedEquals (0.1 , F (1 , 10 ) / 1.0 )
605
806
self .assertTypedEquals (0.1 + 0j , F (1 , 10 ) / (1.0 + 0j ))
606
807
self .assertTypedEquals (F (10 , 1 ), 1 / F (1 , 10 ))
607
808
self .assertTypedEquals (10.0 , 1.0 / F (1 , 10 ))
608
809
self .assertTypedEquals (10.0 + 0j , (1.0 + 0j ) / F (1 , 10 ))
609
810
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 ):
610
829
self .assertTypedEquals (0 , F (1 , 10 ) // 1 )
611
830
self .assertTypedEquals (0.0 , F (1 , 10 ) // 1.0 )
612
831
self .assertTypedEquals (10 , 1 // F (1 , 10 ))
@@ -631,6 +850,21 @@ def testMixedArithmetic(self):
631
850
self .assertTypedTupleEquals (divmod (- 0.1 , float ('inf' )), divmod (F (- 1 , 10 ), float ('inf' )))
632
851
self .assertTypedTupleEquals (divmod (- 0.1 , float ('-inf' )), divmod (F (- 1 , 10 ), float ('-inf' )))
633
852
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 ):
634
868
# ** has more interesting conversion rules.
635
869
self .assertTypedEquals (F (100 , 1 ), F (1 , 10 ) ** - 2 )
636
870
self .assertTypedEquals (F (100 , 1 ), F (10 , 1 ) ** 2 )
@@ -647,6 +881,35 @@ def testMixedArithmetic(self):
647
881
self .assertRaises (ZeroDivisionError , operator .pow ,
648
882
F (0 , 1 ), - 2 )
649
883
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
+
650
913
def testMixingWithDecimal (self ):
651
914
# Decimal refuses mixed arithmetic (but not mixed comparisons)
652
915
self .assertRaises (TypeError , operator .add ,
0 commit comments