opal/corelib/complex.rb
# backtick_javascript: true
require 'corelib/numeric'
require 'corelib/complex/base'
class ::Complex < ::Numeric
def self.rect(real, imag = 0)
unless ::Numeric === real && real.real? && ::Numeric === imag && imag.real?
::Kernel.raise ::TypeError, 'not a real'
end
new(real, imag)
end
def self.polar(r, theta = 0)
unless ::Numeric === r && r.real? && ::Numeric === theta && theta.real?
::Kernel.raise ::TypeError, 'not a real'
end
new(r * ::Math.cos(theta), r * ::Math.sin(theta))
end
attr_reader :real, :imag
def initialize(real, imag = 0)
@real = real
@imag = imag
freeze
end
def coerce(other)
if ::Complex === other
[other, self]
elsif ::Numeric === other && other.real?
[::Complex.new(other, 0), self]
else
::Kernel.raise ::TypeError, "#{other.class} can't be coerced into Complex"
end
end
def ==(other)
if ::Complex === other
@real == other.real && @imag == other.imag
elsif ::Numeric === other && other.real?
@real == other && @imag == 0
else
other == self
end
end
def -@
::Kernel.Complex(-@real, -@imag)
end
def +(other)
if ::Complex === other
::Kernel.Complex(@real + other.real, @imag + other.imag)
elsif ::Numeric === other && other.real?
::Kernel.Complex(@real + other, @imag)
else
__coerced__ :+, other
end
end
def -(other)
if ::Complex === other
::Kernel.Complex(@real - other.real, @imag - other.imag)
elsif ::Numeric === other && other.real?
::Kernel.Complex(@real - other, @imag)
else
__coerced__ :-, other
end
end
def *(other)
if ::Complex === other
::Kernel.Complex(@real * other.real - @imag * other.imag,
@real * other.imag + @imag * other.real,
)
elsif ::Numeric === other && other.real?
::Kernel.Complex(@real * other, @imag * other)
else
__coerced__ :*, other
end
end
def /(other)
if ::Complex === other
if (::Number === @real && @real.nan?) || (::Number === @imag && @imag.nan?) ||
(::Number === other.real && other.real.nan?) || (::Number === other.imag && other.imag.nan?)
::Complex.new(::Float::NAN, ::Float::NAN)
else
self * other.conj / other.abs2
end
elsif ::Numeric === other && other.real?
::Kernel.Complex(@real.quo(other), @imag.quo(other))
else
__coerced__ :/, other
end
end
def **(other)
if other == 0
return ::Complex.new(1, 0)
end
if ::Complex === other
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 ::Integer === other
if other > 0
x = self
z = x
n = other - 1
while n != 0
div, mod = n.divmod(2)
while mod == 0
x = ::Kernel.Complex(x.real * x.real - x.imag * x.imag, 2 * x.real * x.imag)
n = div
div, mod = n.divmod(2)
end
z *= x
n -= 1
end
z
else
(::Rational.new(1, 1) / self)**-other
end
elsif ::Float === other || ::Rational === other
r, theta = polar
::Complex.polar(r**other, theta * other)
else
__coerced__ :**, other
end
end
def abs
::Math.hypot(@real, @imag)
end
def abs2
@real * @real + @imag * @imag
end
def angle
::Math.atan2(@imag, @real)
end
def conj
::Kernel.Complex(@real, -@imag)
end
def denominator
@real.denominator.lcm(@imag.denominator)
end
def eql?(other)
Complex === other && @real.class == @imag.class && self == other
end
def fdiv(other)
unless ::Numeric === other
::Kernel.raise ::TypeError, "#{other.class} can't be coerced into Complex"
end
self / other
end
def finite?
@real.finite? && @imag.finite?
end
def hash
[::Complex, @real, @imag].hash
end
def infinite?
@real.infinite? || @imag.infinite?
end
def inspect
"(#{self})"
end
def numerator
d = denominator
::Kernel.Complex(@real.numerator * (d / @real.denominator),
@imag.numerator * (d / @imag.denominator),
)
end
def polar
[abs, arg]
end
def rationalize(eps = undefined)
%x{
if (arguments.length > 1) {
#{::Kernel.raise ::ArgumentError, "wrong number of arguments (#{`arguments.length`} for 0..1)"};
}
}
if @imag != 0
::Kernel.raise ::RangeError, "can't convert #{self} into Rational"
end
real.rationalize(eps)
end
def real?
false
end
def rect
[@real, @imag]
end
def to_f
unless @imag == 0
::Kernel.raise ::RangeError, "can't convert #{self} into Float"
end
@real.to_f
end
def to_i
unless @imag == 0
::Kernel.raise ::RangeError, "can't convert #{self} into Integer"
end
@real.to_i
end
def to_r
unless @imag == 0
::Kernel.raise ::RangeError, "can't convert #{self} into Rational"
end
@real.to_r
end
def to_s
result = @real.inspect
result +=
if (::Number === @imag && @imag.nan?) || @imag.positive? || @imag.zero?
'+'
else
'-'
end
result += @imag.abs.inspect
if ::Number === @imag && (@imag.nan? || @imag.infinite?)
result += '*'
end
result + 'i'
end
I = new(0, 1)
def self.from_string(str)
%x{
var re = /[+-]?[\d_]+(\.[\d_]+)?(e\d+)?/,
match = str.match(re),
real, imag, denominator;
function isFloat() {
return re.test(str);
}
function cutFloat() {
var match = str.match(re);
var number = match[0];
str = str.slice(number.length);
return number.replace(/_/g, '');
}
// handles both floats and rationals
function cutNumber() {
if (isFloat()) {
var numerator = parseFloat(cutFloat());
if (str[0] === '/') {
// rational real part
str = str.slice(1);
if (isFloat()) {
var denominator = parseFloat(cutFloat());
return #{::Kernel.Rational(`numerator`, `denominator`)};
} else {
// reverting '/'
str = '/' + str;
return numerator;
}
} else {
// float real part, no denominator
return numerator;
}
} else {
return null;
}
}
real = cutNumber();
if (!real) {
if (str[0] === 'i') {
// i => Complex(0, 1)
return #{::Kernel.Complex(0, 1)};
}
if (str[0] === '-' && str[1] === 'i') {
// -i => Complex(0, -1)
return #{::Kernel.Complex(0, -1)};
}
if (str[0] === '+' && str[1] === 'i') {
// +i => Complex(0, 1)
return #{::Kernel.Complex(0, 1)};
}
// anything => Complex(0, 0)
return #{::Kernel.Complex(0, 0)};
}
imag = cutNumber();
if (!imag) {
if (str[0] === 'i') {
// 3i => Complex(0, 3)
return #{::Kernel.Complex(0, `real`)};
} else {
// 3 => Complex(3, 0)
return #{::Kernel.Complex(`real`, 0)};
}
} else {
// 3+2i => Complex(3, 2)
return #{::Kernel.Complex(`real`, `imag`)};
}
}
end
class << self
alias rectangular rect
end
alias arg angle
alias conjugate conj
alias divide /
alias imaginary imag
alias magnitude abs
alias phase arg
alias quo /
alias rectangular rect
undef negative?
undef positive?
undef step
end