# $Release Version: 0.5 $
# $Date: 1998/07/08 10:05:28 $
# by Keiju ISHITSUKA(SHL Japan Inc.)
# complex.rb implements the Complex class for complex numbers. Additionally,
# some methods in other Numeric classes are redefined or added to allow greater
# interoperability with Complex numbers.
# Complex numbers can be created in the following manner:
# - <tt>Complex(a, b)</tt>
# - <tt>Complex.polar(radius, theta)</tt>
# Additionally, note the following:
# - <tt>Complex::I</tt> (the mathematical constant <i>i</i>)
# - <tt>Numeric#im</tt> (e.g. <tt>5.im -> 0+5i</tt>)
# The following +Math+ module methods are redefined to handle Complex arguments.
# They will work as normal with non-Complex arguments.
# sqrt exp cos sin tan log log10
# cosh sinh tanh acos asin atan atan2 acosh asinh atanh
# Numeric is a built-in class on which Fixnum, Bignum, etc., are based. Here
# some methods are added so that all number types can be treated to some extent
# Returns a Complex number <tt>(0,<i>self</i>)</tt>.
# The real part of a complex number, i.e. <i>self</i>.
# The imaginary part of a complex number, i.e. 0.
# See Complex#conjugate (short answer: returns <i>self</i>).
# Creates a Complex number. +a+ and +b+ should be Numeric. The result will be
if b == 0 and (a.kind_of?(Complex) or defined? Complex::Unify)
Complex.new( a.real-b.imag, a.imag+b.real )
# The complex number class. See complex.rb for an overview.
@RCS_ID='-$Id: complex.rb,v 1.3 1998/07/08 10:05:28 keiju Exp keiju $-'
undef floor, truncate, ceil, round
def Complex.generic?(other) # :nodoc:
other.kind_of?(Integer) or
(defined?(Rational) and other.kind_of?(Rational))
# Creates a +Complex+ number in terms of +r+ (radius) and +theta+ (angle).
def Complex.polar(r, theta)
Complex(r*Math.cos(theta), r*Math.sin(theta))
# Creates a +Complex+ number <tt>a</tt>+<tt>b</tt><i>i</i>.
raise TypeError, "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric
raise TypeError, "`#{a.inspect}' for 1st arg" if a.kind_of? Complex
raise TypeError, "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric
raise TypeError, "`#{b.inspect}' for 2nd arg" if b.kind_of? Complex
# Addition with real or complex number.
if other.kind_of?(Complex)
im = @image + other.image
elsif Complex.generic?(other)
Complex(@real + other, @image)
x , y = other.coerce(self)
# Subtraction with real or complex number.
if other.kind_of?(Complex)
im = @image - other.image
elsif Complex.generic?(other)
Complex(@real - other, @image)
x , y = other.coerce(self)
# Multiplication with real or complex number.
if other.kind_of?(Complex)
re = @real*other.real - @image*other.image
im = @real*other.image + @image*other.real
elsif Complex.generic?(other)
Complex(@real * other, @image * other)
x , y = other.coerce(self)
# Division by real or complex number.
if other.kind_of?(Complex)
self*other.conjugate/other.abs2
elsif Complex.generic?(other)
Complex(@real/other, @image/other)
x, y = other.coerce(self)
Complex(@real.quo(1), @image.quo(1)) / other
# Raise this complex number to the given (real or complex) power.
if other.kind_of?(Complex)
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)
while (div, mod = n.divmod(2)
x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image)
(Rational(1) / self) ** -other
elsif Complex.generic?(other)
Complex.polar(r**other, theta*other)
x, y = other.coerce(self)
# Remainder after division by a real or complex number.
if other.kind_of?(Complex)
Complex(@real % other.real, @image % other.image)
elsif Complex.generic?(other)
Complex(@real % other, @image % other)
x , y = other.coerce(self)
# if other.kind_of?(Complex)
# rdiv, rmod = @real.divmod(other.real)
# idiv, imod = @image.divmod(other.image)
# return Complex(rdiv, idiv), Complex(rmod, rmod)
# elsif Complex.generic?(other)
# Complex(@real.divmod(other), @image.divmod(other))
# x , y = other.coerce(self)
# Absolute value (aka modulus): distance from the zero point on the complex
Math.hypot(@real, @image)
# Square of the absolute value.
@real*@real + @image*@image
# Argument (angle from (1,0) on the complex plane).
Math.atan2!(@image, @real)
# Returns the absolute value _and_ the argument.
# Complex conjugate (<tt>z + z.conjugate = 2 * z.real</tt>).
# Compares the absolute values of the two numbers.
# Test for numerical equality (<tt>a == a + 0<i>i</i></tt>).
if other.kind_of?(Complex)
@real == other.real and @image == other.image
elsif Complex.generic?(other)
@real == other and @image == 0
# Attempts to coerce +other+ to a Complex number.
if Complex.generic?(other)
return Complex.new!(other), self
@real.denominator.lcm(@image.denominator)
Complex(@real.numerator*(cd/@real.denominator),
@image.numerator*(cd/@image.denominator))
# Standard string representation of the complex number.
if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
@real.to_s+"+("+@image.to_s+")i"
@real.to_s+"-("+(-@image).to_s+")i"
@real.to_s+"+"+@image.to_s+"i"
@real.to_s+"-"+(-@image).to_s+"i"
if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
# Returns a hash code for the complex number.
# Returns "<tt>Complex(<i>real</i>, <i>image</i>)</tt>".
sprintf("Complex(%s, %s)", @real.inspect, @image.inspect)
# +I+ is the imaginary number. It exists at point (0,1) on the complex plane.
# The real part of a complex number.
# The imaginary part of a complex number.
unless defined?(1.numerator)
if self.zero? or other.zero?
(self.div(self.gcd(other)) * other).abs
# Redefined to handle a Complex argument.
sqrt(z.conjugate).conjugate
Complex( sqrt!((r+x)/2), sqrt!((r-x)/2) )
# Redefined to handle a Complex argument.
Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image))
# Redefined to handle a Complex argument.
Complex(cos!(z.real)*cosh!(z.image),
-sin!(z.real)*sinh!(z.image))
# Redefined to handle a Complex argument.