summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-rational/mrblib/rational.rb
blob: a38f7140774a203d8eb1d1e997a8838019693533 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
class Rational < Numeric
  # Override #<, #<=, #>, #>= in Numeric
  prepend Comparable

  def initialize(numerator = 0, denominator = 1)
    @numerator = numerator
    @denominator = denominator

    _simplify
  end

  def inspect
    "(#{to_s})"
  end

  def to_f
    @numerator.to_f / @denominator.to_f
  end

  def to_i
    to_f.to_i
  end

  def to_r
    self
  end

  def to_s
    "#{numerator}/#{denominator}"
  end

  def *(rhs)
    if rhs.is_a? Rational
      Rational(numerator * rhs.numerator, denominator * rhs.denominator)
    elsif rhs.is_a? Integer
      Rational(numerator * rhs, denominator)
    elsif rhs.is_a? Numeric
      numerator * rhs / denominator
    end
  end

  def +(rhs)
    if rhs.is_a? Rational
      Rational(numerator * rhs.denominator + rhs.numerator * denominator, denominator * rhs.denominator)
    elsif rhs.is_a? Integer
      Rational(numerator + rhs * denominator, denominator)
    elsif rhs.is_a? Numeric
      (numerator + rhs * denominator) / denominator
    end
  end

  def -(rhs)
    if rhs.is_a? Rational
      Rational(numerator * rhs.denominator - rhs.numerator * denominator, denominator * rhs.denominator)
    elsif rhs.is_a? Integer
      Rational(numerator - rhs * denominator, denominator)
    elsif rhs.is_a? Numeric
      (numerator - rhs * denominator) / denominator
    end
  end

  def /(rhs)
    if rhs.is_a? Rational
      Rational(numerator * rhs.denominator, denominator * rhs.numerator)
    elsif rhs.is_a? Integer
      Rational(numerator, denominator * rhs)
    elsif rhs.is_a? Numeric
      numerator / rhs / denominator
    end
  end

  def <=>(rhs)
    case rhs
    when Fixnum
      return numerator <=> rhs if denominator == 1
      rhs = Rational(rhs)
    when Float
      return to_f <=> rhs
    end
    case rhs
    when Rational
      (numerator * rhs.denominator - denominator * rhs.numerator) <=> 0
    when Numeric
      return rhs <=> self
    else
      nil
    end
  end

  def negative?
    numerator.negative?
  end

  def _simplify
    a = numerator
    b = denominator
    a, b = b, a % b until b.zero?
    @numerator = @numerator.div(a)
    @denominator = @denominator.div(a)
  end

  attr_reader :numerator, :denominator
end

def Rational(numerator = 0, denominator = 1)
  Rational.new(numerator, denominator)
end

[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
  Fixnum.instance_exec do
    original_operator_name = "__original_operator_#{op}_rational"
    alias_method original_operator_name, op
    define_method op do |rhs|
      if rhs.is_a? Rational
        Rational(self).__send__(op, rhs)
      else
        __send__(original_operator_name, rhs)
      end
    end
  end
  Float.instance_exec do
    original_operator_name = "__original_operator_#{op}_rational"
    alias_method original_operator_name, op
    define_method op do |rhs|
      if rhs.is_a? Rational
        rhs = rhs.to_f
      end
      __send__(original_operator_name, rhs)
    end
  end
end