-
Notifications
You must be signed in to change notification settings - Fork 606
/
numeric.rb
289 lines (236 loc) · 4.9 KB
/
numeric.rb
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
class Numeric
include Comparable
# Always raises TypeError, as dup'ing Numerics is not allowed.
def initialize_copy(other)
raise TypeError, "copy of #{self.class} is not allowed"
end
private :initialize_copy
def +@
self
end
def -@
0 - self
end
def divmod(other)
[div(other), self % other]
end
def eql?(other)
return false unless other.instance_of? self.class
self == other
end
def <=>(other)
# It's important this method NOT contain the coercion protocols!
# MRI doesn't and doing so breaks stuff!
return 0 if self.equal? other
return nil
end
def step(limit=nil, step=nil, to: nil, by: nil)
unless block_given?
return to_enum(:step, limit, step, to: to, by: by) do
Rubinius::Mirror::Numeric.reflect(self).step_size(limit, step, to, by)
end
end
m = Rubinius::Mirror::Numeric.reflect(self)
values = m.step_fetch_args(limit, step, to, by)
value = values[0]
limit = values[1]
step = values[2]
asc = values[3]
is_float = values[4]
if step == 0
while true
yield value
end
end
if is_float
n = m.step_float_size(value, limit, step, asc)
if n > 0
if step.infinite?
yield value
else
i = 0
if asc
while i < n
d = i * step + value
d = limit if limit < d
yield d
i += 1
end
else
while i < n
d = i * step + value
d = limit if limit > d
yield d
i += 1
end
end
end
end
else
if asc
until value > limit
yield value
value += step
end
else
until value < limit
yield value
value += step
end
end
end
return self
end
def truncate
Float(self).truncate
end
# Delegate #to_int to #to_i in subclasses
def to_int
to_i
end
def integer?
false
end
def zero?
self == 0
end
def nonzero?
zero? ? nil : self
end
def round
to_f.round
end
def abs
self < 0 ? -self : self
end
def floor
FloatValue(self).floor
end
def ceil
FloatValue(self).ceil
end
def remainder(other)
mod = self % other
if mod != 0 and ((self < 0 and other > 0) or (self > 0 and other < 0))
mod - other
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)
if other.instance_of? self.class
return [other, self]
end
[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
if error == :coerce_error
raise TypeError, "#{other.class} can't be coerced into #{self.class}"
else
raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
end
end
unless Rubinius::Type.object_kind_of?(values, Array) && values.length == 2
raise TypeError, "coerce must return [x, y]"
end
return values[1], values[0]
end
private :math_coerce
def redo_coerced(meth, right)
b, a = math_coerce(right)
a.__send__ meth, b
end
private :redo_coerced
def redo_compare(meth, right)
b, a = math_coerce(right, :compare_error)
a.__send__ meth, b
end
def div(other)
raise ZeroDivisionError, "divided by 0" if other == 0
self.__slash__(other).floor
end
def fdiv(other)
self.to_f / other
end
def quo(other)
Rubinius.privately do
Rational.convert(self, 1, false) / other
end
end
def modulo(other)
self - other * self.div(other)
end
alias_method :%, :modulo
def i
Complex(0, self)
end
def to_c
Complex(self, 0)
end
def real
self
end
def imag
0
end
alias_method :imaginary, :imag
def arg
if self < 0
Math::PI
else
0
end
end
alias_method :angle, :arg
alias_method :phase, :arg
def polar
return abs, arg
end
def conjugate
self
end
alias_method :conj, :conjugate
def rect
[self, 0]
end
alias_method :rectangular, :rect
def abs2
self * self
end
alias_method :magnitude, :abs
def numerator
to_r.numerator
end
def denominator
to_r.denominator
end
def real?
true
end
def positive?
self > 0
end
def negative?
self < 0
end
end