1 | #
|
---|
2 | # rational.rb -
|
---|
3 | # $Release Version: 0.5 $
|
---|
4 | # $Revision: 1.7 $
|
---|
5 | # $Date: 1999/08/24 12:49:28 $
|
---|
6 | # by Keiju ISHITSUKA(SHL Japan Inc.)
|
---|
7 | #
|
---|
8 | # Documentation by Kevin Jackson and Gavin Sinclair.
|
---|
9 | #
|
---|
10 | # When you <tt>require 'rational'</tt>, all interactions between numbers
|
---|
11 | # potentially return a rational result. For example:
|
---|
12 | #
|
---|
13 | # 1.quo(2) # -> 0.5
|
---|
14 | # require 'rational'
|
---|
15 | # 1.quo(2) # -> Rational(1,2)
|
---|
16 | #
|
---|
17 | # See Rational for full documentation.
|
---|
18 | #
|
---|
19 |
|
---|
20 |
|
---|
21 | #
|
---|
22 | # Creates a Rational number (i.e. a fraction). +a+ and +b+ should be Integers:
|
---|
23 | #
|
---|
24 | # Rational(1,3) # -> 1/3
|
---|
25 | #
|
---|
26 | # Note: trying to construct a Rational with floating point or real values
|
---|
27 | # produces errors:
|
---|
28 | #
|
---|
29 | # Rational(1.1, 2.3) # -> NoMethodError
|
---|
30 | #
|
---|
31 | def Rational(a, b = 1)
|
---|
32 | if a.kind_of?(Rational) && b == 1
|
---|
33 | a
|
---|
34 | else
|
---|
35 | Rational.reduce(a, b)
|
---|
36 | end
|
---|
37 | end
|
---|
38 |
|
---|
39 | #
|
---|
40 | # Rational implements a rational class for numbers.
|
---|
41 | #
|
---|
42 | # <em>A rational number is a number that can be expressed as a fraction p/q
|
---|
43 | # where p and q are integers and q != 0. A rational number p/q is said to have
|
---|
44 | # numerator p and denominator q. Numbers that are not rational are called
|
---|
45 | # irrational numbers.</em> (http://mathworld.wolfram.com/RationalNumber.html)
|
---|
46 | #
|
---|
47 | # To create a Rational Number:
|
---|
48 | # Rational(a,b) # -> a/b
|
---|
49 | # Rational.new!(a,b) # -> a/b
|
---|
50 | #
|
---|
51 | # Examples:
|
---|
52 | # Rational(5,6) # -> 5/6
|
---|
53 | # Rational(5) # -> 5/1
|
---|
54 | #
|
---|
55 | # Rational numbers are reduced to their lowest terms:
|
---|
56 | # Rational(6,10) # -> 3/5
|
---|
57 | #
|
---|
58 | # But not if you use the unusual method "new!":
|
---|
59 | # Rational.new!(6,10) # -> 6/10
|
---|
60 | #
|
---|
61 | # Division by zero is obviously not allowed:
|
---|
62 | # Rational(3,0) # -> ZeroDivisionError
|
---|
63 | #
|
---|
64 | class Rational < Numeric
|
---|
65 | @RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-'
|
---|
66 |
|
---|
67 | #
|
---|
68 | # Reduces the given numerator and denominator to their lowest terms. Use
|
---|
69 | # Rational() instead.
|
---|
70 | #
|
---|
71 | def Rational.reduce(num, den = 1)
|
---|
72 | raise ZeroDivisionError, "denominator is zero" if den == 0
|
---|
73 |
|
---|
74 | if den < 0
|
---|
75 | num = -num
|
---|
76 | den = -den
|
---|
77 | end
|
---|
78 | gcd = num.gcd(den)
|
---|
79 | num = num.div(gcd)
|
---|
80 | den = den.div(gcd)
|
---|
81 | if den == 1 && defined?(Unify)
|
---|
82 | num
|
---|
83 | else
|
---|
84 | new!(num, den)
|
---|
85 | end
|
---|
86 | end
|
---|
87 |
|
---|
88 | #
|
---|
89 | # Implements the constructor. This method does not reduce to lowest terms or
|
---|
90 | # check for division by zero. Therefore #Rational() should be preferred in
|
---|
91 | # normal use.
|
---|
92 | #
|
---|
93 | def Rational.new!(num, den = 1)
|
---|
94 | new(num, den)
|
---|
95 | end
|
---|
96 |
|
---|
97 | private_class_method :new
|
---|
98 |
|
---|
99 | #
|
---|
100 | # This method is actually private.
|
---|
101 | #
|
---|
102 | def initialize(num, den)
|
---|
103 | if den < 0
|
---|
104 | num = -num
|
---|
105 | den = -den
|
---|
106 | end
|
---|
107 | if num.kind_of?(Integer) and den.kind_of?(Integer)
|
---|
108 | @numerator = num
|
---|
109 | @denominator = den
|
---|
110 | else
|
---|
111 | @numerator = num.to_i
|
---|
112 | @denominator = den.to_i
|
---|
113 | end
|
---|
114 | end
|
---|
115 |
|
---|
116 | #
|
---|
117 | # Returns the addition of this value and +a+.
|
---|
118 | #
|
---|
119 | # Examples:
|
---|
120 | # r = Rational(3,4) # -> Rational(3,4)
|
---|
121 | # r + 1 # -> Rational(7,4)
|
---|
122 | # r + 0.5 # -> 1.25
|
---|
123 | #
|
---|
124 | def + (a)
|
---|
125 | if a.kind_of?(Rational)
|
---|
126 | num = @numerator * a.denominator
|
---|
127 | num_a = a.numerator * @denominator
|
---|
128 | Rational(num + num_a, @denominator * a.denominator)
|
---|
129 | elsif a.kind_of?(Integer)
|
---|
130 | self + Rational.new!(a, 1)
|
---|
131 | elsif a.kind_of?(Float)
|
---|
132 | Float(self) + a
|
---|
133 | else
|
---|
134 | x, y = a.coerce(self)
|
---|
135 | x + y
|
---|
136 | end
|
---|
137 | end
|
---|
138 |
|
---|
139 | #
|
---|
140 | # Returns the difference of this value and +a+.
|
---|
141 | # subtracted.
|
---|
142 | #
|
---|
143 | # Examples:
|
---|
144 | # r = Rational(3,4) # -> Rational(3,4)
|
---|
145 | # r - 1 # -> Rational(-1,4)
|
---|
146 | # r - 0.5 # -> 0.25
|
---|
147 | #
|
---|
148 | def - (a)
|
---|
149 | if a.kind_of?(Rational)
|
---|
150 | num = @numerator * a.denominator
|
---|
151 | num_a = a.numerator * @denominator
|
---|
152 | Rational(num - num_a, @denominator*a.denominator)
|
---|
153 | elsif a.kind_of?(Integer)
|
---|
154 | self - Rational.new!(a, 1)
|
---|
155 | elsif a.kind_of?(Float)
|
---|
156 | Float(self) - a
|
---|
157 | else
|
---|
158 | x, y = a.coerce(self)
|
---|
159 | x - y
|
---|
160 | end
|
---|
161 | end
|
---|
162 |
|
---|
163 | #
|
---|
164 | # Returns the product of this value and +a+.
|
---|
165 | #
|
---|
166 | # Examples:
|
---|
167 | # r = Rational(3,4) # -> Rational(3,4)
|
---|
168 | # r * 2 # -> Rational(3,2)
|
---|
169 | # r * 4 # -> Rational(3,1)
|
---|
170 | # r * 0.5 # -> 0.375
|
---|
171 | # r * Rational(1,2) # -> Rational(3,8)
|
---|
172 | #
|
---|
173 | def * (a)
|
---|
174 | if a.kind_of?(Rational)
|
---|
175 | num = @numerator * a.numerator
|
---|
176 | den = @denominator * a.denominator
|
---|
177 | Rational(num, den)
|
---|
178 | elsif a.kind_of?(Integer)
|
---|
179 | self * Rational.new!(a, 1)
|
---|
180 | elsif a.kind_of?(Float)
|
---|
181 | Float(self) * a
|
---|
182 | else
|
---|
183 | x, y = a.coerce(self)
|
---|
184 | x * y
|
---|
185 | end
|
---|
186 | end
|
---|
187 |
|
---|
188 | #
|
---|
189 | # Returns the quotient of this value and +a+.
|
---|
190 | # r = Rational(3,4) # -> Rational(3,4)
|
---|
191 | # r / 2 # -> Rational(3,8)
|
---|
192 | # r / 2.0 # -> 0.375
|
---|
193 | # r / Rational(1,2) # -> Rational(3,2)
|
---|
194 | #
|
---|
195 | def / (a)
|
---|
196 | if a.kind_of?(Rational)
|
---|
197 | num = @numerator * a.denominator
|
---|
198 | den = @denominator * a.numerator
|
---|
199 | Rational(num, den)
|
---|
200 | elsif a.kind_of?(Integer)
|
---|
201 | raise ZeroDivisionError, "division by zero" if a == 0
|
---|
202 | self / Rational.new!(a, 1)
|
---|
203 | elsif a.kind_of?(Float)
|
---|
204 | Float(self) / a
|
---|
205 | else
|
---|
206 | x, y = a.coerce(self)
|
---|
207 | x / y
|
---|
208 | end
|
---|
209 | end
|
---|
210 |
|
---|
211 | #
|
---|
212 | # Returns this value raised to the given power.
|
---|
213 | #
|
---|
214 | # Examples:
|
---|
215 | # r = Rational(3,4) # -> Rational(3,4)
|
---|
216 | # r ** 2 # -> Rational(9,16)
|
---|
217 | # r ** 2.0 # -> 0.5625
|
---|
218 | # r ** Rational(1,2) # -> 0.866025403784439
|
---|
219 | #
|
---|
220 | def ** (other)
|
---|
221 | if other.kind_of?(Rational)
|
---|
222 | Float(self) ** other
|
---|
223 | elsif other.kind_of?(Integer)
|
---|
224 | if other > 0
|
---|
225 | num = @numerator ** other
|
---|
226 | den = @denominator ** other
|
---|
227 | elsif other < 0
|
---|
228 | num = @denominator ** -other
|
---|
229 | den = @numerator ** -other
|
---|
230 | elsif other == 0
|
---|
231 | num = 1
|
---|
232 | den = 1
|
---|
233 | end
|
---|
234 | Rational.new!(num, den)
|
---|
235 | elsif other.kind_of?(Float)
|
---|
236 | Float(self) ** other
|
---|
237 | else
|
---|
238 | x, y = other.coerce(self)
|
---|
239 | x ** y
|
---|
240 | end
|
---|
241 | end
|
---|
242 |
|
---|
243 | #
|
---|
244 | # Returns the remainder when this value is divided by +other+.
|
---|
245 | #
|
---|
246 | # Examples:
|
---|
247 | # r = Rational(7,4) # -> Rational(7,4)
|
---|
248 | # r % Rational(1,2) # -> Rational(1,4)
|
---|
249 | # r % 1 # -> Rational(3,4)
|
---|
250 | # r % Rational(1,7) # -> Rational(1,28)
|
---|
251 | # r % 0.26 # -> 0.19
|
---|
252 | #
|
---|
253 | def % (other)
|
---|
254 | value = (self / other).to_i
|
---|
255 | return self - other * value
|
---|
256 | end
|
---|
257 |
|
---|
258 | #
|
---|
259 | # Returns the quotient _and_ remainder.
|
---|
260 | #
|
---|
261 | # Examples:
|
---|
262 | # r = Rational(7,4) # -> Rational(7,4)
|
---|
263 | # r.divmod Rational(1,2) # -> [3, Rational(1,4)]
|
---|
264 | #
|
---|
265 | def divmod(other)
|
---|
266 | value = (self / other).to_i
|
---|
267 | return value, self - other * value
|
---|
268 | end
|
---|
269 |
|
---|
270 | #
|
---|
271 | # Returns the absolute value.
|
---|
272 | #
|
---|
273 | def abs
|
---|
274 | if @numerator > 0
|
---|
275 | Rational.new!(@numerator, @denominator)
|
---|
276 | else
|
---|
277 | Rational.new!(-@numerator, @denominator)
|
---|
278 | end
|
---|
279 | end
|
---|
280 |
|
---|
281 | #
|
---|
282 | # Returns +true+ iff this value is numerically equal to +other+.
|
---|
283 | #
|
---|
284 | # But beware:
|
---|
285 | # Rational(1,2) == Rational(4,8) # -> true
|
---|
286 | # Rational(1,2) == Rational.new!(4,8) # -> false
|
---|
287 | #
|
---|
288 | # Don't use Rational.new!
|
---|
289 | #
|
---|
290 | def == (other)
|
---|
291 | if other.kind_of?(Rational)
|
---|
292 | @numerator == other.numerator and @denominator == other.denominator
|
---|
293 | elsif other.kind_of?(Integer)
|
---|
294 | self == Rational.new!(other, 1)
|
---|
295 | elsif other.kind_of?(Float)
|
---|
296 | Float(self) == other
|
---|
297 | else
|
---|
298 | other == self
|
---|
299 | end
|
---|
300 | end
|
---|
301 |
|
---|
302 | #
|
---|
303 | # Standard comparison operator.
|
---|
304 | #
|
---|
305 | def <=> (other)
|
---|
306 | if other.kind_of?(Rational)
|
---|
307 | num = @numerator * other.denominator
|
---|
308 | num_a = other.numerator * @denominator
|
---|
309 | v = num - num_a
|
---|
310 | if v > 0
|
---|
311 | return 1
|
---|
312 | elsif v < 0
|
---|
313 | return -1
|
---|
314 | else
|
---|
315 | return 0
|
---|
316 | end
|
---|
317 | elsif other.kind_of?(Integer)
|
---|
318 | return self <=> Rational.new!(other, 1)
|
---|
319 | elsif other.kind_of?(Float)
|
---|
320 | return Float(self) <=> other
|
---|
321 | elsif defined? other.coerce
|
---|
322 | x, y = other.coerce(self)
|
---|
323 | return x <=> y
|
---|
324 | else
|
---|
325 | return nil
|
---|
326 | end
|
---|
327 | end
|
---|
328 |
|
---|
329 | def coerce(other)
|
---|
330 | if other.kind_of?(Float)
|
---|
331 | return other, self.to_f
|
---|
332 | elsif other.kind_of?(Integer)
|
---|
333 | return Rational.new!(other, 1), self
|
---|
334 | else
|
---|
335 | super
|
---|
336 | end
|
---|
337 | end
|
---|
338 |
|
---|
339 | #
|
---|
340 | # Converts the rational to an Integer. Not the _nearest_ integer, the
|
---|
341 | # truncated integer. Study the following example carefully:
|
---|
342 | # Rational(+7,4).to_i # -> 1
|
---|
343 | # Rational(-7,4).to_i # -> -2
|
---|
344 | # (-1.75).to_i # -> -1
|
---|
345 | #
|
---|
346 | # In other words:
|
---|
347 | # Rational(-7,4) == -1.75 # -> true
|
---|
348 | # Rational(-7,4).to_i == (-1.75).to_i # false
|
---|
349 | #
|
---|
350 | def to_i
|
---|
351 | Integer(@numerator.div(@denominator))
|
---|
352 | end
|
---|
353 |
|
---|
354 | #
|
---|
355 | # Converts the rational to a Float.
|
---|
356 | #
|
---|
357 | def to_f
|
---|
358 | @numerator.to_f/@denominator.to_f
|
---|
359 | end
|
---|
360 |
|
---|
361 | #
|
---|
362 | # Returns a string representation of the rational number.
|
---|
363 | #
|
---|
364 | # Example:
|
---|
365 | # Rational(3,4).to_s # "3/4"
|
---|
366 | # Rational(8).to_s # "8"
|
---|
367 | #
|
---|
368 | def to_s
|
---|
369 | if @denominator == 1
|
---|
370 | @numerator.to_s
|
---|
371 | else
|
---|
372 | @numerator.to_s+"/"[email protected]_s
|
---|
373 | end
|
---|
374 | end
|
---|
375 |
|
---|
376 | #
|
---|
377 | # Returns +self+.
|
---|
378 | #
|
---|
379 | def to_r
|
---|
380 | self
|
---|
381 | end
|
---|
382 |
|
---|
383 | #
|
---|
384 | # Returns a reconstructable string representation:
|
---|
385 | #
|
---|
386 | # Rational(5,8).inspect # -> "Rational(5, 8)"
|
---|
387 | #
|
---|
388 | def inspect
|
---|
389 | sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect)
|
---|
390 | end
|
---|
391 |
|
---|
392 | #
|
---|
393 | # Returns a hash code for the object.
|
---|
394 | #
|
---|
395 | def hash
|
---|
396 | @numerator.hash ^ @denominator.hash
|
---|
397 | end
|
---|
398 |
|
---|
399 | attr :numerator
|
---|
400 | attr :denominator
|
---|
401 |
|
---|
402 | private :initialize
|
---|
403 | end
|
---|
404 |
|
---|
405 | class Integer
|
---|
406 | #
|
---|
407 | # In an integer, the value _is_ the numerator of its rational equivalent.
|
---|
408 | # Therefore, this method returns +self+.
|
---|
409 | #
|
---|
410 | def numerator
|
---|
411 | self
|
---|
412 | end
|
---|
413 |
|
---|
414 | #
|
---|
415 | # In an integer, the denominator is 1. Therefore, this method returns 1.
|
---|
416 | #
|
---|
417 | def denominator
|
---|
418 | 1
|
---|
419 | end
|
---|
420 |
|
---|
421 | #
|
---|
422 | # Returns a Rational representation of this integer.
|
---|
423 | #
|
---|
424 | def to_r
|
---|
425 | Rational(self, 1)
|
---|
426 | end
|
---|
427 |
|
---|
428 | #
|
---|
429 | # Returns the <em>greatest common denominator</em> of the two numbers (+self+
|
---|
430 | # and +n+).
|
---|
431 | #
|
---|
432 | # Examples:
|
---|
433 | # 72.gcd 168 # -> 24
|
---|
434 | # 19.gcd 36 # -> 1
|
---|
435 | #
|
---|
436 | # The result is positive, no matter the sign of the arguments.
|
---|
437 | #
|
---|
438 | def gcd(other)
|
---|
439 | min = self.abs
|
---|
440 | max = other.abs
|
---|
441 | while min > 0
|
---|
442 | tmp = min
|
---|
443 | min = max % min
|
---|
444 | max = tmp
|
---|
445 | end
|
---|
446 | max
|
---|
447 | end
|
---|
448 |
|
---|
449 | #
|
---|
450 | # Returns the <em>lowest common multiple</em> (LCM) of the two arguments
|
---|
451 | # (+self+ and +other+).
|
---|
452 | #
|
---|
453 | # Examples:
|
---|
454 | # 6.lcm 7 # -> 42
|
---|
455 | # 6.lcm 9 # -> 18
|
---|
456 | #
|
---|
457 | def lcm(other)
|
---|
458 | if self.zero? or other.zero?
|
---|
459 | 0
|
---|
460 | else
|
---|
461 | (self.div(self.gcd(other)) * other).abs
|
---|
462 | end
|
---|
463 | end
|
---|
464 |
|
---|
465 | #
|
---|
466 | # Returns the GCD _and_ the LCM (see #gcd and #lcm) of the two arguments
|
---|
467 | # (+self+ and +other+). This is more efficient than calculating them
|
---|
468 | # separately.
|
---|
469 | #
|
---|
470 | # Example:
|
---|
471 | # 6.gcdlcm 9 # -> [3, 18]
|
---|
472 | #
|
---|
473 | def gcdlcm(other)
|
---|
474 | gcd = self.gcd(other)
|
---|
475 | if self.zero? or other.zero?
|
---|
476 | [gcd, 0]
|
---|
477 | else
|
---|
478 | [gcd, (self.div(gcd) * other).abs]
|
---|
479 | end
|
---|
480 | end
|
---|
481 | end
|
---|
482 |
|
---|
483 | class Fixnum
|
---|
484 | undef quo
|
---|
485 | # If Rational is defined, returns a Rational number instead of a Fixnum.
|
---|
486 | def quo(other)
|
---|
487 | Rational.new!(self,1) / other
|
---|
488 | end
|
---|
489 | alias rdiv quo
|
---|
490 |
|
---|
491 | # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0).
|
---|
492 | def rpower (other)
|
---|
493 | if other >= 0
|
---|
494 | self.power!(other)
|
---|
495 | else
|
---|
496 | Rational.new!(self,1)**other
|
---|
497 | end
|
---|
498 | end
|
---|
499 |
|
---|
500 | unless defined? 1.power!
|
---|
501 | alias power! **
|
---|
502 | alias ** rpower
|
---|
503 | end
|
---|
504 | end
|
---|
505 |
|
---|
506 | class Bignum
|
---|
507 | unless defined? Complex
|
---|
508 | alias power! **
|
---|
509 | end
|
---|
510 |
|
---|
511 | undef quo
|
---|
512 | # If Rational is defined, returns a Rational number instead of a Bignum.
|
---|
513 | def quo(other)
|
---|
514 | Rational.new!(self,1) / other
|
---|
515 | end
|
---|
516 | alias rdiv quo
|
---|
517 |
|
---|
518 | # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0).
|
---|
519 | def rpower (other)
|
---|
520 | if other >= 0
|
---|
521 | self.power!(other)
|
---|
522 | else
|
---|
523 | Rational.new!(self, 1)**other
|
---|
524 | end
|
---|
525 | end
|
---|
526 |
|
---|
527 | unless defined? Complex
|
---|
528 | alias ** rpower
|
---|
529 | end
|
---|
530 | end
|
---|