public
Description: Rubinius, the Ruby VM
Homepage: http://rubini.us
Clone URL: git://github.com/evanphx/rubinius.git
Search Repo:
brixen (author)
Mon Feb 11 16:41:53 -0800 2008
commit  359166e79fe5bb5def62f639e37d9c613b0a731d
tree    20e9475bc90e9d59860b54812b8354c2fd22096b
parent  64b0fb4131276feda0d0ab13301824b20f8d7f8e
rubinius / kernel / core / numeric.rb
100644 207 lines (172 sloc) 3.85 kb
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# depends on: class.rb comparable.rb
 
class Numeric
  include Comparable
 
  def +(other)
    b, a = math_coerce(other)
    a + b
  end
  
  def -(other)
    b, a = math_coerce(other)
    a - b
  end
  
  def *(other)
    b, a = math_coerce(other)
    a * b
  end
  
  # see README-DEVELOPERS regarding safe math compiler plugin
  def divide(other)
    b, a = math_coerce(other)
    raise ZeroDivisionError, "divided by 0" unless b.kind_of?(Float) or b != 0
    a / b
  end
  alias_method :/, :divide
  
  def **(other)
    b, a = math_coerce(other)
    a ** b
  end
  
  def %(other)
    b, a = math_coerce(other)
    raise ZeroDivisionError, "divided by 0" unless b.kind_of?(Float) or b != 0
    a % b
  end
   
  def &(other)
    self & Type.coerce_to(other, Integer, :to_int)
  end
   
  def |(other)
    self | Type.coerce_to(other, Integer, :to_int)
  end
  
  def ^(other)
    self ^ Type.coerce_to(other, Integer, :to_int)
  end
 
  def abs
    self < 0 ? -self : self
  end
 
  def floor
    int = self.to_i
    if self == int or self > 0
      int
    else
      int - 1
    end
  end
 
  def ceil
    int = self.to_i
    if self == int or self < 0
      int
    else
      int + 1
    end
  end
 
  def +@
    self
  end
 
  def -@
    0 - self
  end
 
  def integer?
    false
  end
 
  def div(other)
    raise FloatDomainError, "NaN" if self == 0 && other.is_a?(Float) && other == 0
    b, a = math_coerce(other)
    (a / b).floor
  end
 
  def quo(other)
    if other.is_a?(Integer)
      self / Float(other)
    else
      b, a = math_coerce(other)
      a / b
    end
  end
 
  def divmod(other)
    b, a = math_coerce(other)
    
    if other == 0
      raise FloatDomainError, "NaN" if other.is_a?(Float)
      raise ZeroDivisionError, "divided by 0"
    end
    
    a.divmod(b)
  end
  
  def round
    self.to_f.round
  end
  
  def ==(other)
    begin
      b, a = math_coerce(other)
      return a == b
    rescue TypeError
      return other == self
    end
  end
  
  def <=>(other)
    begin
      b, a = math_coerce(other)
      return a <=> b
    rescue TypeError
      return nil
    end
  end
  
  def zero?
    self == 0
  end
 
  def nonzero?
    if zero?
      nil
    else
      self
    end
  end
 
  def remainder(other)
    b, a = math_coerce(other)
    mod = a % b
 
    if mod != 0 && (a < 0 && b > 0 || a > 0 && b < 0)
      mod - b
    else
      mod
    end
  end
 
  def step(limit, step=1, &block)
    raise ArgumentError, "step cannot be 0" if step == 0
    limit,step = step.coerce(limit)
    # FIXME: why is this not covered by the block parameter above?
    raise LocalJumpError, "no block given" unless block_given?
    idx,step = step.coerce(self)
    cmp = step > 0 ? :<= : :>=
    while (idx.send(cmp,limit))
      yield(idx)
      idx += step
    end
    return self
  rescue TypeError => e
    raise ArgumentError, e.message
  end
 
  # This method mimics the semantics of MRI's do_coerce function
  # in numeric.c. Note these differences between it and #coerce:
  # 1.2.coerce("2") => [2.0, 1.2]
  # 1.2 + "2" => TypeError: String can't be coerced into Float
  #
  # We do not attempt to produce the exact same exception message
  # as MRI, so please do not edit it to match.
  #
  # See also our Numeric#coerce
  def math_coerce(other, error=:coerce_error)
    begin
      values = other.coerce(self)
    rescue
      send error, other
    end
    
    unless values.is_a?(Array) && values.length == 2
      raise TypeError, "coerce must return [x, y]"
    end
 
    return values[1], values[0]
  end
  private :math_coerce
  
  def coerce_error(other)
    raise TypeError, "#{other.class} can't be coerced into #{self.class}"
  end
  private :coerce_error
  
  def compare_error(other)
    raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
  end
  private :compare_error
end