]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Tests for binary operators on subtypes of built-in types."""\r |
2 | \r | |
3 | import unittest\r | |
4 | from test import test_support\r | |
5 | \r | |
6 | def gcd(a, b):\r | |
7 | """Greatest common divisor using Euclid's algorithm."""\r | |
8 | while a:\r | |
9 | a, b = b%a, a\r | |
10 | return b\r | |
11 | \r | |
12 | def isint(x):\r | |
13 | """Test whether an object is an instance of int or long."""\r | |
14 | return isinstance(x, int) or isinstance(x, long)\r | |
15 | \r | |
16 | def isnum(x):\r | |
17 | """Test whether an object is an instance of a built-in numeric type."""\r | |
18 | for T in int, long, float, complex:\r | |
19 | if isinstance(x, T):\r | |
20 | return 1\r | |
21 | return 0\r | |
22 | \r | |
23 | def isRat(x):\r | |
24 | """Test wheter an object is an instance of the Rat class."""\r | |
25 | return isinstance(x, Rat)\r | |
26 | \r | |
27 | class Rat(object):\r | |
28 | \r | |
29 | """Rational number implemented as a normalized pair of longs."""\r | |
30 | \r | |
31 | __slots__ = ['_Rat__num', '_Rat__den']\r | |
32 | \r | |
33 | def __init__(self, num=0L, den=1L):\r | |
34 | """Constructor: Rat([num[, den]]).\r | |
35 | \r | |
36 | The arguments must be ints or longs, and default to (0, 1)."""\r | |
37 | if not isint(num):\r | |
38 | raise TypeError, "Rat numerator must be int or long (%r)" % num\r | |
39 | if not isint(den):\r | |
40 | raise TypeError, "Rat denominator must be int or long (%r)" % den\r | |
41 | # But the zero is always on\r | |
42 | if den == 0:\r | |
43 | raise ZeroDivisionError, "zero denominator"\r | |
44 | g = gcd(den, num)\r | |
45 | self.__num = long(num//g)\r | |
46 | self.__den = long(den//g)\r | |
47 | \r | |
48 | def _get_num(self):\r | |
49 | """Accessor function for read-only 'num' attribute of Rat."""\r | |
50 | return self.__num\r | |
51 | num = property(_get_num, None)\r | |
52 | \r | |
53 | def _get_den(self):\r | |
54 | """Accessor function for read-only 'den' attribute of Rat."""\r | |
55 | return self.__den\r | |
56 | den = property(_get_den, None)\r | |
57 | \r | |
58 | def __repr__(self):\r | |
59 | """Convert a Rat to an string resembling a Rat constructor call."""\r | |
60 | return "Rat(%d, %d)" % (self.__num, self.__den)\r | |
61 | \r | |
62 | def __str__(self):\r | |
63 | """Convert a Rat to a string resembling a decimal numeric value."""\r | |
64 | return str(float(self))\r | |
65 | \r | |
66 | def __float__(self):\r | |
67 | """Convert a Rat to a float."""\r | |
68 | return self.__num*1.0/self.__den\r | |
69 | \r | |
70 | def __int__(self):\r | |
71 | """Convert a Rat to an int; self.den must be 1."""\r | |
72 | if self.__den == 1:\r | |
73 | try:\r | |
74 | return int(self.__num)\r | |
75 | except OverflowError:\r | |
76 | raise OverflowError, ("%s too large to convert to int" %\r | |
77 | repr(self))\r | |
78 | raise ValueError, "can't convert %s to int" % repr(self)\r | |
79 | \r | |
80 | def __long__(self):\r | |
81 | """Convert a Rat to an long; self.den must be 1."""\r | |
82 | if self.__den == 1:\r | |
83 | return long(self.__num)\r | |
84 | raise ValueError, "can't convert %s to long" % repr(self)\r | |
85 | \r | |
86 | def __add__(self, other):\r | |
87 | """Add two Rats, or a Rat and a number."""\r | |
88 | if isint(other):\r | |
89 | other = Rat(other)\r | |
90 | if isRat(other):\r | |
91 | return Rat(self.__num*other.__den + other.__num*self.__den,\r | |
92 | self.__den*other.__den)\r | |
93 | if isnum(other):\r | |
94 | return float(self) + other\r | |
95 | return NotImplemented\r | |
96 | \r | |
97 | __radd__ = __add__\r | |
98 | \r | |
99 | def __sub__(self, other):\r | |
100 | """Subtract two Rats, or a Rat and a number."""\r | |
101 | if isint(other):\r | |
102 | other = Rat(other)\r | |
103 | if isRat(other):\r | |
104 | return Rat(self.__num*other.__den - other.__num*self.__den,\r | |
105 | self.__den*other.__den)\r | |
106 | if isnum(other):\r | |
107 | return float(self) - other\r | |
108 | return NotImplemented\r | |
109 | \r | |
110 | def __rsub__(self, other):\r | |
111 | """Subtract two Rats, or a Rat and a number (reversed args)."""\r | |
112 | if isint(other):\r | |
113 | other = Rat(other)\r | |
114 | if isRat(other):\r | |
115 | return Rat(other.__num*self.__den - self.__num*other.__den,\r | |
116 | self.__den*other.__den)\r | |
117 | if isnum(other):\r | |
118 | return other - float(self)\r | |
119 | return NotImplemented\r | |
120 | \r | |
121 | def __mul__(self, other):\r | |
122 | """Multiply two Rats, or a Rat and a number."""\r | |
123 | if isRat(other):\r | |
124 | return Rat(self.__num*other.__num, self.__den*other.__den)\r | |
125 | if isint(other):\r | |
126 | return Rat(self.__num*other, self.__den)\r | |
127 | if isnum(other):\r | |
128 | return float(self)*other\r | |
129 | return NotImplemented\r | |
130 | \r | |
131 | __rmul__ = __mul__\r | |
132 | \r | |
133 | def __truediv__(self, other):\r | |
134 | """Divide two Rats, or a Rat and a number."""\r | |
135 | if isRat(other):\r | |
136 | return Rat(self.__num*other.__den, self.__den*other.__num)\r | |
137 | if isint(other):\r | |
138 | return Rat(self.__num, self.__den*other)\r | |
139 | if isnum(other):\r | |
140 | return float(self) / other\r | |
141 | return NotImplemented\r | |
142 | \r | |
143 | __div__ = __truediv__\r | |
144 | \r | |
145 | def __rtruediv__(self, other):\r | |
146 | """Divide two Rats, or a Rat and a number (reversed args)."""\r | |
147 | if isRat(other):\r | |
148 | return Rat(other.__num*self.__den, other.__den*self.__num)\r | |
149 | if isint(other):\r | |
150 | return Rat(other*self.__den, self.__num)\r | |
151 | if isnum(other):\r | |
152 | return other / float(self)\r | |
153 | return NotImplemented\r | |
154 | \r | |
155 | __rdiv__ = __rtruediv__\r | |
156 | \r | |
157 | def __floordiv__(self, other):\r | |
158 | """Divide two Rats, returning the floored result."""\r | |
159 | if isint(other):\r | |
160 | other = Rat(other)\r | |
161 | elif not isRat(other):\r | |
162 | return NotImplemented\r | |
163 | x = self/other\r | |
164 | return x.__num // x.__den\r | |
165 | \r | |
166 | def __rfloordiv__(self, other):\r | |
167 | """Divide two Rats, returning the floored result (reversed args)."""\r | |
168 | x = other/self\r | |
169 | return x.__num // x.__den\r | |
170 | \r | |
171 | def __divmod__(self, other):\r | |
172 | """Divide two Rats, returning quotient and remainder."""\r | |
173 | if isint(other):\r | |
174 | other = Rat(other)\r | |
175 | elif not isRat(other):\r | |
176 | return NotImplemented\r | |
177 | x = self//other\r | |
178 | return (x, self - other * x)\r | |
179 | \r | |
180 | def __rdivmod__(self, other):\r | |
181 | """Divide two Rats, returning quotient and remainder (reversed args)."""\r | |
182 | if isint(other):\r | |
183 | other = Rat(other)\r | |
184 | elif not isRat(other):\r | |
185 | return NotImplemented\r | |
186 | return divmod(other, self)\r | |
187 | \r | |
188 | def __mod__(self, other):\r | |
189 | """Take one Rat modulo another."""\r | |
190 | return divmod(self, other)[1]\r | |
191 | \r | |
192 | def __rmod__(self, other):\r | |
193 | """Take one Rat modulo another (reversed args)."""\r | |
194 | return divmod(other, self)[1]\r | |
195 | \r | |
196 | def __eq__(self, other):\r | |
197 | """Compare two Rats for equality."""\r | |
198 | if isint(other):\r | |
199 | return self.__den == 1 and self.__num == other\r | |
200 | if isRat(other):\r | |
201 | return self.__num == other.__num and self.__den == other.__den\r | |
202 | if isnum(other):\r | |
203 | return float(self) == other\r | |
204 | return NotImplemented\r | |
205 | \r | |
206 | def __ne__(self, other):\r | |
207 | """Compare two Rats for inequality."""\r | |
208 | return not self == other\r | |
209 | \r | |
210 | # Silence Py3k warning\r | |
211 | __hash__ = None\r | |
212 | \r | |
213 | class RatTestCase(unittest.TestCase):\r | |
214 | """Unit tests for Rat class and its support utilities."""\r | |
215 | \r | |
216 | def test_gcd(self):\r | |
217 | self.assertEqual(gcd(10, 12), 2)\r | |
218 | self.assertEqual(gcd(10, 15), 5)\r | |
219 | self.assertEqual(gcd(10, 11), 1)\r | |
220 | self.assertEqual(gcd(100, 15), 5)\r | |
221 | self.assertEqual(gcd(-10, 2), -2)\r | |
222 | self.assertEqual(gcd(10, -2), 2)\r | |
223 | self.assertEqual(gcd(-10, -2), -2)\r | |
224 | for i in range(1, 20):\r | |
225 | for j in range(1, 20):\r | |
226 | self.assertTrue(gcd(i, j) > 0)\r | |
227 | self.assertTrue(gcd(-i, j) < 0)\r | |
228 | self.assertTrue(gcd(i, -j) > 0)\r | |
229 | self.assertTrue(gcd(-i, -j) < 0)\r | |
230 | \r | |
231 | def test_constructor(self):\r | |
232 | a = Rat(10, 15)\r | |
233 | self.assertEqual(a.num, 2)\r | |
234 | self.assertEqual(a.den, 3)\r | |
235 | a = Rat(10L, 15L)\r | |
236 | self.assertEqual(a.num, 2)\r | |
237 | self.assertEqual(a.den, 3)\r | |
238 | a = Rat(10, -15)\r | |
239 | self.assertEqual(a.num, -2)\r | |
240 | self.assertEqual(a.den, 3)\r | |
241 | a = Rat(-10, 15)\r | |
242 | self.assertEqual(a.num, -2)\r | |
243 | self.assertEqual(a.den, 3)\r | |
244 | a = Rat(-10, -15)\r | |
245 | self.assertEqual(a.num, 2)\r | |
246 | self.assertEqual(a.den, 3)\r | |
247 | a = Rat(7)\r | |
248 | self.assertEqual(a.num, 7)\r | |
249 | self.assertEqual(a.den, 1)\r | |
250 | try:\r | |
251 | a = Rat(1, 0)\r | |
252 | except ZeroDivisionError:\r | |
253 | pass\r | |
254 | else:\r | |
255 | self.fail("Rat(1, 0) didn't raise ZeroDivisionError")\r | |
256 | for bad in "0", 0.0, 0j, (), [], {}, None, Rat, unittest:\r | |
257 | try:\r | |
258 | a = Rat(bad)\r | |
259 | except TypeError:\r | |
260 | pass\r | |
261 | else:\r | |
262 | self.fail("Rat(%r) didn't raise TypeError" % bad)\r | |
263 | try:\r | |
264 | a = Rat(1, bad)\r | |
265 | except TypeError:\r | |
266 | pass\r | |
267 | else:\r | |
268 | self.fail("Rat(1, %r) didn't raise TypeError" % bad)\r | |
269 | \r | |
270 | def test_add(self):\r | |
271 | self.assertEqual(Rat(2, 3) + Rat(1, 3), 1)\r | |
272 | self.assertEqual(Rat(2, 3) + 1, Rat(5, 3))\r | |
273 | self.assertEqual(1 + Rat(2, 3), Rat(5, 3))\r | |
274 | self.assertEqual(1.0 + Rat(1, 2), 1.5)\r | |
275 | self.assertEqual(Rat(1, 2) + 1.0, 1.5)\r | |
276 | \r | |
277 | def test_sub(self):\r | |
278 | self.assertEqual(Rat(7, 2) - Rat(7, 5), Rat(21, 10))\r | |
279 | self.assertEqual(Rat(7, 5) - 1, Rat(2, 5))\r | |
280 | self.assertEqual(1 - Rat(3, 5), Rat(2, 5))\r | |
281 | self.assertEqual(Rat(3, 2) - 1.0, 0.5)\r | |
282 | self.assertEqual(1.0 - Rat(1, 2), 0.5)\r | |
283 | \r | |
284 | def test_mul(self):\r | |
285 | self.assertEqual(Rat(2, 3) * Rat(5, 7), Rat(10, 21))\r | |
286 | self.assertEqual(Rat(10, 3) * 3, 10)\r | |
287 | self.assertEqual(3 * Rat(10, 3), 10)\r | |
288 | self.assertEqual(Rat(10, 5) * 0.5, 1.0)\r | |
289 | self.assertEqual(0.5 * Rat(10, 5), 1.0)\r | |
290 | \r | |
291 | def test_div(self):\r | |
292 | self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))\r | |
293 | self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))\r | |
294 | self.assertEqual(2 / Rat(5), Rat(2, 5))\r | |
295 | self.assertEqual(3.0 * Rat(1, 2), 1.5)\r | |
296 | self.assertEqual(Rat(1, 2) * 3.0, 1.5)\r | |
297 | \r | |
298 | def test_floordiv(self):\r | |
299 | self.assertEqual(Rat(10) // Rat(4), 2)\r | |
300 | self.assertEqual(Rat(10, 3) // Rat(4, 3), 2)\r | |
301 | self.assertEqual(Rat(10) // 4, 2)\r | |
302 | self.assertEqual(10 // Rat(4), 2)\r | |
303 | \r | |
304 | def test_eq(self):\r | |
305 | self.assertEqual(Rat(10), Rat(20, 2))\r | |
306 | self.assertEqual(Rat(10), 10)\r | |
307 | self.assertEqual(10, Rat(10))\r | |
308 | self.assertEqual(Rat(10), 10.0)\r | |
309 | self.assertEqual(10.0, Rat(10))\r | |
310 | \r | |
311 | def test_future_div(self):\r | |
312 | exec future_test\r | |
313 | \r | |
314 | # XXX Ran out of steam; TO DO: divmod, div, future division\r | |
315 | \r | |
316 | future_test = """\r | |
317 | from __future__ import division\r | |
318 | self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))\r | |
319 | self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))\r | |
320 | self.assertEqual(2 / Rat(5), Rat(2, 5))\r | |
321 | self.assertEqual(3.0 * Rat(1, 2), 1.5)\r | |
322 | self.assertEqual(Rat(1, 2) * 3.0, 1.5)\r | |
323 | self.assertEqual(eval('1/2'), 0.5)\r | |
324 | """\r | |
325 | \r | |
326 | def test_main():\r | |
327 | test_support.run_unittest(RatTestCase)\r | |
328 | \r | |
329 | \r | |
330 | if __name__ == "__main__":\r | |
331 | test_main()\r |