blob: 523ecbe2989b99da5d1db391ec6b0d1bedd6f693 [file] [log] [blame]
Guido van Rossum64deef22001-08-08 22:27:20 +00001"""Tests for binary operators on subtypes of built-in types."""
2
Guido van Rossum64deef22001-08-08 22:27:20 +00003import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00004from test import support
Guido van Rossum64deef22001-08-08 22:27:20 +00005
Guido van Rossum64deef22001-08-08 22:27:20 +00006def gcd(a, b):
7 """Greatest common divisor using Euclid's algorithm."""
8 while a:
9 a, b = b%a, a
10 return b
11
12def isint(x):
13 """Test whether an object is an instance of int or long."""
Guido van Rossume2a383d2007-01-15 16:59:06 +000014 return isinstance(x, int) or isinstance(x, int)
Guido van Rossum64deef22001-08-08 22:27:20 +000015
16def isnum(x):
17 """Test whether an object is an instance of a built-in numeric type."""
Florent Xiclunab4efb3d2010-08-14 18:24:40 +000018 for T in int, float, complex:
Guido van Rossum64deef22001-08-08 22:27:20 +000019 if isinstance(x, T):
20 return 1
21 return 0
22
23def isRat(x):
24 """Test wheter an object is an instance of the Rat class."""
25 return isinstance(x, Rat)
26
27class Rat(object):
28
29 """Rational number implemented as a normalized pair of longs."""
30
31 __slots__ = ['_Rat__num', '_Rat__den']
32
Guido van Rossume2a383d2007-01-15 16:59:06 +000033 def __init__(self, num=0, den=1):
Guido van Rossum64deef22001-08-08 22:27:20 +000034 """Constructor: Rat([num[, den]]).
35
36 The arguments must be ints or longs, and default to (0, 1)."""
37 if not isint(num):
Collin Winter3add4d72007-08-29 23:37:32 +000038 raise TypeError("Rat numerator must be int or long (%r)" % num)
Guido van Rossum64deef22001-08-08 22:27:20 +000039 if not isint(den):
Collin Winter3add4d72007-08-29 23:37:32 +000040 raise TypeError("Rat denominator must be int or long (%r)" % den)
Guido van Rossum64deef22001-08-08 22:27:20 +000041 # But the zero is always on
42 if den == 0:
Collin Winter3add4d72007-08-29 23:37:32 +000043 raise ZeroDivisionError("zero denominator")
Guido van Rossum64deef22001-08-08 22:27:20 +000044 g = gcd(den, num)
Guido van Rossume2a383d2007-01-15 16:59:06 +000045 self.__num = int(num//g)
46 self.__den = int(den//g)
Guido van Rossum64deef22001-08-08 22:27:20 +000047
48 def _get_num(self):
49 """Accessor function for read-only 'num' attribute of Rat."""
50 return self.__num
Guido van Rossum8bce4ac2001-09-06 21:56:42 +000051 num = property(_get_num, None)
Guido van Rossum64deef22001-08-08 22:27:20 +000052
53 def _get_den(self):
54 """Accessor function for read-only 'den' attribute of Rat."""
55 return self.__den
Guido van Rossum8bce4ac2001-09-06 21:56:42 +000056 den = property(_get_den, None)
Guido van Rossum64deef22001-08-08 22:27:20 +000057
58 def __repr__(self):
59 """Convert a Rat to an string resembling a Rat constructor call."""
60 return "Rat(%d, %d)" % (self.__num, self.__den)
61
62 def __str__(self):
63 """Convert a Rat to a string resembling a decimal numeric value."""
64 return str(float(self))
65
66 def __float__(self):
67 """Convert a Rat to a float."""
68 return self.__num*1.0/self.__den
69
70 def __int__(self):
71 """Convert a Rat to an int; self.den must be 1."""
72 if self.__den == 1:
73 try:
74 return int(self.__num)
75 except OverflowError:
Collin Winter3add4d72007-08-29 23:37:32 +000076 raise OverflowError("%s too large to convert to int" %
Guido van Rossum64deef22001-08-08 22:27:20 +000077 repr(self))
Collin Winter3add4d72007-08-29 23:37:32 +000078 raise ValueError("can't convert %s to int" % repr(self))
Guido van Rossum64deef22001-08-08 22:27:20 +000079
Guido van Rossum64deef22001-08-08 22:27:20 +000080 def __add__(self, other):
81 """Add two Rats, or a Rat and a number."""
82 if isint(other):
83 other = Rat(other)
84 if isRat(other):
85 return Rat(self.__num*other.__den + other.__num*self.__den,
86 self.__den*other.__den)
87 if isnum(other):
88 return float(self) + other
89 return NotImplemented
90
91 __radd__ = __add__
92
93 def __sub__(self, other):
94 """Subtract two Rats, or a Rat and a number."""
95 if isint(other):
96 other = Rat(other)
97 if isRat(other):
98 return Rat(self.__num*other.__den - other.__num*self.__den,
99 self.__den*other.__den)
100 if isnum(other):
101 return float(self) - other
102 return NotImplemented
103
104 def __rsub__(self, other):
105 """Subtract two Rats, or a Rat and a number (reversed args)."""
106 if isint(other):
107 other = Rat(other)
108 if isRat(other):
109 return Rat(other.__num*self.__den - self.__num*other.__den,
110 self.__den*other.__den)
111 if isnum(other):
112 return other - float(self)
113 return NotImplemented
114
115 def __mul__(self, other):
116 """Multiply two Rats, or a Rat and a number."""
117 if isRat(other):
118 return Rat(self.__num*other.__num, self.__den*other.__den)
119 if isint(other):
120 return Rat(self.__num*other, self.__den)
121 if isnum(other):
122 return float(self)*other
123 return NotImplemented
124
125 __rmul__ = __mul__
126
127 def __truediv__(self, other):
128 """Divide two Rats, or a Rat and a number."""
129 if isRat(other):
130 return Rat(self.__num*other.__den, self.__den*other.__num)
131 if isint(other):
132 return Rat(self.__num, self.__den*other)
133 if isnum(other):
134 return float(self) / other
135 return NotImplemented
136
Guido van Rossum64deef22001-08-08 22:27:20 +0000137 def __rtruediv__(self, other):
138 """Divide two Rats, or a Rat and a number (reversed args)."""
139 if isRat(other):
140 return Rat(other.__num*self.__den, other.__den*self.__num)
141 if isint(other):
142 return Rat(other*self.__den, self.__num)
143 if isnum(other):
144 return other / float(self)
145 return NotImplemented
146
Guido van Rossum64deef22001-08-08 22:27:20 +0000147 def __floordiv__(self, other):
148 """Divide two Rats, returning the floored result."""
149 if isint(other):
150 other = Rat(other)
151 elif not isRat(other):
152 return NotImplemented
153 x = self/other
154 return x.__num // x.__den
155
156 def __rfloordiv__(self, other):
157 """Divide two Rats, returning the floored result (reversed args)."""
158 x = other/self
159 return x.__num // x.__den
160
161 def __divmod__(self, other):
162 """Divide two Rats, returning quotient and remainder."""
163 if isint(other):
164 other = Rat(other)
165 elif not isRat(other):
166 return NotImplemented
167 x = self//other
168 return (x, self - other * x)
169
170 def __rdivmod__(self, other):
Brett Cannon53e9a8b2005-04-30 05:50:19 +0000171 """Divide two Rats, returning quotient and remainder (reversed args)."""
Guido van Rossum64deef22001-08-08 22:27:20 +0000172 if isint(other):
173 other = Rat(other)
174 elif not isRat(other):
175 return NotImplemented
176 return divmod(other, self)
177
178 def __mod__(self, other):
179 """Take one Rat modulo another."""
180 return divmod(self, other)[1]
181
182 def __rmod__(self, other):
183 """Take one Rat modulo another (reversed args)."""
184 return divmod(other, self)[1]
185
186 def __eq__(self, other):
187 """Compare two Rats for equality."""
188 if isint(other):
189 return self.__den == 1 and self.__num == other
190 if isRat(other):
191 return self.__num == other.__num and self.__den == other.__den
192 if isnum(other):
193 return float(self) == other
194 return NotImplemented
195
196 def __ne__(self, other):
197 """Compare two Rats for inequality."""
198 return not self == other
199
200class RatTestCase(unittest.TestCase):
201 """Unit tests for Rat class and its support utilities."""
202
203 def test_gcd(self):
204 self.assertEqual(gcd(10, 12), 2)
205 self.assertEqual(gcd(10, 15), 5)
206 self.assertEqual(gcd(10, 11), 1)
207 self.assertEqual(gcd(100, 15), 5)
208 self.assertEqual(gcd(-10, 2), -2)
209 self.assertEqual(gcd(10, -2), 2)
210 self.assertEqual(gcd(-10, -2), -2)
211 for i in range(1, 20):
212 for j in range(1, 20):
Georg Brandlab91fde2009-08-13 08:51:18 +0000213 self.assertTrue(gcd(i, j) > 0)
214 self.assertTrue(gcd(-i, j) < 0)
215 self.assertTrue(gcd(i, -j) > 0)
216 self.assertTrue(gcd(-i, -j) < 0)
Guido van Rossum64deef22001-08-08 22:27:20 +0000217
218 def test_constructor(self):
219 a = Rat(10, 15)
220 self.assertEqual(a.num, 2)
221 self.assertEqual(a.den, 3)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000222 a = Rat(10, 15)
Guido van Rossum64deef22001-08-08 22:27:20 +0000223 self.assertEqual(a.num, 2)
224 self.assertEqual(a.den, 3)
225 a = Rat(10, -15)
226 self.assertEqual(a.num, -2)
227 self.assertEqual(a.den, 3)
228 a = Rat(-10, 15)
229 self.assertEqual(a.num, -2)
230 self.assertEqual(a.den, 3)
231 a = Rat(-10, -15)
232 self.assertEqual(a.num, 2)
233 self.assertEqual(a.den, 3)
234 a = Rat(7)
235 self.assertEqual(a.num, 7)
236 self.assertEqual(a.den, 1)
237 try:
238 a = Rat(1, 0)
239 except ZeroDivisionError:
240 pass
241 else:
242 self.fail("Rat(1, 0) didn't raise ZeroDivisionError")
243 for bad in "0", 0.0, 0j, (), [], {}, None, Rat, unittest:
244 try:
245 a = Rat(bad)
246 except TypeError:
247 pass
248 else:
249 self.fail("Rat(%r) didn't raise TypeError" % bad)
250 try:
251 a = Rat(1, bad)
252 except TypeError:
253 pass
254 else:
255 self.fail("Rat(1, %r) didn't raise TypeError" % bad)
256
257 def test_add(self):
258 self.assertEqual(Rat(2, 3) + Rat(1, 3), 1)
259 self.assertEqual(Rat(2, 3) + 1, Rat(5, 3))
260 self.assertEqual(1 + Rat(2, 3), Rat(5, 3))
261 self.assertEqual(1.0 + Rat(1, 2), 1.5)
262 self.assertEqual(Rat(1, 2) + 1.0, 1.5)
263
264 def test_sub(self):
265 self.assertEqual(Rat(7, 2) - Rat(7, 5), Rat(21, 10))
266 self.assertEqual(Rat(7, 5) - 1, Rat(2, 5))
267 self.assertEqual(1 - Rat(3, 5), Rat(2, 5))
268 self.assertEqual(Rat(3, 2) - 1.0, 0.5)
269 self.assertEqual(1.0 - Rat(1, 2), 0.5)
270
271 def test_mul(self):
272 self.assertEqual(Rat(2, 3) * Rat(5, 7), Rat(10, 21))
273 self.assertEqual(Rat(10, 3) * 3, 10)
274 self.assertEqual(3 * Rat(10, 3), 10)
275 self.assertEqual(Rat(10, 5) * 0.5, 1.0)
276 self.assertEqual(0.5 * Rat(10, 5), 1.0)
277
278 def test_div(self):
279 self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
280 self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
281 self.assertEqual(2 / Rat(5), Rat(2, 5))
282 self.assertEqual(3.0 * Rat(1, 2), 1.5)
283 self.assertEqual(Rat(1, 2) * 3.0, 1.5)
284
285 def test_floordiv(self):
286 self.assertEqual(Rat(10) // Rat(4), 2)
287 self.assertEqual(Rat(10, 3) // Rat(4, 3), 2)
288 self.assertEqual(Rat(10) // 4, 2)
289 self.assertEqual(10 // Rat(4), 2)
290
291 def test_eq(self):
292 self.assertEqual(Rat(10), Rat(20, 2))
293 self.assertEqual(Rat(10), 10)
294 self.assertEqual(10, Rat(10))
295 self.assertEqual(Rat(10), 10.0)
296 self.assertEqual(10.0, Rat(10))
297
Benjamin Peterson42806ba2008-06-28 23:34:00 +0000298 def test_true_div(self):
299 self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
300 self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
301 self.assertEqual(2 / Rat(5), Rat(2, 5))
302 self.assertEqual(3.0 * Rat(1, 2), 1.5)
303 self.assertEqual(Rat(1, 2) * 3.0, 1.5)
304 self.assertEqual(eval('1/2'), 0.5)
Guido van Rossum64deef22001-08-08 22:27:20 +0000305
306 # XXX Ran out of steam; TO DO: divmod, div, future division
307
Fred Drake2e2be372001-09-20 21:33:42 +0000308def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000309 support.run_unittest(RatTestCase)
Fred Drake2e2be372001-09-20 21:33:42 +0000310
311
312if __name__ == "__main__":
313 test_main()