core/complex.rb
#
# complex.rb -
# $Release Version: 0.5 $
# $Revision: 1.3 $
# $Date: 1998/07/08 10:05:28 $
# by Keiju ISHITSUKA(SHL Japan Inc.)
#
class Complex < Numeric
undef_method :%
undef_method :<
undef_method :<=
undef_method :<=>
undef_method :>
undef_method :>=
undef_method :between?
undef_method :div
undef_method :divmod
undef_method :floor
undef_method :ceil
undef_method :modulo
undef_method :remainder
undef_method :round
undef_method :step
undef_method :truncate
undef_method :i
undef_method :positive?
undef_method :negative?
def self.convert(real, imag = undefined)
if real.equal?(nil) || imag.equal?(nil)
raise TypeError, "cannot convert nil into Complex"
end
real = real.to_c if real.kind_of?(String)
imag = imag.to_c if imag.kind_of?(String)
if real.kind_of?(Complex) && !real.imag.kind_of?(Float) && real.imag == 0
real = real.real
end
if imag.kind_of?(Complex) && !imag.imag.kind_of?(Float) && imag.imag == 0
imag = imag.real
end
if real.kind_of?(Complex) && !imag.kind_of?(Float) && imag == 0
return real
end
if undefined.equal?(imag)
if real.kind_of?(Numeric) && !real.real?
return real
elsif !real.kind_of?(Numeric)
return Rubinius::Type.coerce_to(real, Complex, :to_c)
else
imag = 0
end
elsif real.kind_of?(Numeric) && imag.kind_of?(Numeric) && (!real.real? || !imag.real?)
return real + imag * Complex.new(0, 1)
end
return real if Rubinius.mathn_loaded? && imag.equal?(0)
rect(real, imag)
end
def Complex.generic?(other) # :nodoc:
other.kind_of?(Integer) or
other.kind_of?(Float) or
(defined?(Rational) and other.kind_of?(Rational))
end
def Complex.rect(real, imag=0)
raise TypeError, 'not a real' unless check_real?(real) && check_real?(imag)
new(real, imag)
end
def Complex.polar(r, theta=0)
raise TypeError, 'not a real' unless check_real?(r) && check_real?(theta)
Complex(r*Math.cos(theta), r*Math.sin(theta))
end
def Complex.check_real?(obj)
obj.kind_of?(Numeric) && obj.real?
end
def initialize(a, b = 0)
@real = a
@imag = b
end
private :initialize
def -@
Complex(-real, -imag)
end
def +(other)
if other.kind_of?(Complex)
Complex(real + other.real, imag + other.imag)
elsif other.kind_of?(Numeric) && other.real?
Complex(real + other, imag)
else
redo_coerced(:+, other)
end
end
def -(other)
if other.kind_of?(Complex)
Complex(real - other.real, imag - other.imag)
elsif other.kind_of?(Numeric) && other.real?
Complex(real - other, imag)
else
redo_coerced(:-, other)
end
end
def *(other)
if other.kind_of?(Complex)
Complex(real * other.real - imag * other.imag,
real * other.imag + imag * other.real)
elsif other.kind_of?(Numeric) && other.real?
Complex(real * other, imag * other)
else
redo_coerced(:*, other)
end
end
def divide(other)
if other.kind_of?(Complex)
self * other.conjugate / other.abs2
elsif other.kind_of?(Numeric) && other.real?
Complex(real.quo(other), imag.quo(other))
else
redo_coerced(:quo, other)
end
end
alias_method :/, :divide
alias_method :quo, :divide
def ** (other)
if !other.kind_of?(Float) && other == 0
return Complex(1)
end
if other.kind_of?(Complex)
r, theta = polar
ore = other.real
oim = other.imag
nr = Math.exp(ore*Math.log(r) - oim * theta)
ntheta = theta*ore + oim*Math.log(r)
Complex.polar(nr, ntheta)
elsif other.kind_of?(Integer)
if other > 0
x = self
z = x
n = other - 1
while n != 0
while (div, mod = n.divmod(2)
mod == 0)
x = Complex(x.real*x.real - x.imag*x.imag, 2*x.real*x.imag)
n = div
end
z *= x
n -= 1
end
z
else
if defined? Rational
(Rational.new(1, 1) / self) ** -other
else
self ** Float(other)
end
end
elsif Complex.generic?(other)
r, theta = polar
Complex.polar(r**other, theta*other)
else
x, y = other.coerce(self)
x**y
end
end
def abs
Math.hypot(@real, @imag)
end
alias_method :magnitude, :abs
def abs2
@real*@real + @imag*@imag
end
def arg
Math.atan2(@imag, @real)
end
alias_method :angle, :arg
alias_method :phase, :arg
def polar
[abs, arg]
end
def conjugate
Complex(@real, -@imag)
end
alias_method :conj, :conjugate
def ==(other)
if other.kind_of?(Complex)
real == other.real && imag == other.imag
elsif other.kind_of?(Numeric) && other.real?
real == other && imag == 0
else
other == self
end
end
def eql?(other)
other.kind_of?(Complex) and
imag.class == other.imag.class and
real.class == other.real.class and
self == other
end
def coerce(other)
if other.kind_of?(Numeric) && other.real?
[Complex.new(other, 0), self]
elsif other.kind_of?(Complex)
[other, self]
else
raise TypeError, "#{other.class} can't be coerced into Complex"
end
end
def denominator
@real.denominator.lcm(@imag.denominator)
end
def numerator
cd = denominator
Complex(@real.numerator*(cd/@real.denominator),
@imag.numerator*(cd/@imag.denominator))
end
def real?
false
end
def rect
[@real, @imag]
end
alias_method :rectangular, :rect
def to_f
raise RangeError, "can't convert #{self} into Float" unless !imag.kind_of?(Float) && imag == 0
real.to_f
end
def to_i
raise RangeError, "can't convert #{self} into Integer" unless !imag.kind_of?(Float) && imag == 0
real.to_i
end
def to_r
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
real.to_r
end
def rationalize(eps = nil)
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
real.rationalize(eps)
end
def to_s
result = real.to_s
if imag.kind_of?(Float) ? !imag.nan? && imag.signbit? : imag < 0
result << "-"
else
result << "+"
end
imag_s = imag.abs.to_s
result << imag_s
unless imag_s[-1] =~ /\d/
result << "*"
end
result << "i"
result
end
def hash
@real.hash ^ @imag.hash
end
def inspect
"(#{to_s})"
end
def fdiv(other)
raise TypeError, "#{other.class} can't be coerced into Complex" unless other.is_a?(Numeric)
# FIXME
self / other
end
def marshal_dump
ary = [real, imag]
instance_variables.each do |ivar|
ary.instance_variable_set(ivar, instance_variable_get(ivar))
end
ary
end
private :marshal_dump
def marshal_load(ary)
@real, @imag = ary
ary.instance_variables.each do |ivar|
instance_variable_set(ivar, ary.instance_variable_get(ivar))
end
self
end
class << self
private :convert
private :check_real?
alias_method :rectangular, :rect
end
attr_reader :real
attr_reader :imag
alias_method :imaginary, :imag
end