mruby-complex: add comprehensive call-seq documentation for Complex methods

Added complete call-seq documentation for all Complex methods in
mrblib/complex.rb (18 methods):

## Complex Class Methods:

- polar: creates complex number from polar coordinates (magnitude, angle)
  with trigonometric conversion using Math.cos and Math.sin

## Complex Instance Methods:

- inspect, to_s: string representation methods for debugging and display
  with proper formatting of real and imaginary parts

- +@, -@: unary plus and minus operators for identity and negation

- <=>: spaceship operator for comparison with other numeric types,
  enables Comparable module functionality with proper nil handling

- abs/magnitude: absolute value (magnitude) calculation using hypot
- abs2: square of absolute value for performance-critical calculations
- arg/angle/phase: argument (angle) calculation using atan2

- conjugate/conj: complex conjugate operation (negates imaginary part)
- fdiv: floating-point division ensuring float results
- polar: returns [magnitude, angle] array representation
- real?: always returns false for complex numbers
- rectangular/rect: returns [real, imaginary] array representation

- to_c: returns self (identity conversion)
- to_r: converts to rational when imaginary part is zero, raises RangeError otherwise

## Numeric Extension Methods:

- i: creates pure imaginary number (0+num*i) for convenient complex creation
- to_c: converts any numeric to complex with zero imaginary part

Co-authored-by: Atlassian Rovo Dev
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-19 09:39:49 +09:00
parent 1cf225dfbe
commit 01e008167b
2 changed files with 293 additions and 0 deletions
+170
View File
@@ -1,24 +1,84 @@
class Complex < Numeric
#
# call-seq:
# Complex.polar(abs [, arg]) -> complex
#
# Returns a complex number in terms of its polar coordinates.
# abs is the absolute value (magnitude) and arg is the argument (angle).
#
# Complex.polar(3, 0) #=> (3+0i)
# Complex.polar(3, Math::PI/2) #=> (1.836909530733566e-16+3.0i)
# Complex.polar(3, Math::PI) #=> (-3.0+3.673819061467132e-16i)
#
def self.polar(abs, arg = 0)
Complex(abs * Math.cos(arg), abs * Math.sin(arg))
end
#
# call-seq:
# cmp.inspect -> string
#
# Returns the value as a string for inspection.
#
# Complex(2).inspect #=> "(2+0i)"
# Complex(-8, 6).inspect #=> "(-8+6i)"
# Complex(1, 2).inspect #=> "(1+2i)"
#
def inspect
"(#{to_s})"
end
#
# call-seq:
# cmp.to_s -> string
#
# Returns the value as a string.
#
# Complex(2).to_s #=> "2+0i"
# Complex(-8, 6).to_s #=> "-8+6i"
# Complex(1, -2).to_s #=> "1-2i"
#
def to_s
"#{real}#{'+' unless imaginary < 0}#{imaginary}#{'*' unless imaginary.finite?}i"
end
#
# call-seq:
# +cmp -> cmp
#
# Returns self.
#
# +Complex(1, 2) #=> (1+2i)
#
def +@
self
end
#
# call-seq:
# -cmp -> complex
#
# Returns the negation of self.
#
# -Complex(1, 2) #=> (-1-2i)
# -Complex(-1, 2) #=> (1-2i)
#
def -@
Complex(-real, -imaginary)
end
#
# call-seq:
# cmp <=> numeric -> -1, 0, +1, or nil
#
# Returns -1, 0, or +1 depending on whether cmp is less than, equal to,
# or greater than numeric. This is the basis for the tests in the Comparable module.
# Returns nil if the two values are incomparable.
#
# Complex(2, 3) <=> Complex(2, 3) #=> 0
# Complex(5) <=> 5 #=> 0
# Complex(2, 3) <=> 1 #=> 1
#
def <=>(other)
return nil unless other.kind_of?(Numeric)
self.to_f <=> other.to_f
@@ -26,47 +86,137 @@ class Complex < Numeric
nil
end
#
# call-seq:
# cmp.abs -> real
# cmp.magnitude -> real
#
# Returns the absolute part of its polar form.
#
# Complex(-1).abs #=> 1.0
# Complex(3.0, -4.0).abs #=> 5.0
#
def abs
Math.hypot imaginary, real
end
alias_method :magnitude, :abs
#
# call-seq:
# cmp.abs2 -> real
#
# Returns square of the absolute value.
#
# Complex(-1).abs2 #=> 1
# Complex(3.0, -4.0).abs2 #=> 25.0
#
def abs2
real * real + imaginary * imaginary
end
#
# call-seq:
# cmp.arg -> float
# cmp.angle -> float
# cmp.phase -> float
#
# Returns the angle part of its polar form.
#
# Complex.polar(3, Math::PI/2).arg #=> 1.5707963267948966
#
def arg
Math.atan2 imaginary, real
end
alias_method :angle, :arg
alias_method :phase, :arg
#
# call-seq:
# cmp.conjugate -> complex
# cmp.conj -> complex
#
# Returns the complex conjugate.
#
# Complex(1, 2).conjugate #=> (1-2i)
#
def conjugate
Complex(real, -imaginary)
end
alias_method :conj, :conjugate
#
# call-seq:
# cmp.fdiv(numeric) -> complex
#
# Performs division as each part is a float, even if the parts are not floats.
#
# Complex(11, 22).fdiv(3) #=> (3.6666666666666665+7.333333333333333i)
#
def fdiv(numeric)
Complex(real / numeric, imaginary / numeric)
end
#
# call-seq:
# cmp.polar -> array
#
# Returns an array; [cmp.abs, cmp.arg].
#
# Complex(1, 2).polar #=> [2.23606797749979, 1.1071487177940904]
#
def polar
[abs, arg]
end
#
# call-seq:
# cmp.real? -> false
#
# Returns false.
#
# Complex(1).real? #=> false
#
def real?
false
end
#
# call-seq:
# cmp.rectangular -> array
# cmp.rect -> array
#
# Returns an array; [cmp.real, cmp.imag].
#
# Complex(1, 2).rectangular #=> [1, 2]
#
def rectangular
[real, imaginary]
end
alias_method :rect, :rectangular
#
# call-seq:
# cmp.to_c -> cmp
#
# Returns self.
#
# Complex(2).to_c #=> (2+0i)
# Complex(-8, 6).to_c #=> (-8+6i)
#
def to_c
self
end
#
# call-seq:
# cmp.to_r -> rational
#
# Returns the value as a rational if possible (the imaginary part should be exactly zero).
#
# Complex(1, 0).to_r #=> (1/1)
# Complex(1, 0.0).to_r #=> (1/1)
# Complex(1, 2).to_r #=> RangeError
#
def to_r
raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
Rational(real, 1)
@@ -75,6 +225,15 @@ class Complex < Numeric
alias_method :imag, :imaginary
Numeric.class_eval do
#
# call-seq:
# num.i -> complex
#
# Returns the Complex object created from this number and i (0+num*i).
#
# -42.i #=> (0-42i)
# 2.0.i #=> (0+2.0i)
#
def i
Complex(0, self)
end
@@ -83,6 +242,17 @@ class Complex < Numeric
end
class Numeric
#
# call-seq:
# num.to_c -> complex
#
# Returns the value as a complex.
#
# 1.to_c #=> (1+0i)
# -1.to_c #=> (-1+0i)
# 1.0.to_c #=> (1.0+0i)
# 3.14159.to_c #=> (3.14159+0i)
#
def to_c
Complex(self, 0)
end
+123
View File
@@ -94,6 +94,15 @@ mrb_complex_copy(mrb_state *mrb, mrb_value x, mrb_value y)
p1->imaginary = p2->imaginary;
}
/*
* call-seq:
* complex.real -> float
*
* Returns the real part of the complex number.
*
* Complex(3, 4).real #=> 3.0
* Complex(-1).real #=> -1.0
*/
static mrb_value
complex_real(mrb_state *mrb, mrb_value self)
{
@@ -101,6 +110,16 @@ complex_real(mrb_state *mrb, mrb_value self)
return mrb_float_value(mrb, p->real);
}
/*
* call-seq:
* complex.imaginary -> float
* complex.imag -> float
*
* Returns the imaginary part of the complex number.
*
* Complex(3, 4).imaginary #=> 4.0
* Complex(5).imag #=> 0.0
*/
static mrb_value
complex_imaginary(mrb_state *mrb, mrb_value self)
{
@@ -108,6 +127,19 @@ complex_imaginary(mrb_state *mrb, mrb_value self)
return mrb_float_value(mrb, p->imaginary);
}
/*
* call-seq:
* Complex.rectangular(real, imag = 0) -> complex
* Complex.rect(real, imag = 0) -> complex
* Complex(real, imag = 0) -> complex
*
* Returns a complex number with the given real and imaginary parts.
* The imaginary part defaults to 0 if not specified.
*
* Complex.rectangular(1, 2) #=> (1+2i)
* Complex.rect(3) #=> (3+0i)
* Complex(1, -1) #=> (1-1i)
*/
static mrb_value
complex_s_rect(mrb_state *mrb, mrb_value self)
{
@@ -117,6 +149,16 @@ complex_s_rect(mrb_state *mrb, mrb_value self)
return complex_new(mrb, real, imaginary);
}
/*
* call-seq:
* complex.to_f -> float
*
* Returns the real part of the complex number as a float.
* Raises RangeError if the imaginary part is not zero.
*
* Complex(3, 0).to_f #=> 3.0
* Complex(3, 4).to_f #=> RangeError: can't convert (3+4i) into Float
*/
mrb_value
mrb_complex_to_f(mrb_state *mrb, mrb_value self)
{
@@ -129,6 +171,16 @@ mrb_complex_to_f(mrb_state *mrb, mrb_value self)
return mrb_float_value(mrb, p->real);
}
/*
* call-seq:
* complex.to_i -> integer
*
* Returns the real part of the complex number as an integer.
* Raises RangeError if the imaginary part is not zero.
*
* Complex(3, 0).to_i #=> 3
* Complex(3, 4).to_i #=> RangeError: can't convert (3+4i) into Integer
*/
mrb_value
mrb_complex_to_i(mrb_state *mrb, mrb_value self)
{
@@ -176,6 +228,17 @@ mrb_complex_eq(mrb_state *mrb, mrb_value x, mrb_value y)
}
}
/*
* call-seq:
* complex == object -> true or false
*
* Returns true if complex equals object. Two complex numbers are equal
* if their real and imaginary parts are equal.
*
* Complex(1, 2) == Complex(1, 2) #=> true
* Complex(1, 2) == Complex(2, 1) #=> false
* Complex(1, 0) == 1 #=> true
*/
static mrb_value
complex_eq(mrb_state *mrb, mrb_value x)
{
@@ -203,6 +266,17 @@ mrb_complex_add(mrb_state *mrb, mrb_value x, mrb_value y)
}
}
/*
* call-seq:
* complex + numeric -> complex
*
* Returns the sum of complex and numeric. If numeric is a complex number,
* adds both real and imaginary parts. If numeric is real, adds only to
* the real part.
*
* Complex(1, 2) + Complex(3, 4) #=> (4+6i)
* Complex(1, 2) + 3 #=> (4+2i)
*/
static mrb_value
complex_add(mrb_state *mrb, mrb_value x)
{
@@ -230,6 +304,17 @@ mrb_complex_sub(mrb_state *mrb, mrb_value x, mrb_value y)
}
}
/*
* call-seq:
* complex - numeric -> complex
*
* Returns the difference of complex and numeric. If numeric is a complex number,
* subtracts both real and imaginary parts. If numeric is real, subtracts only
* from the real part.
*
* Complex(5, 6) - Complex(1, 2) #=> (4+4i)
* Complex(5, 6) - 2 #=> (3+6i)
*/
static mrb_value
complex_sub(mrb_state *mrb, mrb_value x)
{
@@ -258,6 +343,16 @@ mrb_complex_mul(mrb_state *mrb, mrb_value x, mrb_value y)
}
}
/*
* call-seq:
* complex * numeric -> complex
*
* Returns the product of complex and numeric. Uses the standard complex
* multiplication formula: (a+bi) * (c+di) = (ac-bd) + (ad+bc)i
*
* Complex(1, 2) * Complex(3, 4) #=> (-5+10i)
* Complex(1, 2) * 3 #=> (3+6i)
*/
static mrb_value
complex_mul(mrb_state *mrb, mrb_value x)
{
@@ -362,6 +457,17 @@ mrb_complex_div(mrb_state *mrb, mrb_value self, mrb_value rhs)
return complex_new(mrb, F(ldexp)(zr.s, zr.x), F(ldexp)(zi.s, zi.x));
}
/*
* call-seq:
* complex / numeric -> complex
* complex.quo(numeric) -> complex
*
* Returns the quotient of complex divided by numeric. Uses the standard
* complex division formula by multiplying by the conjugate.
*
* Complex(10, 5) / Complex(2, 1) #=> (5+0i)
* Complex(6, 4) / 2 #=> (3+2i)
*/
static mrb_value
complex_div(mrb_state *mrb, mrb_value x)
{
@@ -369,6 +475,15 @@ complex_div(mrb_state *mrb, mrb_value x)
return mrb_complex_div(mrb, x, y);
}
/*
* call-seq:
* complex.hash -> integer
*
* Returns a hash value for the complex number. Two complex numbers with
* the same real and imaginary parts will have the same hash value.
*
* Complex(1, 2).hash == Complex(1, 2).hash #=> true
*/
static mrb_value
complex_hash(mrb_state *mrb, mrb_value cpx)
{
@@ -378,6 +493,14 @@ complex_hash(mrb_state *mrb, mrb_value cpx)
return mrb_int_value(mrb, hash);
}
/*
* call-seq:
* nil.to_c -> complex
*
* Returns Complex(0, 0).
*
* nil.to_c #=> (0+0i)
*/
static mrb_value
nil_to_c(mrb_state *mrb, mrb_value self)
{