test_pythonmpq.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. """
  2. test_pythonmpq.py
  3. Test the PythonMPQ class for consistency with gmpy2's mpq type. If gmpy2 is
  4. installed run the same tests for both.
  5. """
  6. from fractions import Fraction
  7. from decimal import Decimal
  8. import pickle
  9. from typing import Callable, List, Tuple, Type
  10. from sympy.testing.pytest import raises
  11. from sympy.external.pythonmpq import PythonMPQ
  12. #
  13. # If gmpy2 is installed then run the tests for both mpq and PythonMPQ.
  14. # That should ensure consistency between the implementation here and mpq.
  15. #
  16. rational_types: List[Tuple[Callable, Type, Callable, Type]]
  17. rational_types = [(PythonMPQ, PythonMPQ, int, int)]
  18. try:
  19. from gmpy2 import mpq, mpz
  20. rational_types.append((mpq, type(mpq(1)), mpz, type(mpz(1))))
  21. except ImportError:
  22. pass
  23. def test_PythonMPQ():
  24. #
  25. # Test PythonMPQ and also mpq if gmpy/gmpy2 is installed.
  26. #
  27. for Q, TQ, Z, TZ in rational_types:
  28. def check_Q(q):
  29. assert isinstance(q, TQ)
  30. assert isinstance(q.numerator, TZ)
  31. assert isinstance(q.denominator, TZ)
  32. return q.numerator, q.denominator
  33. # Check construction from different types
  34. assert check_Q(Q(3)) == (3, 1)
  35. assert check_Q(Q(3, 5)) == (3, 5)
  36. assert check_Q(Q(Q(3, 5))) == (3, 5)
  37. assert check_Q(Q(0.5)) == (1, 2)
  38. assert check_Q(Q('0.5')) == (1, 2)
  39. assert check_Q(Q(Fraction(3, 5))) == (3, 5)
  40. # https://github.com/aleaxit/gmpy/issues/327
  41. if Q is PythonMPQ:
  42. assert check_Q(Q(Decimal('0.6'))) == (3, 5)
  43. # Invalid types
  44. raises(TypeError, lambda: Q([]))
  45. raises(TypeError, lambda: Q([], []))
  46. # Check normalisation of signs
  47. assert check_Q(Q(2, 3)) == (2, 3)
  48. assert check_Q(Q(-2, 3)) == (-2, 3)
  49. assert check_Q(Q(2, -3)) == (-2, 3)
  50. assert check_Q(Q(-2, -3)) == (2, 3)
  51. # Check gcd calculation
  52. assert check_Q(Q(12, 8)) == (3, 2)
  53. # __int__/__float__
  54. assert int(Q(5, 3)) == 1
  55. assert int(Q(-5, 3)) == -1
  56. assert float(Q(5, 2)) == 2.5
  57. assert float(Q(-5, 2)) == -2.5
  58. # __str__/__repr__
  59. assert str(Q(2, 1)) == "2"
  60. assert str(Q(1, 2)) == "1/2"
  61. if Q is PythonMPQ:
  62. assert repr(Q(2, 1)) == "MPQ(2,1)"
  63. assert repr(Q(1, 2)) == "MPQ(1,2)"
  64. else:
  65. assert repr(Q(2, 1)) == "mpq(2,1)"
  66. assert repr(Q(1, 2)) == "mpq(1,2)"
  67. # __bool__
  68. assert bool(Q(1, 2)) is True
  69. assert bool(Q(0)) is False
  70. # __eq__/__ne__
  71. assert (Q(2, 3) == Q(2, 3)) is True
  72. assert (Q(2, 3) == Q(2, 5)) is False
  73. assert (Q(2, 3) != Q(2, 3)) is False
  74. assert (Q(2, 3) != Q(2, 5)) is True
  75. # __hash__
  76. assert hash(Q(3, 5)) == hash(Fraction(3, 5))
  77. # __reduce__
  78. q = Q(2, 3)
  79. assert pickle.loads(pickle.dumps(q)) == q
  80. # __ge__/__gt__/__le__/__lt__
  81. assert (Q(1, 3) < Q(2, 3)) is True
  82. assert (Q(2, 3) < Q(2, 3)) is False
  83. assert (Q(2, 3) < Q(1, 3)) is False
  84. assert (Q(-2, 3) < Q(1, 3)) is True
  85. assert (Q(1, 3) < Q(-2, 3)) is False
  86. assert (Q(1, 3) <= Q(2, 3)) is True
  87. assert (Q(2, 3) <= Q(2, 3)) is True
  88. assert (Q(2, 3) <= Q(1, 3)) is False
  89. assert (Q(-2, 3) <= Q(1, 3)) is True
  90. assert (Q(1, 3) <= Q(-2, 3)) is False
  91. assert (Q(1, 3) > Q(2, 3)) is False
  92. assert (Q(2, 3) > Q(2, 3)) is False
  93. assert (Q(2, 3) > Q(1, 3)) is True
  94. assert (Q(-2, 3) > Q(1, 3)) is False
  95. assert (Q(1, 3) > Q(-2, 3)) is True
  96. assert (Q(1, 3) >= Q(2, 3)) is False
  97. assert (Q(2, 3) >= Q(2, 3)) is True
  98. assert (Q(2, 3) >= Q(1, 3)) is True
  99. assert (Q(-2, 3) >= Q(1, 3)) is False
  100. assert (Q(1, 3) >= Q(-2, 3)) is True
  101. # __abs__/__pos__/__neg__
  102. assert abs(Q(2, 3)) == abs(Q(-2, 3)) == Q(2, 3)
  103. assert +Q(2, 3) == Q(2, 3)
  104. assert -Q(2, 3) == Q(-2, 3)
  105. # __add__/__radd__
  106. assert Q(2, 3) + Q(5, 7) == Q(29, 21)
  107. assert Q(2, 3) + 1 == Q(5, 3)
  108. assert 1 + Q(2, 3) == Q(5, 3)
  109. raises(TypeError, lambda: [] + Q(1))
  110. raises(TypeError, lambda: Q(1) + [])
  111. # __sub__/__rsub__
  112. assert Q(2, 3) - Q(5, 7) == Q(-1, 21)
  113. assert Q(2, 3) - 1 == Q(-1, 3)
  114. assert 1 - Q(2, 3) == Q(1, 3)
  115. raises(TypeError, lambda: [] - Q(1))
  116. raises(TypeError, lambda: Q(1) - [])
  117. # __mul__/__rmul__
  118. assert Q(2, 3) * Q(5, 7) == Q(10, 21)
  119. assert Q(2, 3) * 1 == Q(2, 3)
  120. assert 1 * Q(2, 3) == Q(2, 3)
  121. raises(TypeError, lambda: [] * Q(1))
  122. raises(TypeError, lambda: Q(1) * [])
  123. # __pow__/__rpow__
  124. assert Q(2, 3) ** 2 == Q(4, 9)
  125. assert Q(2, 3) ** 1 == Q(2, 3)
  126. assert Q(-2, 3) ** 2 == Q(4, 9)
  127. assert Q(-2, 3) ** -1 == Q(-3, 2)
  128. if Q is PythonMPQ:
  129. raises(TypeError, lambda: 1 ** Q(2, 3))
  130. raises(TypeError, lambda: Q(1, 4) ** Q(1, 2))
  131. raises(TypeError, lambda: [] ** Q(1))
  132. raises(TypeError, lambda: Q(1) ** [])
  133. # __div__/__rdiv__
  134. assert Q(2, 3) / Q(5, 7) == Q(14, 15)
  135. assert Q(2, 3) / 1 == Q(2, 3)
  136. assert 1 / Q(2, 3) == Q(3, 2)
  137. raises(TypeError, lambda: [] / Q(1))
  138. raises(TypeError, lambda: Q(1) / [])
  139. raises(ZeroDivisionError, lambda: Q(1, 2) / Q(0))
  140. # __divmod__
  141. if Q is PythonMPQ:
  142. raises(TypeError, lambda: Q(2, 3) // Q(1, 3))
  143. raises(TypeError, lambda: Q(2, 3) % Q(1, 3))
  144. raises(TypeError, lambda: 1 // Q(1, 3))
  145. raises(TypeError, lambda: 1 % Q(1, 3))
  146. raises(TypeError, lambda: Q(2, 3) // 1)
  147. raises(TypeError, lambda: Q(2, 3) % 1)