FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[3.12] gh-119189: Add more tests for mixed Fraction arithmetic (GH-119236) by miss-islington · Pull Request #119256 · python/cpython · GitHub

/ cpython Public
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (1) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
263 changes: 263 additions & 0 deletions Lib/test/test_fractions.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for Lib/fractions.py."""

import cmath
from decimal import Decimal
from test.support import requires_IEEE_754
import math
Expand Down Expand Up @@ -91,6 +92,187 @@ class DummyFraction(fractions.Fraction):
def _components(r):
return (r.numerator, r.denominator)

def typed_approx_eq(a, b):
return type(a) == type(b) and (a == b or math.isclose(a, b))

class Symbolic:
"""Simple non-numeric class for testing mixed arithmetic.
It is not Integral, Rational, Real or Complex, and cannot be conveted
to int, float or complex. but it supports some arithmetic operations.
"""
def __init__(self, value):
self.value = value
def __mul__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(f'{self} * {other}')
def __rmul__(self, other):
return self.__class__(f'{other} * {self}')
def __truediv__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(f'{self} / {other}')
def __rtruediv__(self, other):
return self.__class__(f'{other} / {self}')
def __mod__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(f'{self} % {other}')
def __rmod__(self, other):
return self.__class__(f'{other} % {self}')
def __pow__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(f'{self} ** {other}')
def __rpow__(self, other):
return self.__class__(f'{other} ** {self}')
def __eq__(self, other):
if other.__class__ != self.__class__:
return NotImplemented
return self.value == other.value
def __str__(self):
return f'{self.value}'
def __repr__(self):
return f'{self.__class__.__name__}({self.value!r})'

class Rat:
"""Simple Rational class for testing mixed arithmetic."""
def __init__(self, n, d):
self.numerator = n
self.denominator = d
def __mul__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.numerator * other.numerator,
self.denominator * other.denominator)
def __rmul__(self, other):
return self.__class__(other.numerator * self.numerator,
other.denominator * self.denominator)
def __truediv__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.numerator * other.denominator,
self.denominator * other.numerator)
def __rtruediv__(self, other):
return self.__class__(other.numerator * self.denominator,
other.denominator * self.numerator)
def __mod__(self, other):
if isinstance(other, F):
return NotImplemented
d = self.denominator * other.numerator
return self.__class__(self.numerator * other.denominator % d, d)
def __rmod__(self, other):
d = other.denominator * self.numerator
return self.__class__(other.numerator * self.denominator % d, d)

return self.__class__(other.numerator / self.numerator,
other.denominator / self.denominator)
def __pow__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.numerator ** other,
self.denominator ** other)
def __float__(self):
return self.numerator / self.denominator
def __eq__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return (typed_approx_eq(self.numerator, other.numerator) and
typed_approx_eq(self.denominator, other.denominator))
def __repr__(self):
return f'{self.__class__.__name__}({self.numerator!r}, {self.denominator!r})'
numbers.Rational.register(Rat)

class Root:
"""Simple Real class for testing mixed arithmetic."""
def __init__(self, v, n=F(2)):
self.base = v
self.degree = n
def __mul__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.base * other**self.degree, self.degree)
def __rmul__(self, other):
return self.__class__(other**self.degree * self.base, self.degree)
def __truediv__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.base / other**self.degree, self.degree)
def __rtruediv__(self, other):
return self.__class__(other**self.degree / self.base, self.degree)
def __pow__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.base, self.degree / other)
def __float__(self):
return float(self.base) ** (1 / float(self.degree))
def __eq__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return typed_approx_eq(self.base, other.base) and typed_approx_eq(self.degree, other.degree)
def __repr__(self):
return f'{self.__class__.__name__}({self.base!r}, {self.degree!r})'
numbers.Real.register(Root)

class Polar:
"""Simple Complex class for testing mixed arithmetic."""
def __init__(self, r, phi):
self.r = r
self.phi = phi
def __mul__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.r * other, self.phi)
def __rmul__(self, other):
return self.__class__(other * self.r, self.phi)
def __truediv__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.r / other, self.phi)
def __rtruediv__(self, other):
return self.__class__(other / self.r, -self.phi)
def __pow__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.r ** other, self.phi * other)
def __eq__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return typed_approx_eq(self.r, other.r) and typed_approx_eq(self.phi, other.phi)
def __repr__(self):
return f'{self.__class__.__name__}({self.r!r}, {self.phi!r})'
numbers.Complex.register(Polar)

class Rect:
"""Other simple Complex class for testing mixed arithmetic."""
def __init__(self, x, y):
self.x = x
self.y = y
def __mul__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.x * other, self.y * other)
def __rmul__(self, other):
return self.__class__(other * self.x, other * self.y)
def __truediv__(self, other):
if isinstance(other, F):
return NotImplemented
return self.__class__(self.x / other, self.y / other)
def __rtruediv__(self, other):
r = self.x * self.x + self.y * self.y
return self.__class__(other * (self.x / r), other * (self.y / r))
def __rpow__(self, other):
return Polar(other ** self.x, math.log(other) * self.y)
def __complex__(self):
return complex(self.x, self.y)
def __eq__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return typed_approx_eq(self.x, other.x) and typed_approx_eq(self.y, other.y)
def __repr__(self):
return f'{self.__class__.__name__}({self.x!r}, {self.y!r})'
numbers.Complex.register(Rect)


class FractionTest(unittest.TestCase):

Expand Down Expand Up @@ -593,20 +775,57 @@ def testMixedArithmetic(self):
self.assertTypedEquals(0.9, 1.0 - F(1, 10))
self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - F(1, 10))

def testMixedMultiplication(self):
self.assertTypedEquals(F(1, 10), F(1, 10) * 1)
self.assertTypedEquals(0.1, F(1, 10) * 1.0)
self.assertTypedEquals(0.1 + 0j, F(1, 10) * (1.0 + 0j))
self.assertTypedEquals(F(1, 10), 1 * F(1, 10))
self.assertTypedEquals(0.1, 1.0 * F(1, 10))
self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * F(1, 10))

self.assertTypedEquals(F(3, 2) * DummyFraction(5, 3), F(5, 2))
self.assertTypedEquals(DummyFraction(5, 3) * F(3, 2), F(5, 2))
self.assertTypedEquals(F(3, 2) * Rat(5, 3), Rat(15, 6))
self.assertTypedEquals(Rat(5, 3) * F(3, 2), F(5, 2))

self.assertTypedEquals(F(3, 2) * Root(4), Root(F(9, 1)))
self.assertTypedEquals(Root(4) * F(3, 2), 3.0)

self.assertTypedEquals(F(3, 2) * Polar(4, 2), Polar(F(6, 1), 2))
self.assertTypedEquals(F(3, 2) * Polar(4.0, 2), Polar(6.0, 2))
self.assertTypedEquals(F(3, 2) * Rect(4, 3), Rect(F(6, 1), F(9, 2)))
self.assertRaises(TypeError, operator.mul, Polar(4, 2), F(3, 2))
self.assertTypedEquals(Rect(4, 3) * F(3, 2), 6.0 + 4.5j)

self.assertEqual(F(3, 2) * Symbolic('X'), Symbolic('3/2 * X'))
self.assertRaises(TypeError, operator.mul, Symbolic('X'), F(3, 2))

def testMixedDivision(self):
self.assertTypedEquals(F(1, 10), F(1, 10) / 1)
self.assertTypedEquals(0.1, F(1, 10) / 1.0)
self.assertTypedEquals(0.1 + 0j, F(1, 10) / (1.0 + 0j))
self.assertTypedEquals(F(10, 1), 1 / F(1, 10))
self.assertTypedEquals(10.0, 1.0 / F(1, 10))
self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / F(1, 10))

self.assertTypedEquals(F(3, 2) / DummyFraction(3, 5), F(5, 2))
self.assertTypedEquals(DummyFraction(5, 3) / F(2, 3), F(5, 2))
self.assertTypedEquals(F(3, 2) / Rat(3, 5), Rat(15, 6))
self.assertTypedEquals(Rat(5, 3) / F(2, 3), F(5, 2))

self.assertTypedEquals(F(2, 3) / Root(4), Root(F(1, 9)))
self.assertTypedEquals(Root(4) / F(2, 3), 3.0)

self.assertTypedEquals(F(3, 2) / Polar(4, 2), Polar(F(3, 8), -2))
self.assertTypedEquals(F(3, 2) / Polar(4.0, 2), Polar(0.375, -2))
self.assertTypedEquals(F(3, 2) / Rect(4, 3), Rect(0.24, 0.18))
self.assertRaises(TypeError, operator.truediv, Polar(4, 2), F(2, 3))
self.assertTypedEquals(Rect(4, 3) / F(2, 3), 6.0 + 4.5j)

self.assertEqual(F(3, 2) / Symbolic('X'), Symbolic('3/2 / X'))
self.assertRaises(TypeError, operator.truediv, Symbolic('X'), F(2, 3))

def testMixedIntegerDivision(self):
self.assertTypedEquals(0, F(1, 10) // 1)
self.assertTypedEquals(0.0, F(1, 10) // 1.0)
self.assertTypedEquals(10, 1 // F(1, 10))
Expand All @@ -631,6 +850,21 @@ def testMixedArithmetic(self):
self.assertTypedTupleEquals(divmod(-0.1, float('inf')), divmod(F(-1, 10), float('inf')))
self.assertTypedTupleEquals(divmod(-0.1, float('-inf')), divmod(F(-1, 10), float('-inf')))

self.assertTypedEquals(F(3, 2) % DummyFraction(3, 5), F(3, 10))
self.assertTypedEquals(DummyFraction(5, 3) % F(2, 3), F(1, 3))
self.assertTypedEquals(F(3, 2) % Rat(3, 5), Rat(3, 6))
self.assertTypedEquals(Rat(5, 3) % F(2, 3), F(1, 3))

self.assertRaises(TypeError, operator.mod, F(2, 3), Root(4))
self.assertTypedEquals(Root(4) % F(3, 2), 0.5)

self.assertRaises(TypeError, operator.mod, F(3, 2), Polar(4, 2))
self.assertRaises(TypeError, operator.mod, Rect(4, 3), F(2, 3))

self.assertEqual(F(3, 2) % Symbolic('X'), Symbolic('3/2 % X'))
self.assertRaises(TypeError, operator.mod, Symbolic('X'), F(2, 3))

def testMixedPower(self):
# ** has more interesting conversion rules.
self.assertTypedEquals(F(100, 1), F(1, 10) ** -2)
self.assertTypedEquals(F(100, 1), F(10, 1) ** 2)
Expand All @@ -647,6 +881,35 @@ def testMixedArithmetic(self):
self.assertRaises(ZeroDivisionError, operator.pow,
F(0, 1), -2)

self.assertTypedEquals(F(3, 2) ** Rat(3, 1), F(27, 8))
self.assertTypedEquals(F(3, 2) ** Rat(-3, 1), F(8, 27))
self.assertTypedEquals(F(-3, 2) ** Rat(-3, 1), F(-8, 27))
self.assertTypedEquals(F(9, 4) ** Rat(3, 2), 3.375)
self.assertIsInstance(F(4, 9) ** Rat(-3, 2), float)
self.assertAlmostEqual(F(4, 9) ** Rat(-3, 2), 3.375)
self.assertAlmostEqual(F(-4, 9) ** Rat(-3, 2), 3.375j)

self.assertTypedEquals(Rat(9, 4) ** F(3, 2), 3.375)
self.assertTypedEquals(Rat(3, 2) ** F(3, 1), Rat(27, 8))
self.assertTypedEquals(Rat(3, 2) ** F(-3, 1), F(8, 27))
self.assertIsInstance(Rat(4, 9) ** F(-3, 2), float)
self.assertAlmostEqual(Rat(4, 9) ** F(-3, 2), 3.375)

self.assertTypedEquals(Root(4) ** F(2, 3), Root(4, 3.0))
self.assertTypedEquals(Root(4) ** F(2, 1), Root(4, F(1)))
self.assertTypedEquals(Root(4) ** F(-2, 1), Root(4, -F(1)))
self.assertTypedEquals(Root(4) ** F(-2, 3), Root(4, -3.0))

self.assertTypedEquals(F(3, 2) ** Rect(2, 0), Polar(2.25, 0.0))
self.assertTypedEquals(F(1, 1) ** Rect(2, 3), Polar(1.0, 0.0))
self.assertTypedEquals(Polar(4, 2) ** F(3, 2), Polar(8.0, 3.0))
self.assertTypedEquals(Polar(4, 2) ** F(3, 1), Polar(64, 6))
self.assertTypedEquals(Polar(4, 2) ** F(-3, 1), Polar(0.015625, -6))
self.assertTypedEquals(Polar(4, 2) ** F(-3, 2), Polar(0.125, -3.0))

self.assertTypedEquals(F(3, 2) ** Symbolic('X'), Symbolic('1.5 ** X'))
self.assertTypedEquals(Symbolic('X') ** F(3, 2), Symbolic('X ** 1.5'))

def testMixingWithDecimal(self):
# Decimal refuses mixed arithmetic (but not mixed comparisons)
self.assertRaises(TypeError, operator.add,
Expand Down

Back | FazBrowse Home | New Git URL