mruby-complex: implement Complex#** in C

Implement the Complex#** method in C. This method calculates complex
exponentiation using `exp(w * log(z))` for complex exponents and
`(abs(z)**n) * Complex.polar(1, n * arg(z))` for real exponents.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-16 12:03:12 +09:00
parent 8b2975424f
commit ee4d349f2e
2 changed files with 53 additions and 0 deletions
+44
View File
@@ -513,6 +513,49 @@ nil_to_c(mrb_state *mrb, mrb_value self)
return complex_new(mrb, 0, 0);
}
/*
* call-seq:
* cmp ** numeric -> complex
*
* Returns the result of raising cmp to the power of numeric.
*
* Complex(1, 2) ** 2 #=> (-3+4i)
* Complex(1, 2) ** Complex(1, 0) #=> (1+2i)
*/
static mrb_value
complex_pow(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
struct mrb_complex *c_self = complex_ptr(mrb, self);
mrb_float self_real = c_self->real;
mrb_float self_imaginary = c_self->imaginary;
if (mrb_type(other) == MRB_TT_COMPLEX) {
struct mrb_complex *c_other = complex_ptr(mrb, other);
mrb_float x = c_other->real;
mrb_float y = c_other->imaginary;
mrb_float log_abs_self = F(log)(F(hypot)(self_real, self_imaginary));
mrb_float arg_self = F(atan2)(self_imaginary, self_real);
mrb_float a = x * log_abs_self - y * arg_self;
mrb_float b = x * arg_self + y * log_abs_self;
mrb_float exp_a = F(exp)(a);
return mrb_complex_new(mrb, exp_a * F(cos)(b), exp_a * F(sin)(b));
} else {
mrb_float other_float = mrb_as_float(mrb, other);
mrb_float abs_self = F(hypot)(self_real, self_imaginary);
mrb_float arg_self = F(atan2)(self_imaginary, self_real);
mrb_float pow_abs_self = F(pow)(abs_self, other_float);
mrb_float new_arg = arg_self * other_float;
return mrb_complex_new(mrb, pow_abs_self * F(cos)(new_arg), pow_abs_self * F(sin)(new_arg));
}
}
void mrb_mruby_complex_gem_init(mrb_state *mrb)
{
struct RClass *comp;
@@ -537,6 +580,7 @@ void mrb_mruby_complex_gem_init(mrb_state *mrb)
mrb_define_method_id(mrb, comp, MRB_SYM(quo), complex_div, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, comp, MRB_OPSYM(eq), complex_eq, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, comp, MRB_SYM(hash), complex_hash, MRB_ARGS_NONE());
mrb_define_method_id(mrb, comp, MRB_OPSYM(pow), complex_pow, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, mrb->nil_class, MRB_SYM(to_c), nil_to_c, MRB_ARGS_NONE());
}
+9
View File
@@ -163,3 +163,12 @@ assert 'Complex#frozen?' do
assert_predicate(Complex(2,3), :frozen?)
assert_predicate(4+5i, :frozen?)
end
assert 'Complex#**' do
assert_complex Complex(2, 3) ** 2, Complex(-5, 12)
assert_complex Complex(2, 3) ** 0, Complex(1, 0)
assert_complex Complex(2, 3) ** 1, Complex(2, 3)
assert_complex Complex(2, 3) ** Complex(1, 0), Complex(2, 3)
assert_complex Complex(0, 1) ** 2, Complex(-1, 0)
assert_complex Complex(0, 1) ** Complex(0, 1), Complex(Math::E ** (-Math::PI / 2), 0)
end