source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rational.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 11.6 KB
Line 
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#
31def Rational(a, b = 1)
32 if a.kind_of?(Rational) && b == 1
33 a
34 else
35 Rational.reduce(a, b)
36 end
37end
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#
64class 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
403end
404
405class 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
481end
482
483class 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
504end
505
506class 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
530end
Note: See TracBrowser for help on using the repository browser.