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