core/numeric.rb
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 / 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