public
Description: Rubinius, the Ruby VM
Homepage: http://rubini.us
Clone URL: git://github.com/evanphx/rubinius.git
Search Repo:
Ryan Davis (author)
Thu May 15 16:05:12 -0700 2008
commit  ccefd8c6fd63cf4ca4f1d72d2402d8e00bea0b15
tree    5a1a9d0e83cae1399ca6592556c2d2c2612e5ac8
parent  ac5a764219bbd9a06fe4550a7754fc9c9950c206
rubinius / kernel / core / numeric.rb
100644 240 lines (197 sloc) 4.424 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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# depends on: class.rb comparable.rb
 
class Numeric
  include Comparable
 
  # Numeric and sub-classes do not have ivars
  def __ivars__ ; nil ; end
 
  def +@
    self
  end
 
  def -@
    0 - self
  end
 
  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
  
  def %(other)
    b, a = math_coerce other
    raise ZeroDivisionError, "divided by 0" unless b.__kind_of__(Float) or b != 0
    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 divmod(other)
    b, a = math_coerce other
    
    if other == 0
      raise FloatDomainError, "NaN" if other.__kind_of__ Float
      raise ZeroDivisionError, "divided by 0"
    end
    
    a.divmod b
  end
  
  def div(other)
    raise FloatDomainError, "NaN" if self == 0 && other.__kind_of__(Float) && other == 0
    b, a = math_coerce other
    (a / b).floor
  end
 
  def quo(other)
    if other.__kind_of__ Integer
      self / Float(other)
    else
      b, a = math_coerce other
      a / b
    end
  end
 
  def <(other)
    b, a = math_coerce other, :compare_error
    a < b
  end
  
  def <=(other)
    b, a = math_coerce other, :compare_error
    a <= b
  end
  
  def >(other)
    b, a = math_coerce other, :compare_error
    a > b
  end
  
  def >=(other)
    b, a = math_coerce other, :compare_error
    a >= b
  end
 
  def ==(other)
    !!(other == self)
  end
  
  def <=>(other)
    begin
      b, a = math_coerce other, :compare_error
      return a <=> b
    rescue ArgumentError
      return nil
    end
  end
  
  def truncate
    Float(self).truncate
  end
 
  # Delegate #to_int to #to_i in subclasses
  def to_int
    self.to_i
  end
  
  # Delegate #modulp to #% in subclasses
  def modulo(other)
    self % other
  end
 
  def integer?
    false
  end
 
  def zero?
    self == 0
  end
 
  def nonzero?
    zero? ? nil : self
  end
 
  def round
    self.to_f.round
  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 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
 
  #--
  # We deviate from MRI behavior here because we ensure that Fixnum op Bignum
  # => Bignum (possibly normalized to Fixnum)
  #
  # Note these differences on MRI, where a is a Fixnum, b is a Bignum
  #
  # a.coerce b => [Float, Float]
  # b.coerce a => [Bignum, Bignum]
  #++
 
  def coerce(other)
    Ruby.primitive :numeric_coerce
    [Float(other), Float(self)]
  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
  #
  # See also Integer#coerce
 
  def math_coerce(other, error=:coerce_error)
    begin
      values = other.coerce(self)
    rescue
      send error, other
    end
    
    unless values.__kind_of__(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
 
  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
end