Hi,

Please inspect and commit the following patch.

Thanks,
Gavin



Index: lib/rational.rb
===================================================================
RCS file: /src/ruby/lib/rational.rb,v
retrieving revision 1.13.2.1
diff -u -b -r1.13.2.1 rational.rb
--- lib/rational.rb	7 May 2004 08:48:23 -0000	1.13.2.1
+++ lib/rational.rb	24 Oct 2005 14:02:21 -0000
@@ -5,37 +5,29 @@
 #   	$Date: 1999/08/24 12:49:28 $
 #   	by Keiju ISHITSUKA(SHL Japan Inc.)
 #
-# --
-#   Usage:
-#   class Rational < Numeric
-#      (include Comparable)
-#
-#   Rational(a, b) --> a/b
-#
-#   Rational::+
-#   Rational::-
-#   Rational::*
-#   Rational::/
-#   Rational::**
-#   Rational::%
-#   Rational::divmod
-#   Rational::abs
-#   Rational::<=>
-#   Rational::to_i
-#   Rational::to_f
-#   Rational::to_s
-#
-#   Integer::gcd
-#   Integer::lcm
-#   Integer::gcdlcm
-#   Integer::to_r
-#
-#   Fixnum::**
-#   Fixnum::quo
-#   Bignum::**
-#   Bignum::quo
+# Documentation by Kevin Jackson and Gavin Sinclair.
 #
+# When you <tt>require 'rational'</tt>, all interactions between numbers
+# potentially return a rational result.  For example:
+#
+#   1.quo(2)              # -> 0.5
+#   require 'rational'
+#   1.quo(2)              # -> Rational(1,2)
+#
+# See Rational for full documentation.
+#
+

+#
+# Creates a Rational number (i.e. a fraction).  +a+ and +b+ should be Integers:
+#
+#   Rational(1,3)           # -> 1/3
+#
+# Note: trying to construct a Rational with floating point or real values
+# produces errors:
+#
+#   Rational(1.1, 2.3)      # -> NoMethodError
+#
 def Rational(a, b = 1)
   if a.kind_of?(Rational) && b == 1
     a
@@ -44,9 +36,38 @@
   end
 end

+#
+# Rational implements a rational class for numbers.
+#
+# <em>A rational number is a number that can be expressed as a fraction p/q
+# where p and q are integers and q != 0.  A rational number p/q is said to have
+# numerator p and denominator q.  Numbers that are not rational are called
+# irrational numbers.</em> (http://mathworld.wolfram.com/RationalNumber.html)
+#
+# To create a Rational Number:
+#   Rational(a,b)             # -> a/b
+#   Rational.new!(a,b)        # -> a/b
+#
+# Examples:
+#   Rational(5,6)             # -> 5/6
+#   Rational(5)               # -> 5/1
+#
+# Rational numbers are reduced to their lowest terms:
+#   Rational(6,10)            # -> 3/5
+#
+# But not if you use the unusual method "new!":
+#   Rational.new!(6,10)       # -> 6/10
+#
+# Division by zero is obviously not allowed:
+#   Rational(3,0)             # -> ZeroDivisionError
+#
 class Rational < Numeric
   @RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-'

+  #
+  # Reduces the given numerator and denominator to their lowest terms.  Use
+  # Rational() instead.
+  #
   def Rational.reduce(num, den = 1)
     raise ZeroDivisionError, "denominator is zero" if den == 0

@@ -64,12 +85,20 @@
     end
   end

+  #
+  # Implements the constructor.  This method does not reduce to lowest terms or
+  # check for division by zero.  Therefore #Rational() should be preferred in
+  # normal use.
+  #
   def Rational.new!(num, den = 1)
     new(num, den)
   end

   private_class_method :new

+  #
+  # This method is actually private.
+  #
   def initialize(num, den)
     if den < 0
       num = -num
@@ -84,6 +113,14 @@
     end
   end

+  #
+  # Returns the addition of this value and +a+.
+  #
+  # Examples:
+  #   r = Rational(3,4)      # -> Rational(3,4)
+  #   r + 1                  # -> Rational(7,4)
+  #   r + 0.5                # -> 1.25
+  #
   def + (a)
     if a.kind_of?(Rational)
       num = @numerator * a.denominator
@@ -99,6 +136,15 @@
     end
   end

+  #
+  # Returns the difference of this value and +a+.
+  # subtracted.
+  #
+  # Examples:
+  #   r = Rational(3,4)    # -> Rational(3,4)
+  #   r - 1                # -> Rational(-1,4)
+  #   r - 0.5              # -> 0.25
+  #
   def - (a)
     if a.kind_of?(Rational)
       num = @numerator * a.denominator
@@ -114,6 +160,16 @@
     end
   end

+  #
+  # Returns the product of this value and +a+.
+  #
+  # Examples:
+  #   r = Rational(3,4)    # -> Rational(3,4)
+  #   r * 2                # -> Rational(3,2)
+  #   r * 4                # -> Rational(3,1)
+  #   r * 0.5              # -> 0.375
+  #   r * Rational(1,2)    # -> Rational(3,8)
+  #
   def * (a)
     if a.kind_of?(Rational)
       num = @numerator * a.numerator
@@ -129,6 +185,13 @@
     end
   end

+  #
+  # Returns the quotient of this value and +a+.
+  #   r = Rational(3,4)    # -> Rational(3,4)
+  #   r / 2                # -> Rational(3,8)
+  #   r / 2.0              # -> 0.375
+  #   r / Rational(1,2)    # -> Rational(3,2)
+  #
   def / (a)
     if a.kind_of?(Rational)
       num = @numerator * a.denominator
@@ -145,6 +208,15 @@
     end
   end

+  #
+  # Returns this value raised to the given power.
+  #
+  # Examples:
+  #   r = Rational(3,4)    # -> Rational(3,4)
+  #   r ** 2               # -> Rational(9,16)
+  #   r ** 2.0             # -> 0.5625
+  #   r ** Rational(1,2)   # -> 0.866025403784439
+  #
   def ** (other)
     if other.kind_of?(Rational)
       Float(self) ** other
@@ -168,16 +240,36 @@
     end
   end

+  #
+  # Returns the remainder when this value is divided by +other+.
+  #
+  # Examples:
+  #   r = Rational(7,4)    # -> Rational(7,4)
+  #   r % Rational(1,2)    # -> Rational(1,4)
+  #   r % 1                # -> Rational(3,4)
+  #   r % Rational(1,7)    # -> Rational(1,28)
+  #   r % 0.26             # -> 0.19
+  #
   def % (other)
     value = (self / other).to_i
     return self - other * value
   end

+  #
+  # Returns the quotient _and_ remainder.
+  #
+  # Examples:
+  #   r = Rational(7,4)        # -> Rational(7,4)
+  #   r.divmod Rational(1,2)   # -> [3, Rational(1,4)]
+  #
   def divmod(other)
     value = (self / other).to_i
     return value, self - other * value
   end

+  #
+  # Returns the absolute value.
+  #
   def abs
     if @numerator > 0
       Rational.new!(@numerator, @denominator)
@@ -186,6 +278,15 @@
     end
   end

+  #
+  # Returns +true+ iff this value is numerically equal to +other+.
+  #
+  # But beware:
+  #   Rational(1,2) == Rational(4,8)          # -> true
+  #   Rational(1,2) == Rational.new!(4,8)     # -> false
+  #
+  # Don't use Rational.new!
+  #
   def == (other)
     if other.kind_of?(Rational)
       @numerator == other.numerator and @denominator == other.denominator
@@ -198,6 +299,9 @@
     end
   end

+  #
+  # Standard comparison operator.
+  #
   def <=> (other)
     if other.kind_of?(Rational)
       num = @numerator * other.denominator
@@ -232,14 +336,35 @@
     end
   end

+  #
+  # Converts the rational to an Integer.  Not the _nearest_ integer, the
+  # truncated integer.  Study the following example carefully:
+  #   Rational(+7,4).to_i             # -> 1
+  #   Rational(-7,4).to_i             # -> -2
+  #   (-1.75).to_i                    # -> -1
+  #
+  # In other words:
+  #   Rational(-7,4) == -1.75                 # -> true
+  #   Rational(-7,4).to_i == (-1.75).to_i     # false
+  #
   def to_i
     Integer(@numerator.div(@denominator))
   end

+  #
+  # Converts the rational to a Float.
+  #
   def to_f
     @numerator.to_f/@denominator.to_f
   end

+  #
+  # Returns a string representation of the rational number.
+  #
+  # Example:
+  #   Rational(3,4).to_s          #  "3/4"
+  #   Rational(8).to_s            #  "8"
+  #
   def to_s
     if @denominator == 1
       @numerator.to_s
@@ -248,14 +373,25 @@
     end
   end

+  #
+  # Returns +self+.
+  #
   def to_r
     self
   end

+  #
+  # Returns a reconstructable string representation:
+  #
+  #   Rational(5,8).inspect     # -> "Rational(5, 8)"
+  #
   def inspect
     sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect)
   end

+  #
+  # Returns a hash code for the object.
+  #
   def hash
     @numerator.hash ^ @denominator.hash
   end
@@ -267,18 +403,38 @@
 end

 class Integer
+  #
+  # In an integer, the value _is_ the numerator of its rational equivalent.
+  # Therefore, this method returns +self+.
+  #
   def numerator
     self
   end

+  #
+  # In an integer, the denominator is 1.  Therefore, this method returns 1.
+  #
   def denominator
     1
   end

+  #
+  # Returns a Rational representation of this integer.
+  #
   def to_r
     Rational(self, 1)
   end

+  #
+  # Returns the <em>greatest common denominator</em> of the two numbers (+self+
+  # and +n+).
+  #
+  # Examples:
+  #   72.gcd 168           # -> 24
+  #   19.gcd 36            # -> 1
+  #
+  # The result is positive, no matter the sign of the arguments.
+  #
   def gcd(n)
     m = self.abs
     n = n.abs
@@ -312,29 +468,49 @@
     return a
   end

-  def lcm(int)
-    a = self.abs
-    b = int.abs
-    gcd = a.gcd(b)
-    (a.div(gcd)) * b
+  #
+  # Returns the <em>lowest common multiple</em> (LCM) of the two arguments
+  # (+self+ and +other+).
+  #
+  # Examples:
+  #   6.lcm 7        # -> 42
+  #   6.lcm 9        # -> 18
+  #
+  def lcm(other)
+    if self.zero? or other.zero?
+      0
+    else
+      (self.div(self.gcd(other)) * other).abs
   end
-
-  def gcdlcm(int)
-    a = self.abs
-    b = int.abs
-    gcd = a.gcd(b)
-    return gcd, (a.div(gcd)) * b
   end

+  #
+  # Returns the GCD _and_ the LCM (see #gcd and #lcm) of the two arguments
+  # (+self+ and +other+).  This is more efficient than calculating them
+  # separately.
+  #
+  # Example:
+  #   6.gcdlcm 9     # -> [3, 18]
+  #
+  def gcdlcm(other)
+    gcd = self.gcd(other)
+    if self.zero? or other.zero?
+      [gcd, 0]
+    else
+      [gcd, (self.div(gcd) * other).abs]
+    end
+  end
 end

 class Fixnum
   undef quo
+  # If Rational is defined, returns a Rational number instead of a Fixnum.
   def quo(other)
     Rational.new!(self,1) / other
   end
   alias rdiv quo

+  # Returns a Rational number if the result is in fact rational (i.e.
+other+ < 0).
   def rpower (other)
     if other >= 0
       self.power!(other)
@@ -355,11 +531,13 @@
   end

   undef quo
+  # If Rational is defined, returns a Rational number instead of a Bignum.
   def quo(other)
     Rational.new!(self,1) / other
   end
   alias rdiv quo

+  # Returns a Rational number if the result is in fact rational (i.e.
+other+ < 0).
   def rpower (other)
     if other >= 0
       self.power!(other)