mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 11:34:29 -07:00
340 lines
7.1 KiB
Ruby
340 lines
7.1 KiB
Ruby
#
|
|
# 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
|