File size: 5,797 Bytes
6a86ad5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"""
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)