1class Rational < Numeric
2  def inspect
3    "(#{to_s})"
4  end
5
6  def to_s
7    "#{numerator}/#{denominator}"
8  end
9
10  def *(rhs)
11    if rhs.is_a? Rational
12      Rational(numerator * rhs.numerator, denominator * rhs.denominator)
13    elsif rhs.is_a? Integer
14      Rational(numerator * rhs, denominator)
15    elsif rhs.is_a? Numeric
16      numerator * rhs / denominator
17    end
18  end
19
20  def +(rhs)
21    if rhs.is_a? Rational
22      Rational(numerator * rhs.denominator + rhs.numerator * denominator, denominator * rhs.denominator)
23    elsif rhs.is_a? Integer
24      Rational(numerator + rhs * denominator, denominator)
25    elsif rhs.is_a? Numeric
26      (numerator + rhs * denominator) / denominator
27    end
28  end
29
30  def -(rhs)
31    if rhs.is_a? Rational
32      Rational(numerator * rhs.denominator - rhs.numerator * denominator, denominator * rhs.denominator)
33    elsif rhs.is_a? Integer
34      Rational(numerator - rhs * denominator, denominator)
35    elsif rhs.is_a? Numeric
36      (numerator - rhs * denominator) / denominator
37    end
38  end
39
40  def /(rhs)
41    if rhs.is_a? Rational
42      Rational(numerator * rhs.denominator, denominator * rhs.numerator)
43    elsif rhs.is_a? Integer
44      Rational(numerator, denominator * rhs)
45    elsif rhs.is_a? Numeric
46      numerator / rhs / denominator
47    end
48  end
49
50  def <=>(rhs)
51    if rhs.is_a?(Integral)
52      return numerator <=> rhs if denominator == 1
53      rhs = Rational(rhs)
54    end
55
56    case rhs
57    when Rational
58      (numerator * rhs.denominator - denominator * rhs.numerator) <=> 0
59    when Numeric
60      (rhs <=> self)&.-@
61    else
62      nil
63    end
64  end
65
66  def ==(rhs)
67    return true if self.equal?(rhs)
68    if rhs.is_a?(Integral) && denominator == 1
69      return numerator == rhs
70    end
71    if rhs.is_a?(Rational)
72      numerator * rhs.denominator == denominator * rhs.numerator
73    else
74      rhs == self
75    end
76  end
77end
78
79class Numeric
80  def to_r
81    Rational(self, 1)
82  end
83end
84
85module Kernel
86  def Rational(numerator, denominator = 1)
87    a = numerator
88    b = denominator
89    a, b = b, a % b until b == 0
90    Rational._new(numerator.div(a), denominator.div(a))
91  end
92
93  [:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
94    original_operator_name = :"__original_operator_#{op}_rational"
95    Fixnum.instance_eval do
96      alias_method original_operator_name, op
97      define_method op do |rhs|
98        if rhs.is_a? Rational
99          Rational(self).__send__(op, rhs)
100        else
101          __send__(original_operator_name, rhs)
102        end
103      end
104    end
105    Float.instance_eval do
106      alias_method original_operator_name, op
107      define_method op do |rhs|
108        if rhs.is_a? Rational
109          rhs = rhs.to_f
110        end
111        __send__(original_operator_name, rhs)
112      end
113    end if Object.const_defined?(:Float)
114  end
115end
116