diff --git a/ext/symengine/CMakeLists.txt b/ext/symengine/CMakeLists.txt index b587bb6..55c8ea1 100644 --- a/ext/symengine/CMakeLists.txt +++ b/ext/symengine/CMakeLists.txt @@ -2,10 +2,11 @@ set(RUBY_WRAPPER_SRC ruby_basic.c ruby_symbol.c ruby_integer.c - ruby_rational.c + ruby_complex.c ruby_constant.c ruby_function.c ruby_ntheory.c + ruby_utils.c symengine_utils.c symengine.c ) diff --git a/ext/symengine/ruby_complex.c b/ext/symengine/ruby_complex.c new file mode 100644 index 0000000..eba2437 --- /dev/null +++ b/ext/symengine/ruby_complex.c @@ -0,0 +1,9 @@ +#include "ruby_complex.h" + +VALUE ccomplex_real_part(VALUE self) { + return function_onearg(complex_real_part, self); +} + +VALUE ccomplex_imaginary_part(VALUE self) { + return function_onearg(complex_imaginary_part, self); +} diff --git a/ext/symengine/ruby_complex.h b/ext/symengine/ruby_complex.h new file mode 100644 index 0000000..7df00a7 --- /dev/null +++ b/ext/symengine/ruby_complex.h @@ -0,0 +1,13 @@ +#ifndef RUBY_COMPLEX_H_ +#define RUBY_COMPLEX_H_ + +#include +#include + +#include "symengine.h" +#include "symengine_utils.h" + +VALUE ccomplex_real_part(VALUE self); +VALUE ccomplex_imaginary_part(VALUE self); + +#endif //RUBY_COMPLEX_H_ diff --git a/ext/symengine/ruby_constant.c b/ext/symengine/ruby_constant.c index 8e110b6..8f25fe0 100644 --- a/ext/symengine/ruby_constant.c +++ b/ext/symengine/ruby_constant.c @@ -1,24 +1,28 @@ #include "ruby_constant.h" -VALUE cconstant_const(void (*cwfunc_ptr)(basic_struct*)) { +VALUE cconstant_const(void (*cwfunc_ptr)(basic_struct*), VALUE klass) { basic_struct *cresult; VALUE result; cresult = basic_new_heap(); cwfunc_ptr(cresult); - result = Data_Wrap_Struct(c_constant, NULL, cbasic_free_heap, cresult); + result = Data_Wrap_Struct(klass, NULL, cbasic_free_heap, cresult); return result; } VALUE cconstant_pi() { - return cconstant_const(basic_const_pi); + return cconstant_const(basic_const_pi, c_constant); } VALUE cconstant_e() { - return cconstant_const(basic_const_E); + return cconstant_const(basic_const_E, c_constant); } VALUE cconstant_euler_gamma() { - return cconstant_const(basic_const_EulerGamma); + return cconstant_const(basic_const_EulerGamma, c_constant); +} + +VALUE cconstant_i() { + return cconstant_const(basic_const_I, c_complex); } diff --git a/ext/symengine/ruby_constant.h b/ext/symengine/ruby_constant.h index bfaeabe..a89377e 100644 --- a/ext/symengine/ruby_constant.h +++ b/ext/symengine/ruby_constant.h @@ -7,7 +7,7 @@ #include "symengine.h" #include "symengine_utils.h" -VALUE cconstant_const(void (*cwfunc_ptr)(basic_struct*)); +VALUE cconstant_const(void (*cwfunc_ptr)(basic_struct*), VALUE klass); VALUE cconstant_pi(); @@ -15,4 +15,6 @@ VALUE cconstant_e(); VALUE cconstant_euler_gamma(); +VALUE cconstant_i(); + #endif //RUBY_CONSTANTS_H_ diff --git a/ext/symengine/ruby_rational.c b/ext/symengine/ruby_rational.c deleted file mode 100644 index 4093789..0000000 --- a/ext/symengine/ruby_rational.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "ruby_rational.h" - -VALUE crational_init(VALUE self, VALUE rat_value) { - basic_struct *this; - basic num_basic, den_basic; - - basic_new_stack(num_basic); - basic_new_stack(den_basic); - - Data_Get_Struct(self, basic_struct, this); - - VALUE num, den; - num = rb_funcall(rat_value, rb_intern("numerator"), 0, NULL); - den = rb_funcall(rat_value, rb_intern("denominator"), 0, NULL); - - GET_SYMINTFROMVAL(num, num_basic); - GET_SYMINTFROMVAL(den, den_basic); - - rational_set(this, num_basic, den_basic); - basic_free_stack(num_basic); - basic_free_stack(den_basic); - return self; -} diff --git a/ext/symengine/ruby_rational.h b/ext/symengine/ruby_rational.h deleted file mode 100644 index 6946f28..0000000 --- a/ext/symengine/ruby_rational.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef RUBY_RATIONAL_H_ -#define RUBY_RATIONAL_H_ - -#include "ruby_basic.h" - -VALUE crational_init(VALUE self, VALUE rat_value); - -#endif //RUBY_RATIONAL_H_ diff --git a/ext/symengine/ruby_utils.c b/ext/symengine/ruby_utils.c new file mode 100644 index 0000000..fd80893 --- /dev/null +++ b/ext/symengine/ruby_utils.c @@ -0,0 +1,15 @@ +#include "ruby_utils.h" + +VALUE cutils_sympify(VALUE self, VALUE operand) { + + VALUE result; + + basic_struct *cbasic_operand; + cbasic_operand = basic_new_heap(); + + sympify(operand, cbasic_operand); + result = Data_Wrap_Struct(Klass_of_Basic(cbasic_operand), NULL , cbasic_free_heap, cbasic_operand); + + return result; +} + diff --git a/ext/symengine/ruby_utils.h b/ext/symengine/ruby_utils.h new file mode 100644 index 0000000..84df641 --- /dev/null +++ b/ext/symengine/ruby_utils.h @@ -0,0 +1,9 @@ +#ifndef RUBY_UTILS_H_ +#define RUBY_UTILS_H_ + +#include "symengine_utils.h" + +//Returns the Ruby Value after going through sympify +VALUE cutils_sympify(VALUE self, VALUE operand); + +#endif //RUBY_UTILS_H_ diff --git a/ext/symengine/symengine.c b/ext/symengine/symengine.c index 2f9f22c..02c35d5 100644 --- a/ext/symengine/symengine.c +++ b/ext/symengine/symengine.c @@ -2,10 +2,12 @@ #include "ruby_basic.h" #include "ruby_symbol.h" #include "ruby_integer.h" -#include "ruby_rational.h" #include "ruby_constant.h" +#include "ruby_complex.h" #include "ruby_function.h" #include "ruby_ntheory.h" +#include "ruby_utils.h" +#include "symengine_utils.h" #include "symengine.h" /////////////////// @@ -20,6 +22,7 @@ VALUE msymengine_ascii_art(VALUE self) { } void Init_symengine() { + m_symengine = rb_define_module("SymEngine"); rb_define_singleton_method(m_symengine, "ascii_art", msymengine_ascii_art, 0); @@ -44,6 +47,10 @@ void Init_symengine() { rb_define_method(c_basic, "subs", cbasic_subs, -1); rb_define_method(c_basic, "coerce", cbasic_coerce, 1); + //Sympify as a Module Level Function + rb_define_module_function(m_symengine, "convert", cutils_sympify, 1); + rb_define_global_function("SymEngine", cutils_sympify, 1); + //Symbol class c_symbol = rb_define_class_under(m_symengine, "Symbol", c_basic); rb_define_alloc_func(c_symbol, cbasic_alloc); @@ -58,7 +65,16 @@ void Init_symengine() { //Rational class c_rational = rb_define_class_under(m_symengine, "Rational", c_basic); rb_define_alloc_func(c_rational, cbasic_alloc); - rb_define_method(c_rational, "initialize", crational_init, 1); + + //Complex class + c_complex = rb_define_class_under(m_symengine, "Complex", c_basic); + rb_define_alloc_func(c_complex, cbasic_alloc); + rb_define_method(c_complex, "real", ccomplex_real_part, 0); + rb_define_method(c_complex, "imaginary", ccomplex_imaginary_part, 0); + + //ComplexDouble class + c_complex_double = rb_define_class_under(m_symengine, "ComplexDouble", c_basic); + rb_define_alloc_func(c_complex_double, cbasic_alloc); //Constant class c_constant = rb_define_class_under(m_symengine, "Constant", c_basic); @@ -67,6 +83,7 @@ void Init_symengine() { rb_define_const(m_symengine, "PI", cconstant_pi()); rb_define_const(m_symengine, "E", cconstant_e()); rb_define_const(m_symengine, "EULER_GAMMA", cconstant_euler_gamma()); + rb_define_const(m_symengine, "I", cconstant_i()); //Add class c_add = rb_define_class_under(m_symengine, "Add", c_basic); diff --git a/ext/symengine/symengine.h b/ext/symengine/symengine.h index 5893a2c..b50beb1 100644 --- a/ext/symengine/symengine.h +++ b/ext/symengine/symengine.h @@ -11,6 +11,8 @@ VALUE c_basic; VALUE c_symbol; VALUE c_integer; VALUE c_rational; +VALUE c_complex; +VALUE c_complex_double; VALUE c_constant; VALUE c_add; VALUE c_mul; diff --git a/ext/symengine/symengine_utils.c b/ext/symengine/symengine_utils.c index f914ac3..50a0ab7 100644 --- a/ext/symengine/symengine_utils.c +++ b/ext/symengine/symengine_utils.c @@ -2,8 +2,10 @@ #include "symengine.h" void sympify(VALUE operand2, basic_struct *cbasic_operand2) { + basic_struct *temp; VALUE new_operand2, num, den; + VALUE real, imag; switch(TYPE(operand2)) { case T_FIXNUM: @@ -28,10 +30,33 @@ void sympify(VALUE operand2, basic_struct *cbasic_operand2) { basic_free_stack(den_basic); break; + case T_COMPLEX: + real = rb_funcall(operand2, rb_intern("real"), 0, NULL); + imag = rb_funcall(operand2, rb_intern("imaginary"), 0, NULL); + + basic real_basic; + basic imag_basic; + + basic_new_stack(real_basic); + basic_new_stack(imag_basic); + + sympify(real, real_basic); + sympify(imag, imag_basic); + + basic_const_I(cbasic_operand2); + basic_mul(cbasic_operand2, cbasic_operand2, imag_basic); + basic_add(cbasic_operand2, cbasic_operand2, real_basic); + + basic_free_stack(real_basic); + basic_free_stack(imag_basic); + + break; + case T_DATA: Data_Get_Struct(operand2, basic_struct, temp); basic_assign(cbasic_operand2, temp); break; + } } @@ -43,6 +68,10 @@ VALUE Klass_of_Basic(const basic_struct *basic_ptr) { return c_integer; case SYMENGINE_RATIONAL: return c_rational; + case SYMENGINE_COMPLEX: + return c_complex; + case SYMENGINE_COMPLEX_DOUBLE: + return c_complex_double; case SYMENGINE_CONSTANT: return c_constant; case SYMENGINE_ADD: diff --git a/lib/symengine.rb b/lib/symengine.rb index 31e5f3e..06ebd31 100644 --- a/lib/symengine.rb +++ b/lib/symengine.rb @@ -35,3 +35,4 @@ def symbols ary_or_string, *params require 'symengine/iruby' require 'symengine/basic' require 'symengine/integer' +require 'symengine/complex' diff --git a/lib/symengine/complex.rb b/lib/symengine/complex.rb new file mode 100644 index 0000000..3903ebd --- /dev/null +++ b/lib/symengine/complex.rb @@ -0,0 +1,7 @@ +module SymEngine + class Complex + def to_c + to_s.tr('I', 'i').delete(' *').to_c + end + end +end diff --git a/spec/complex_spec.rb b/spec/complex_spec.rb new file mode 100644 index 0000000..ee29ddb --- /dev/null +++ b/spec/complex_spec.rb @@ -0,0 +1,51 @@ +describe SymEngine::Complex do + context 'Convert to SymEngine types' do + context 'with a Complex' do + subject { SymEngine(Complex(2, 3)) } + + it { is_expected.to be_a SymEngine::Complex } + its(:to_s) { is_expected.to eq '2 + 3*I' } + end + + context 'with an integer' do + subject { SymEngine(Complex(2, 0)) } + + it { is_expected.to be_a SymEngine::Integer } + its(:to_s) { is_expected.to eq '2' } + end + end + + context 'real_part and imaginary_part' do + let(:a) { SymEngine(Complex(Rational('2/7'), Rational('3/8'))) } + let(:b) { SymEngine(Complex(2, 3)) } + + context 'real_part' do + context 'using SymEngine Rationals' do + subject { a.real } + it { is_expected.to be_a SymEngine::Rational } + its(:to_s) { is_expected.to eq '2/7' } + end + + context 'using SymEngine Integers' do + subject { b.real } + it { is_expected.to be_a SymEngine::Integer } + its(:to_s) { is_expected.to eq '2' } + end + end + + context 'imaginary_part' do + context 'using SymEngine Rationals' do + subject { a.imaginary } + it { is_expected.to be_a SymEngine::Rational } + its(:to_s) { is_expected.to eq '3/8' } + end + + context 'using SymEngine Integers' do + subject { b.imaginary } + it { is_expected.to be_a SymEngine::Integer } + its(:to_s) { is_expected.to eq '3' } + end + end + end +end + diff --git a/spec/constant_spec.rb b/spec/constant_spec.rb index 85606e9..3bcfa70 100644 --- a/spec/constant_spec.rb +++ b/spec/constant_spec.rb @@ -42,4 +42,17 @@ its(:to_s) { is_expected.to eq 'x + E' } end end + + describe '#i' do + subject(:i) { SymEngine::I } + + it { is_expected.to be_a SymEngine::Complex } + + context 'when squared' do + subject { i * i} + it { is_expected.to be_a SymEngine::Basic } + its(:to_s) { is_expected.to eq '-1' } + end + end + end diff --git a/spec/rational_spec.rb b/spec/rational_spec.rb index 0f852c9..a9771d5 100644 --- a/spec/rational_spec.rb +++ b/spec/rational_spec.rb @@ -1,9 +1,25 @@ describe SymEngine::Rational do - context '#initialize' do - subject { SymEngine::Rational.new(Rational('2/3')) } + context 'Convert to SymEngine types' do + context 'with a Rational' do + subject { SymEngine(Rational('2/3')) } - it { is_expected.to be_a SymEngine::Rational } - its(:to_s) { is_expected.to eq '2/3' } + it { is_expected.to be_a SymEngine::Rational } + its(:to_s) { is_expected.to eq '2/3' } + end + + context 'with an integer' do + subject { SymEngine(Rational('2/1')) } + + it { is_expected.to be_a SymEngine::Integer } + its(:to_s) { is_expected.to eq '2' } + end + + context 'with one (x/x)' do + subject { SymEngine(Rational('44/44')) } + + it { is_expected.to be_a SymEngine::Integer } + its(:to_s) { is_expected.to eq '1' } + end end context 'coercion' do @@ -14,13 +30,13 @@ context 'commutative operations' do subject { x * r } it { is_expected.to be_a SymEngine::Basic } - it { is_expected.to eq(SymEngine::Rational.new(r) * x) } + it { is_expected.to eq(SymEngine(r) * x) } end context 'non commutative operations' do subject { x / r } it { is_expected.to be_a SymEngine::Basic } - it { is_expected.to eq(x / SymEngine::Rational.new(r)) } + it { is_expected.to eq(x / SymEngine(r)) } end end @@ -28,13 +44,13 @@ context 'commutative operations' do subject { r * x } it { is_expected.to be_a SymEngine::Basic } - it { is_expected.to eq(SymEngine::Rational.new(r) * x) } + it { is_expected.to eq(SymEngine(r) * x) } end context 'non commutative operations' do subject { r / x } it { is_expected.to be_a SymEngine::Basic } - it { is_expected.to eq(SymEngine::Rational.new(r) / x) } + it { is_expected.to eq(SymEngine(r) / x) } end end end diff --git a/spec/symengine_spec.rb b/spec/symengine_spec.rb index c3782d9..0f1017a 100644 --- a/spec/symengine_spec.rb +++ b/spec/symengine_spec.rb @@ -4,4 +4,19 @@ expect(SymEngine.ascii_art).to_not be_empty end end + + describe 'convert' do + subject { SymEngine::convert(Complex(2, 3)) } + + it { is_expected.to be_a SymEngine::Complex } + its(:to_s) { is_expected.to eq '2 + 3*I' } + end + + describe 'SymEngine()' do + subject { SymEngine(Rational('1/3')) } + + it { is_expected.to be_a SymEngine::Rational } + its(:to_s) { is_expected.to eq '1/3' } + end + end diff --git a/symengine_version.txt b/symengine_version.txt index 642392c..bc0e52b 100644 --- a/symengine_version.txt +++ b/symengine_version.txt @@ -1,2 +1,2 @@ -5e88e62b42c38200573eecf688d90b46beb38a77 +3f58acc1246661323b1daf2adc3f9fa6a8762df4