Skip to content

Commit 317c1c6

Browse files
gh-119189: Add more tests for mixed Fraction arithmetic
1 parent 16b46eb commit 317c1c6

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed

Lib/test/test_fractions.py

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for Lib/fractions.py."""
22

3+
import cmath
34
from decimal import Decimal
45
from test.support import requires_IEEE_754
56
import math
@@ -91,6 +92,170 @@ class DummyFraction(fractions.Fraction):
9192
def _components(r):
9293
return (r.numerator, r.denominator)
9394

95+
def is_eq(a, b):
96+
return type(a) == type(b) and (a == b or math.isclose(a, b))
97+
98+
class One:
99+
def __mul__(self, other):
100+
if isinstance(other, F):
101+
return NotImplemented
102+
return other
103+
def __rmul__(self, other):
104+
return other
105+
def __truediv__(self, other):
106+
if isinstance(other, F):
107+
return NotImplemented
108+
return 1 / other
109+
def __rtruediv__(self, other):
110+
return other
111+
def __mod__(self, other):
112+
if isinstance(other, F):
113+
return NotImplemented
114+
return 1 % other
115+
def __rmod__(self, other):
116+
return other % 1
117+
def __pow__(self, other):
118+
if isinstance(other, F):
119+
return NotImplemented
120+
return self
121+
def __rpow__(self, other):
122+
return other
123+
One = One()
124+
125+
class Rat:
126+
def __init__(self, n, d):
127+
self.numerator = n
128+
self.denominator = d
129+
def __mul__(self, other):
130+
if isinstance(other, F):
131+
return NotImplemented
132+
return self.__class__(self.numerator * other.numerator,
133+
self.denominator * other.denominator)
134+
def __rmul__(self, other):
135+
return self.__class__(other.numerator * self.numerator,
136+
other.denominator * self.denominator)
137+
def __truediv__(self, other):
138+
if isinstance(other, F):
139+
return NotImplemented
140+
return self.__class__(self.numerator * other.denominator,
141+
self.denominator * other.numerator)
142+
def __rtruediv__(self, other):
143+
return self.__class__(other.numerator * self.denominator,
144+
other.denominator * self.numerator)
145+
def __mod__(self, other):
146+
if isinstance(other, F):
147+
return NotImplemented
148+
d = self.denominator * other.numerator
149+
return self.__class__(self.numerator * other.denominator % d, d)
150+
def __rmod__(self, other):
151+
d = other.denominator * self.numerator
152+
return self.__class__(other.numerator * self.denominator % d, d)
153+
154+
return self.__class__(other.numerator / self.numerator,
155+
other.denominator / self.denominator)
156+
def __pow__(self, other):
157+
if isinstance(other, F):
158+
return NotImplemented
159+
return self.__class__(self.numerator ** other,
160+
self.denominator ** other)
161+
def __float__(self):
162+
return self.numerator / self.denominator
163+
def __eq__(self, other):
164+
if self.__class__ != other.__class__:
165+
return NotImplemented
166+
return (is_eq(self.numerator, other.numerator) and
167+
is_eq(self.denominator, other.denominator))
168+
def __repr__(self):
169+
return f'{self.__class__.__name__}({self.numerator!r}, {self.denominator!r})'
170+
numbers.Rational.register(Rat)
171+
172+
class Root:
173+
def __init__(self, v, n=F(2)):
174+
self.base = v
175+
self.degree = n
176+
def __mul__(self, other):
177+
if isinstance(other, F):
178+
return NotImplemented
179+
return self.__class__(self.base * other**self.degree, self.degree)
180+
def __rmul__(self, other):
181+
return self.__class__(other**self.degree * self.base, self.degree)
182+
def __truediv__(self, other):
183+
if isinstance(other, F):
184+
return NotImplemented
185+
return self.__class__(self.base / other**self.degree, self.degree)
186+
def __rtruediv__(self, other):
187+
return self.__class__(other**self.degree / self.base, self.degree)
188+
def __pow__(self, other):
189+
if isinstance(other, F):
190+
return NotImplemented
191+
return self.__class__(self.base, self.degree / other)
192+
def __float__(self):
193+
return float(self.base) ** (1 / float(self.degree))
194+
def __eq__(self, other):
195+
if self.__class__ != other.__class__:
196+
return NotImplemented
197+
return is_eq(self.base, other.base) and is_eq(self.degree, other.degree)
198+
def __repr__(self):
199+
return f'{self.__class__.__name__}({self.base!r}, {self.degree!r})'
200+
numbers.Real.register(Root)
201+
202+
class Polar:
203+
def __init__(self, r, phi):
204+
self.r = r
205+
self.phi = phi
206+
def __mul__(self, other):
207+
if isinstance(other, F):
208+
return NotImplemented
209+
return self.__class__(self.r * other, self.phi)
210+
def __rmul__(self, other):
211+
return self.__class__(other * self.r, self.phi)
212+
def __truediv__(self, other):
213+
if isinstance(other, F):
214+
return NotImplemented
215+
return self.__class__(self.r / other, self.phi)
216+
def __rtruediv__(self, other):
217+
return self.__class__(other / self.r, -self.phi)
218+
def __pow__(self, other):
219+
if isinstance(other, F):
220+
return NotImplemented
221+
return self.__class__(self.r ** other, self.phi * other)
222+
def __eq__(self, other):
223+
if self.__class__ != other.__class__:
224+
return NotImplemented
225+
return is_eq(self.r, other.r) and is_eq(self.phi, other.phi)
226+
def __repr__(self):
227+
return f'{self.__class__.__name__}({self.r!r}, {self.phi!r})'
228+
numbers.Complex.register(Polar)
229+
230+
class Rect:
231+
def __init__(self, x, y):
232+
self.x = x
233+
self.y = y
234+
def __mul__(self, other):
235+
if isinstance(other, F):
236+
return NotImplemented
237+
return self.__class__(self.x * other, self.y * other)
238+
def __rmul__(self, other):
239+
return self.__class__(other * self.x, other * self.y)
240+
def __truediv__(self, other):
241+
if isinstance(other, F):
242+
return NotImplemented
243+
return self.__class__(self.x / other, self.y / other)
244+
def __rtruediv__(self, other):
245+
r = self.x * self.x + self.y * self.y
246+
return self.__class__(other * (self.x / r), other * (self.y / r))
247+
def __rpow__(self, other):
248+
return Polar(other ** self.x, math.log(other) * self.y)
249+
def __complex__(self):
250+
return complex(self.x, self.y)
251+
def __eq__(self, other):
252+
if self.__class__ != other.__class__:
253+
return NotImplemented
254+
return is_eq(self.x, other.x) and is_eq(self.y, other.y)
255+
def __repr__(self):
256+
return f'{self.__class__.__name__}({self.x!r}, {self.y!r})'
257+
numbers.Complex.register(Rect)
258+
94259

95260
class FractionTest(unittest.TestCase):
96261

@@ -593,20 +758,57 @@ def testMixedArithmetic(self):
593758
self.assertTypedEquals(0.9, 1.0 - F(1, 10))
594759
self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - F(1, 10))
595760

761+
def testMixedMultiplication(self):
596762
self.assertTypedEquals(F(1, 10), F(1, 10) * 1)
597763
self.assertTypedEquals(0.1, F(1, 10) * 1.0)
598764
self.assertTypedEquals(0.1 + 0j, F(1, 10) * (1.0 + 0j))
599765
self.assertTypedEquals(F(1, 10), 1 * F(1, 10))
600766
self.assertTypedEquals(0.1, 1.0 * F(1, 10))
601767
self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * F(1, 10))
602768

769+
self.assertTypedEquals(F(3, 2) * DummyFraction(5, 3), F(5, 2))
770+
self.assertTypedEquals(DummyFraction(5, 3) * F(3, 2), F(5, 2))
771+
self.assertTypedEquals(F(3, 2) * Rat(5, 3), Rat(15, 6))
772+
self.assertTypedEquals(Rat(5, 3) * F(3, 2), F(5, 2))
773+
774+
self.assertTypedEquals(F(3, 2) * Root(4), Root(F(9, 1)))
775+
self.assertTypedEquals(Root(4) * F(3, 2), 3.0)
776+
777+
self.assertTypedEquals(F(3, 2) * Polar(4, 2), Polar(F(6, 1), 2))
778+
self.assertTypedEquals(F(3, 2) * Polar(4.0, 2), Polar(6.0, 2))
779+
self.assertTypedEquals(F(3, 2) * Rect(4, 3), Rect(F(6, 1), F(9, 2)))
780+
self.assertRaises(TypeError, operator.mul, Polar(4, 2), F(3, 2))
781+
self.assertTypedEquals(Rect(4, 3) * F(3, 2), 6.0 + 4.5j)
782+
783+
self.assertTypedEquals(F(3, 2) * One, F(3, 2))
784+
self.assertRaises(TypeError, operator.mul, One, F(3, 2))
785+
786+
def testMixedDivision(self):
603787
self.assertTypedEquals(F(1, 10), F(1, 10) / 1)
604788
self.assertTypedEquals(0.1, F(1, 10) / 1.0)
605789
self.assertTypedEquals(0.1 + 0j, F(1, 10) / (1.0 + 0j))
606790
self.assertTypedEquals(F(10, 1), 1 / F(1, 10))
607791
self.assertTypedEquals(10.0, 1.0 / F(1, 10))
608792
self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / F(1, 10))
609793

794+
self.assertTypedEquals(F(3, 2) / DummyFraction(3, 5), F(5, 2))
795+
self.assertTypedEquals(DummyFraction(5, 3) / F(2, 3), F(5, 2))
796+
self.assertTypedEquals(F(3, 2) / Rat(3, 5), Rat(15, 6))
797+
self.assertTypedEquals(Rat(5, 3) / F(2, 3), F(5, 2))
798+
799+
self.assertTypedEquals(F(2, 3) / Root(4), Root(F(1, 9)))
800+
self.assertTypedEquals(Root(4) / F(2, 3), 3.0)
801+
802+
self.assertTypedEquals(F(3, 2) / Polar(4, 2), Polar(F(3, 8), -2))
803+
self.assertTypedEquals(F(3, 2) / Polar(4.0, 2), Polar(0.375, -2))
804+
self.assertTypedEquals(F(3, 2) / Rect(4, 3), Rect(0.24, 0.18))
805+
self.assertRaises(TypeError, operator.truediv, Polar(4, 2), F(2, 3))
806+
self.assertTypedEquals(Rect(4, 3) / F(2, 3), 6.0 + 4.5j)
807+
808+
self.assertTypedEquals(F(3, 2) / One, F(3, 2))
809+
self.assertRaises(TypeError, operator.truediv, One, F(2, 3))
810+
811+
def testMixedIntegerDivision(self):
610812
self.assertTypedEquals(0, F(1, 10) // 1)
611813
self.assertTypedEquals(0.0, F(1, 10) // 1.0)
612814
self.assertTypedEquals(10, 1 // F(1, 10))
@@ -631,6 +833,21 @@ def testMixedArithmetic(self):
631833
self.assertTypedTupleEquals(divmod(-0.1, float('inf')), divmod(F(-1, 10), float('inf')))
632834
self.assertTypedTupleEquals(divmod(-0.1, float('-inf')), divmod(F(-1, 10), float('-inf')))
633835

836+
self.assertTypedEquals(F(3, 2) % DummyFraction(3, 5), F(3, 10))
837+
self.assertTypedEquals(DummyFraction(5, 3) % F(2, 3), F(1, 3))
838+
self.assertTypedEquals(F(3, 2) % Rat(3, 5), Rat(3, 6))
839+
self.assertTypedEquals(Rat(5, 3) % F(2, 3), F(1, 3))
840+
841+
self.assertRaises(TypeError, operator.mod, F(2, 3), Root(4))
842+
self.assertTypedEquals(Root(4) % F(3, 2), 0.5)
843+
844+
self.assertRaises(TypeError, operator.mod, F(3, 2), Polar(4, 2))
845+
self.assertRaises(TypeError, operator.mod, Rect(4, 3), F(2, 3))
846+
847+
self.assertTypedEquals(F(3, 2) % One, F(1, 2))
848+
self.assertRaises(TypeError, operator.mod, One, F(2, 3))
849+
850+
def testMixedPower(self):
634851
# ** has more interesting conversion rules.
635852
self.assertTypedEquals(F(100, 1), F(1, 10) ** -2)
636853
self.assertTypedEquals(F(100, 1), F(10, 1) ** 2)
@@ -647,6 +864,35 @@ def testMixedArithmetic(self):
647864
self.assertRaises(ZeroDivisionError, operator.pow,
648865
F(0, 1), -2)
649866

867+
self.assertTypedEquals(F(3, 2) ** Rat(3, 1), F(27, 8))
868+
self.assertTypedEquals(F(3, 2) ** Rat(-3, 1), F(8, 27))
869+
self.assertTypedEquals(F(-3, 2) ** Rat(-3, 1), F(-8, 27))
870+
self.assertTypedEquals(F(9, 4) ** Rat(3, 2), 3.375)
871+
self.assertIsInstance(F(4, 9) ** Rat(-3, 2), float)
872+
self.assertAlmostEqual(F(4, 9) ** Rat(-3, 2), 3.375)
873+
self.assertAlmostEqual(F(-4, 9) ** Rat(-3, 2), 3.375j)
874+
875+
self.assertTypedEquals(Rat(9, 4) ** F(3, 2), 3.375)
876+
self.assertTypedEquals(Rat(3, 2) ** F(3, 1), Rat(27, 8))
877+
self.assertTypedEquals(Rat(3, 2) ** F(-3, 1), F(8, 27))
878+
self.assertIsInstance(Rat(4, 9) ** F(-3, 2), float)
879+
self.assertAlmostEqual(Rat(4, 9) ** F(-3, 2), 3.375)
880+
881+
self.assertTypedEquals(Root(4) ** F(2, 3), Root(4, 3.0))
882+
self.assertTypedEquals(Root(4) ** F(2, 1), Root(4, F(1)))
883+
self.assertTypedEquals(Root(4) ** F(-2, 1), Root(4, -F(1)))
884+
self.assertTypedEquals(Root(4) ** F(-2, 3), Root(4, -3.0))
885+
886+
self.assertTypedEquals(F(3, 2) ** Rect(2, 0), Polar(2.25, 0.0))
887+
self.assertTypedEquals(F(1, 1) ** Rect(2, 3), Polar(1.0, 0.0))
888+
self.assertTypedEquals(Polar(4, 2) ** F(3, 2), Polar(8.0, 3.0))
889+
self.assertTypedEquals(Polar(4, 2) ** F(3, 1), Polar(64, 6))
890+
self.assertTypedEquals(Polar(4, 2) ** F(-3, 1), Polar(0.015625, -6))
891+
self.assertTypedEquals(Polar(4, 2) ** F(-3, 2), Polar(0.125, -3.0))
892+
893+
self.assertTypedEquals(F(3, 2) ** One, 1.5)
894+
self.assertTypedEquals(One ** F(3, 2), One)
895+
650896
def testMixingWithDecimal(self):
651897
# Decimal refuses mixed arithmetic (but not mixed comparisons)
652898
self.assertRaises(TypeError, operator.add,

0 commit comments

Comments
 (0)