first commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
MRuby::Gem::Specification.new('mruby-complex') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'Complex class'
|
||||
|
||||
spec.add_dependency 'mruby-math', core: 'mruby-math'
|
||||
end
|
||||
@@ -0,0 +1,122 @@
|
||||
class Complex < Numeric
|
||||
def self.polar(abs, arg = 0)
|
||||
Complex(abs * Math.cos(arg), abs * Math.sin(arg))
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(#{to_s})"
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{real}#{'+' unless imaginary < 0}#{imaginary}i"
|
||||
end
|
||||
|
||||
def +@
|
||||
Complex(real, imaginary)
|
||||
end
|
||||
|
||||
def -@
|
||||
Complex(-real, -imaginary)
|
||||
end
|
||||
|
||||
def +(rhs)
|
||||
if rhs.is_a? Complex
|
||||
Complex(real + rhs.real, imaginary + rhs.imaginary)
|
||||
elsif rhs.is_a? Numeric
|
||||
Complex(real + rhs, imaginary)
|
||||
end
|
||||
end
|
||||
|
||||
def -(rhs)
|
||||
if rhs.is_a? Complex
|
||||
Complex(real - rhs.real, imaginary - rhs.imaginary)
|
||||
elsif rhs.is_a? Numeric
|
||||
Complex(real - rhs, imaginary)
|
||||
end
|
||||
end
|
||||
|
||||
def *(rhs)
|
||||
if rhs.is_a? Complex
|
||||
Complex(real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + rhs.real * imaginary)
|
||||
elsif rhs.is_a? Numeric
|
||||
Complex(real * rhs, imaginary * rhs)
|
||||
end
|
||||
end
|
||||
|
||||
def /(rhs)
|
||||
if rhs.is_a? Complex
|
||||
__div__(rhs)
|
||||
elsif rhs.is_a? Numeric
|
||||
Complex(real / rhs, imaginary / rhs)
|
||||
end
|
||||
end
|
||||
alias_method :quo, :/
|
||||
|
||||
def ==(rhs)
|
||||
if rhs.is_a? Complex
|
||||
real == rhs.real && imaginary == rhs.imaginary
|
||||
elsif rhs.is_a? Numeric
|
||||
imaginary == 0 && real == rhs
|
||||
end
|
||||
end
|
||||
|
||||
def abs
|
||||
Math.hypot imaginary, real
|
||||
end
|
||||
alias_method :magnitude, :abs
|
||||
|
||||
def abs2
|
||||
real * real + imaginary * imaginary
|
||||
end
|
||||
|
||||
def arg
|
||||
Math.atan2 imaginary, real
|
||||
end
|
||||
alias_method :angle, :arg
|
||||
alias_method :phase, :arg
|
||||
|
||||
def conjugate
|
||||
Complex(real, -imaginary)
|
||||
end
|
||||
alias_method :conj, :conjugate
|
||||
|
||||
def fdiv(numeric)
|
||||
Complex(real.to_f / numeric, imaginary.to_f / numeric)
|
||||
end
|
||||
|
||||
def polar
|
||||
[abs, arg]
|
||||
end
|
||||
|
||||
def real?
|
||||
false
|
||||
end
|
||||
|
||||
def rectangular
|
||||
[real, imaginary]
|
||||
end
|
||||
alias_method :rect, :rectangular
|
||||
|
||||
def to_r
|
||||
raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
|
||||
Rational(real, 1)
|
||||
end
|
||||
|
||||
alias_method :imag, :imaginary
|
||||
|
||||
[Fixnum, Float].each do |cls|
|
||||
[:+, :-, :*, :/, :==].each do |op|
|
||||
cls.instance_eval do
|
||||
original_operator_name = :"__original_operator_#{op}_complex"
|
||||
alias_method original_operator_name, op
|
||||
define_method op do |rhs|
|
||||
if rhs.is_a? Complex
|
||||
Complex(self).__send__(op, rhs)
|
||||
else
|
||||
__send__(original_operator_name, rhs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,248 @@
|
||||
#include <mruby.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef MRB_WITHOUT_FLOAT
|
||||
# error Complex conflicts 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
|
||||
#endif
|
||||
|
||||
struct mrb_complex {
|
||||
mrb_float real;
|
||||
mrb_float imaginary;
|
||||
};
|
||||
|
||||
#ifdef MRB_USE_FLOAT
|
||||
#define F(x) x##f
|
||||
#else
|
||||
#define F(x) x
|
||||
#endif
|
||||
|
||||
#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT)
|
||||
|
||||
#define COMPLEX_USE_ISTRUCT
|
||||
/* use TT_ISTRUCT */
|
||||
#include <mruby/istruct.h>
|
||||
|
||||
#define complex_ptr(mrb, v) (struct mrb_complex*)mrb_istruct_ptr(v)
|
||||
|
||||
static struct RBasic*
|
||||
complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
|
||||
{
|
||||
struct RIStruct *s;
|
||||
|
||||
s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
|
||||
*p = (struct mrb_complex*)s->inline_data;
|
||||
|
||||
return (struct RBasic*)s;
|
||||
}
|
||||
|
||||
#else
|
||||
/* use TT_DATA */
|
||||
#include <mruby/data.h>
|
||||
|
||||
static const struct mrb_data_type mrb_complex_type = {"Complex", mrb_free};
|
||||
|
||||
static struct RBasic*
|
||||
complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
|
||||
{
|
||||
struct RData *d;
|
||||
|
||||
Data_Make_Struct(mrb, c, struct mrb_complex, &mrb_complex_type, *p, d);
|
||||
|
||||
return (struct RBasic*)d;
|
||||
}
|
||||
|
||||
static struct mrb_complex*
|
||||
complex_ptr(mrb_state *mrb, mrb_value v)
|
||||
{
|
||||
struct mrb_complex *p;
|
||||
|
||||
p = DATA_GET_PTR(mrb, v, &mrb_complex_type, struct mrb_complex);
|
||||
if (!p) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized complex");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
static mrb_value
|
||||
complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
|
||||
{
|
||||
struct RClass *c = mrb_class_get(mrb, "Complex");
|
||||
struct mrb_complex *p;
|
||||
struct RBasic *comp = complex_alloc(mrb, c, &p);
|
||||
p->real = real;
|
||||
p->imaginary = imaginary;
|
||||
MRB_SET_FROZEN_FLAG(comp);
|
||||
|
||||
return mrb_obj_value(comp);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_real(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct mrb_complex *p = complex_ptr(mrb, self);
|
||||
return mrb_float_value(mrb, p->real);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_imaginary(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct mrb_complex *p = complex_ptr(mrb, self);
|
||||
return mrb_float_value(mrb, p->imaginary);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_s_rect(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_float real, imaginary = 0.0;
|
||||
|
||||
mrb_get_args(mrb, "f|f", &real, &imaginary);
|
||||
return complex_new(mrb, real, imaginary);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_to_f(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct mrb_complex *p = complex_ptr(mrb, self);
|
||||
|
||||
if (p->imaginary != 0) {
|
||||
mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
|
||||
}
|
||||
|
||||
return mrb_float_value(mrb, p->real);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_to_i(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct mrb_complex *p = complex_ptr(mrb, self);
|
||||
|
||||
if (p->imaginary != 0) {
|
||||
mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
|
||||
}
|
||||
return mrb_int_value(mrb, p->real);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_to_c(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Arithmetic on (significand, exponent) pairs avoids premature overflow in
|
||||
complex division */
|
||||
struct float_pair {
|
||||
mrb_float s;
|
||||
int x;
|
||||
};
|
||||
|
||||
static void
|
||||
add_pair(struct float_pair *s, struct float_pair const *a,
|
||||
struct float_pair const *b)
|
||||
{
|
||||
if (b->s == 0.0F) {
|
||||
*s = *a;
|
||||
} else if (a->s == 0.0F) {
|
||||
*s = *b;
|
||||
} else if (a->x >= b->x) {
|
||||
s->s = a->s + F(ldexp)(b->s, b->x - a->x);
|
||||
s->x = a->x;
|
||||
} else {
|
||||
s->s = F(ldexp)(a->s, a->x - b->x) + b->s;
|
||||
s->x = b->x;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mul_pair(struct float_pair *p, struct float_pair const *a,
|
||||
struct float_pair const *b)
|
||||
{
|
||||
p->s = a->s * b->s;
|
||||
p->x = a->x + b->x;
|
||||
}
|
||||
|
||||
static void
|
||||
div_pair(struct float_pair *q, struct float_pair const *a,
|
||||
struct float_pair const *b)
|
||||
{
|
||||
q->s = a->s / b->s;
|
||||
q->x = a->x - b->x;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
complex_div(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value rhs = mrb_get_arg1(mrb);
|
||||
struct mrb_complex *a, *b;
|
||||
struct float_pair ar, ai, br, bi;
|
||||
struct float_pair br2, bi2;
|
||||
struct float_pair div;
|
||||
struct float_pair ar_br, ai_bi;
|
||||
struct float_pair ai_br, ar_bi;
|
||||
struct float_pair zr, zi;
|
||||
|
||||
a = complex_ptr(mrb, self);
|
||||
b = complex_ptr(mrb, rhs);
|
||||
|
||||
/* Split floating point components into significand and exponent */
|
||||
ar.s = F(frexp)(a->real, &ar.x);
|
||||
ai.s = F(frexp)(a->imaginary, &ai.x);
|
||||
br.s = F(frexp)(b->real, &br.x);
|
||||
bi.s = F(frexp)(b->imaginary, &bi.x);
|
||||
|
||||
/* Perform arithmetic on (significand, exponent) pairs to produce
|
||||
the result: */
|
||||
|
||||
/* the divisor */
|
||||
mul_pair(&br2, &br, &br);
|
||||
mul_pair(&bi2, &bi, &bi);
|
||||
add_pair(&div, &br2, &bi2);
|
||||
|
||||
/* real component */
|
||||
mul_pair(&ar_br, &ar, &br);
|
||||
mul_pair(&ai_bi, &ai, &bi);
|
||||
add_pair(&zr, &ar_br, &ai_bi);
|
||||
div_pair(&zr, &zr, &div);
|
||||
|
||||
/* imaginary component */
|
||||
mul_pair(&ai_br, &ai, &br);
|
||||
mul_pair(&ar_bi, &ar, &bi);
|
||||
ar_bi.s = -ar_bi.s;
|
||||
add_pair(&zi, &ai_br, &ar_bi);
|
||||
div_pair(&zi, &zi, &div);
|
||||
|
||||
/* assemble the result */
|
||||
return complex_new(mrb, F(ldexp)(zr.s, zr.x), F(ldexp)(zi.s, zi.x));
|
||||
}
|
||||
|
||||
void mrb_mruby_complex_gem_init(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *comp;
|
||||
|
||||
#ifdef COMPLEX_USE_ISTRUCT
|
||||
mrb_assert(sizeof(struct mrb_complex) < ISTRUCT_DATA_SIZE);
|
||||
#endif
|
||||
comp = mrb_define_class(mrb, "Complex", mrb_class_get(mrb, "Numeric"));
|
||||
#ifdef COMPLEX_USE_ISTRUCT
|
||||
MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
|
||||
#else
|
||||
MRB_SET_INSTANCE_TT(comp, MRB_TT_DATA);
|
||||
#endif
|
||||
mrb_undef_class_method(mrb, comp, "new");
|
||||
mrb_define_class_method(mrb, comp, "rectangular", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
|
||||
mrb_define_class_method(mrb, comp, "rect", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
|
||||
mrb_define_method(mrb, mrb->kernel_module, "Complex", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
|
||||
mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, comp, "__div__", complex_div, MRB_ARGS_REQ(1));
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_complex_gem_final(mrb_state* mrb)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
def assert_complex(real, exp)
|
||||
assert "assert_complex" do
|
||||
assert_float real.real, exp.real
|
||||
assert_float real.imaginary, exp.imaginary
|
||||
end
|
||||
end
|
||||
|
||||
assert 'Complex' do
|
||||
c = 123i
|
||||
assert_equal Complex, c.class
|
||||
assert_equal [c.real, c.imaginary], [0, 123]
|
||||
c = 123 + -1.23i
|
||||
assert_equal Complex, c.class
|
||||
assert_equal [c.real, c.imaginary], [123, -1.23]
|
||||
end
|
||||
|
||||
assert 'Complex::polar' do
|
||||
assert_complex Complex.polar(3, 0), (3 + 0i)
|
||||
assert_complex Complex.polar(3, Math::PI/2), (0 + 3i)
|
||||
assert_complex Complex.polar(3, Math::PI), (-3 + 0i)
|
||||
assert_complex Complex.polar(3, -Math::PI/2), (0 + -3i)
|
||||
end
|
||||
|
||||
assert 'Complex::rectangular' do
|
||||
assert_complex Complex.rectangular(1, 2), (1 + 2i)
|
||||
end
|
||||
|
||||
assert 'Complex#*' do
|
||||
assert_complex Complex(2, 3) * Complex(2, 3), (-5 + 12i)
|
||||
assert_complex Complex(900) * Complex(1), (900 + 0i)
|
||||
assert_complex Complex(-2, 9) * Complex(-9, 2), (0 - 85i)
|
||||
assert_complex Complex(9, 8) * 4, (36 + 32i)
|
||||
assert_complex Complex(20, 9) * 9.8, (196.0 + 88.2i)
|
||||
end
|
||||
|
||||
assert 'Complex#+' do
|
||||
assert_complex Complex(2, 3) + Complex(2, 3) , (4 + 6i)
|
||||
assert_complex Complex(900) + Complex(1) , (901 + 0i)
|
||||
assert_complex Complex(-2, 9) + Complex(-9, 2), (-11 + 11i)
|
||||
assert_complex Complex(9, 8) + 4 , (13 + 8i)
|
||||
assert_complex Complex(20, 9) + 9.8 , (29.8 + 9i)
|
||||
end
|
||||
|
||||
assert 'Complex#-' do
|
||||
assert_complex Complex(2, 3) - Complex(2, 3) , (0 + 0i)
|
||||
assert_complex Complex(900) - Complex(1) , (899 + 0i)
|
||||
assert_complex Complex(-2, 9) - Complex(-9, 2), (7 + 7i)
|
||||
assert_complex Complex(9, 8) - 4 , (5 + 8i)
|
||||
assert_complex Complex(20, 9) - 9.8 , (10.2 + 9i)
|
||||
end
|
||||
|
||||
assert 'Complex#-@' do
|
||||
assert_complex(-Complex(1, 2), (-1 - 2i))
|
||||
end
|
||||
|
||||
assert 'Complex#/' do
|
||||
assert_complex Complex(2, 3) / Complex(2, 3) , (1 + 0i)
|
||||
assert_complex Complex(900) / Complex(1) , (900 + 0i)
|
||||
assert_complex Complex(-2, 9) / Complex(-9, 2), ((36 / 85) - (77i / 85))
|
||||
assert_complex Complex(9, 8) / 4 , ((9 / 4) + 2i)
|
||||
assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i)
|
||||
if 1e39.infinite? then
|
||||
# MRB_USE_FLOAT in effect
|
||||
ten = 1e21
|
||||
one = 1e20
|
||||
else
|
||||
ten = 1e201
|
||||
one = 1e200
|
||||
end
|
||||
assert_complex Complex(ten, ten) / Complex(one, one), Complex(10.0, 0.0)
|
||||
end
|
||||
|
||||
assert 'Complex#==' do
|
||||
assert_true Complex(2, 3) == Complex(2, 3)
|
||||
assert_true Complex(5) == 5
|
||||
assert_true Complex(0) == 0.0
|
||||
end
|
||||
|
||||
assert 'Complex#abs' do
|
||||
assert_float Complex(-1).abs, 1
|
||||
assert_float Complex(3.0, -4.0).abs, 5.0
|
||||
if 1e39.infinite? then
|
||||
# MRB_USE_FLOAT in effect
|
||||
exp = 125
|
||||
else
|
||||
exp = 1021
|
||||
end
|
||||
assert_true Complex(3.0*2.0**exp, 4.0*2.0**exp).abs.finite?
|
||||
assert_float Complex(3.0*2.0**exp, 4.0*2.0**exp).abs, 5.0*2.0**exp
|
||||
end
|
||||
|
||||
assert 'Complex#abs2' do
|
||||
assert_float Complex(-1).abs2, 1
|
||||
assert_float Complex(3.0, -4.0).abs2, 25.0
|
||||
end
|
||||
|
||||
assert 'Complex#arg' do
|
||||
assert_float Complex.polar(3, Math::PI/2).arg, 1.5707963267948966
|
||||
end
|
||||
|
||||
assert 'Complex#conjugate' do
|
||||
assert_complex Complex(1, 2).conjugate, (1 - 2i)
|
||||
end
|
||||
|
||||
assert 'Complex#fdiv' do
|
||||
assert_complex Complex(11, 22).fdiv(3), (3.6666666666666665 + 7.333333333333333i)
|
||||
end
|
||||
|
||||
assert 'Complex#imaginary' do
|
||||
assert_float Complex(7).imaginary , 0
|
||||
assert_float Complex(9, -4).imaginary, -4
|
||||
end
|
||||
|
||||
assert 'Complex#polar' do
|
||||
assert_equal Complex(1, 2).polar, [2.23606797749979, 1.1071487177940904]
|
||||
end
|
||||
|
||||
assert 'Complex#real' do
|
||||
assert_float Complex(7).real, 7
|
||||
assert_float Complex(9, -4).real, 9
|
||||
end
|
||||
|
||||
assert 'Complex#real?' do
|
||||
assert_false Complex(1).real?
|
||||
end
|
||||
|
||||
assert 'Complex::rectangular' do
|
||||
assert_equal Complex(1, 2).rectangular, [1, 2]
|
||||
end
|
||||
|
||||
assert 'Complex::to_c' do
|
||||
assert_equal Complex(1, 2).to_c, Complex(1, 2)
|
||||
end
|
||||
|
||||
assert 'Complex::to_f' do
|
||||
assert_float Complex(1, 0).to_f, 1.0
|
||||
assert_raise(RangeError) do
|
||||
Complex(1, 2).to_f
|
||||
end
|
||||
end
|
||||
|
||||
assert 'Complex::to_i' do
|
||||
assert_equal Complex(1, 0).to_i, 1
|
||||
assert_raise(RangeError) do
|
||||
Complex(1, 2).to_i
|
||||
end
|
||||
end
|
||||
|
||||
assert 'Complex#frozen?' do
|
||||
assert_predicate(1i, :frozen?)
|
||||
assert_predicate(Complex(2,3), :frozen?)
|
||||
assert_predicate(4+5i, :frozen?)
|
||||
end
|
||||
Reference in New Issue
Block a user