177 lines
5.7 KiB
Python
177 lines
5.7 KiB
Python
"""
|
|
test_pythonmpq.py
|
|
|
|
Test the PythonMPQ class for consistency with gmpy2's mpq type. If gmpy2 is
|
|
installed run the same tests for both.
|
|
"""
|
|
from fractions import Fraction
|
|
from decimal import Decimal
|
|
import pickle
|
|
from typing import Callable, List, Tuple, Type
|
|
|
|
from sympy.testing.pytest import raises
|
|
|
|
from sympy.external.pythonmpq import PythonMPQ
|
|
|
|
#
|
|
# If gmpy2 is installed then run the tests for both mpq and PythonMPQ.
|
|
# That should ensure consistency between the implementation here and mpq.
|
|
#
|
|
rational_types: List[Tuple[Callable, Type, Callable, Type]]
|
|
rational_types = [(PythonMPQ, PythonMPQ, int, int)]
|
|
try:
|
|
from gmpy2 import mpq, mpz
|
|
rational_types.append((mpq, type(mpq(1)), mpz, type(mpz(1))))
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def test_PythonMPQ():
|
|
#
|
|
# Test PythonMPQ and also mpq if gmpy/gmpy2 is installed.
|
|
#
|
|
for Q, TQ, Z, TZ in rational_types:
|
|
|
|
def check_Q(q):
|
|
assert isinstance(q, TQ)
|
|
assert isinstance(q.numerator, TZ)
|
|
assert isinstance(q.denominator, TZ)
|
|
return q.numerator, q.denominator
|
|
|
|
# Check construction from different types
|
|
assert check_Q(Q(3)) == (3, 1)
|
|
assert check_Q(Q(3, 5)) == (3, 5)
|
|
assert check_Q(Q(Q(3, 5))) == (3, 5)
|
|
assert check_Q(Q(0.5)) == (1, 2)
|
|
assert check_Q(Q('0.5')) == (1, 2)
|
|
assert check_Q(Q(Fraction(3, 5))) == (3, 5)
|
|
|
|
# https://github.com/aleaxit/gmpy/issues/327
|
|
if Q is PythonMPQ:
|
|
assert check_Q(Q(Decimal('0.6'))) == (3, 5)
|
|
|
|
# Invalid types
|
|
raises(TypeError, lambda: Q([]))
|
|
raises(TypeError, lambda: Q([], []))
|
|
|
|
# Check normalisation of signs
|
|
assert check_Q(Q(2, 3)) == (2, 3)
|
|
assert check_Q(Q(-2, 3)) == (-2, 3)
|
|
assert check_Q(Q(2, -3)) == (-2, 3)
|
|
assert check_Q(Q(-2, -3)) == (2, 3)
|
|
|
|
# Check gcd calculation
|
|
assert check_Q(Q(12, 8)) == (3, 2)
|
|
|
|
# __int__/__float__
|
|
assert int(Q(5, 3)) == 1
|
|
assert int(Q(-5, 3)) == -1
|
|
assert float(Q(5, 2)) == 2.5
|
|
assert float(Q(-5, 2)) == -2.5
|
|
|
|
# __str__/__repr__
|
|
assert str(Q(2, 1)) == "2"
|
|
assert str(Q(1, 2)) == "1/2"
|
|
if Q is PythonMPQ:
|
|
assert repr(Q(2, 1)) == "MPQ(2,1)"
|
|
assert repr(Q(1, 2)) == "MPQ(1,2)"
|
|
else:
|
|
assert repr(Q(2, 1)) == "mpq(2,1)"
|
|
assert repr(Q(1, 2)) == "mpq(1,2)"
|
|
|
|
# __bool__
|
|
assert bool(Q(1, 2)) is True
|
|
assert bool(Q(0)) is False
|
|
|
|
# __eq__/__ne__
|
|
assert (Q(2, 3) == Q(2, 3)) is True
|
|
assert (Q(2, 3) == Q(2, 5)) is False
|
|
assert (Q(2, 3) != Q(2, 3)) is False
|
|
assert (Q(2, 3) != Q(2, 5)) is True
|
|
|
|
# __hash__
|
|
assert hash(Q(3, 5)) == hash(Fraction(3, 5))
|
|
|
|
# __reduce__
|
|
q = Q(2, 3)
|
|
assert pickle.loads(pickle.dumps(q)) == q
|
|
|
|
# __ge__/__gt__/__le__/__lt__
|
|
assert (Q(1, 3) < Q(2, 3)) is True
|
|
assert (Q(2, 3) < Q(2, 3)) is False
|
|
assert (Q(2, 3) < Q(1, 3)) is False
|
|
assert (Q(-2, 3) < Q(1, 3)) is True
|
|
assert (Q(1, 3) < Q(-2, 3)) is False
|
|
|
|
assert (Q(1, 3) <= Q(2, 3)) is True
|
|
assert (Q(2, 3) <= Q(2, 3)) is True
|
|
assert (Q(2, 3) <= Q(1, 3)) is False
|
|
assert (Q(-2, 3) <= Q(1, 3)) is True
|
|
assert (Q(1, 3) <= Q(-2, 3)) is False
|
|
|
|
assert (Q(1, 3) > Q(2, 3)) is False
|
|
assert (Q(2, 3) > Q(2, 3)) is False
|
|
assert (Q(2, 3) > Q(1, 3)) is True
|
|
assert (Q(-2, 3) > Q(1, 3)) is False
|
|
assert (Q(1, 3) > Q(-2, 3)) is True
|
|
|
|
assert (Q(1, 3) >= Q(2, 3)) is False
|
|
assert (Q(2, 3) >= Q(2, 3)) is True
|
|
assert (Q(2, 3) >= Q(1, 3)) is True
|
|
assert (Q(-2, 3) >= Q(1, 3)) is False
|
|
assert (Q(1, 3) >= Q(-2, 3)) is True
|
|
|
|
# __abs__/__pos__/__neg__
|
|
assert abs(Q(2, 3)) == abs(Q(-2, 3)) == Q(2, 3)
|
|
assert +Q(2, 3) == Q(2, 3)
|
|
assert -Q(2, 3) == Q(-2, 3)
|
|
|
|
# __add__/__radd__
|
|
assert Q(2, 3) + Q(5, 7) == Q(29, 21)
|
|
assert Q(2, 3) + 1 == Q(5, 3)
|
|
assert 1 + Q(2, 3) == Q(5, 3)
|
|
raises(TypeError, lambda: [] + Q(1))
|
|
raises(TypeError, lambda: Q(1) + [])
|
|
|
|
# __sub__/__rsub__
|
|
assert Q(2, 3) - Q(5, 7) == Q(-1, 21)
|
|
assert Q(2, 3) - 1 == Q(-1, 3)
|
|
assert 1 - Q(2, 3) == Q(1, 3)
|
|
raises(TypeError, lambda: [] - Q(1))
|
|
raises(TypeError, lambda: Q(1) - [])
|
|
|
|
# __mul__/__rmul__
|
|
assert Q(2, 3) * Q(5, 7) == Q(10, 21)
|
|
assert Q(2, 3) * 1 == Q(2, 3)
|
|
assert 1 * Q(2, 3) == Q(2, 3)
|
|
raises(TypeError, lambda: [] * Q(1))
|
|
raises(TypeError, lambda: Q(1) * [])
|
|
|
|
# __pow__/__rpow__
|
|
assert Q(2, 3) ** 2 == Q(4, 9)
|
|
assert Q(2, 3) ** 1 == Q(2, 3)
|
|
assert Q(-2, 3) ** 2 == Q(4, 9)
|
|
assert Q(-2, 3) ** -1 == Q(-3, 2)
|
|
if Q is PythonMPQ:
|
|
raises(TypeError, lambda: 1 ** Q(2, 3))
|
|
raises(TypeError, lambda: Q(1, 4) ** Q(1, 2))
|
|
raises(TypeError, lambda: [] ** Q(1))
|
|
raises(TypeError, lambda: Q(1) ** [])
|
|
|
|
# __div__/__rdiv__
|
|
assert Q(2, 3) / Q(5, 7) == Q(14, 15)
|
|
assert Q(2, 3) / 1 == Q(2, 3)
|
|
assert 1 / Q(2, 3) == Q(3, 2)
|
|
raises(TypeError, lambda: [] / Q(1))
|
|
raises(TypeError, lambda: Q(1) / [])
|
|
raises(ZeroDivisionError, lambda: Q(1, 2) / Q(0))
|
|
|
|
# __divmod__
|
|
if Q is PythonMPQ:
|
|
raises(TypeError, lambda: Q(2, 3) // Q(1, 3))
|
|
raises(TypeError, lambda: Q(2, 3) % Q(1, 3))
|
|
raises(TypeError, lambda: 1 // Q(1, 3))
|
|
raises(TypeError, lambda: 1 % Q(1, 3))
|
|
raises(TypeError, lambda: Q(2, 3) // 1)
|
|
raises(TypeError, lambda: Q(2, 3) % 1)
|