Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruby Wrappers for Real MPFR and Complex MPC #49

Merged
merged 15 commits into from
Jun 12, 2016
1 change: 1 addition & 0 deletions ext/symengine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(RUBY_WRAPPER_SRC
ruby_symbol.c
ruby_integer.c
ruby_real_double.c
ruby_real_mpfr.c
ruby_complex.c
ruby_complex_double.c
ruby_constant.c
Expand Down
48 changes: 48 additions & 0 deletions ext/symengine/ruby_real_mpfr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "ruby_real_mpfr.h"

VALUE crealmpfr_init(VALUE self, VALUE num_value, VALUE prec_value)
{
basic_struct *cresult;
double d;
char *c;
int prec = NUM2INT(prec_value);

Data_Get_Struct(self, basic_struct, cresult);

switch( TYPE(num_value) ) {
case T_FLOAT:
d = RFLOAT_VALUE(num_value);
real_mpfr_set_d(cresult, d, prec);
break;
case T_STRING:
c = RSTRING_PTR(num_value);
real_mpfr_set_str(cresult, c, prec);
break;
case T_DATA:
c = rb_obj_classname(num_value);
if(strcmp(c, "BigDecimal") == 0){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after if and before {

c = RSTRING_PTR( rb_funcall(num_value, rb_intern("to_s"), 1, rb_str_new2("F")) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove space after RSTRING_PTR( and before last )

real_mpfr_set_str(cresult, c, prec);
break;
}
default:
rb_raise(rb_eTypeError, "Invalid Type: Float, BigDecimal or String required.");
break;
}

return self;
}

VALUE crealmpfr_to_float(VALUE self)
{
VALUE result;
basic cbasic_operand1;
basic_new_stack(cbasic_operand1);
sympify(self, cbasic_operand1);

result = rb_float_new(real_mpfr_get_d(cbasic_operand1));

basic_free_stack(cbasic_operand1);
return result;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a terminate called after throwing an instance of 'Teuchos::NullReferenceError' error right after the return of this code. I've been trying to find out with no luck.

The real_mpfr_set_d function is working, it's being tested in here

Also, I used print statements to make sure that the code runs up until the return statement.

The error is given when I give a sample input like this:

A = SymEngine::RealMPFR.new(10.4, 10)

@abinashmeher999 @isuruf

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are trying to return a new ruby VALUE instead of self. See Symbol::initialize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @isuruf got it to be working, only to proceed into another confusion.

I'm getting the following output. Should I be worried?

2.1.8 :012 > X = SymEngine::RealMPFR.new(123.456, 200)
 => #<SymEngine::RealMPFR(123.45600000000000306954461848363280296325683593750000000000000)> 
2.1.8 :013 > Y = SymEngine::RealMPFR.new(456.123, 200)
 => #<SymEngine::RealMPFR(456.12299999999999045030563138425350189208984375000000000000000)> 

I am guessing this is to be expected with the arbitrary precision. But just wanted to confirm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's expected when using floats. Let me know when you have added String input and BigDecimal input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String and BigDecimal input added. @isuruf

}

13 changes: 13 additions & 0 deletions ext/symengine/ruby_real_mpfr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef RUBY_REAL_MPFR_H_
#define RUBY_REAL_MPFR_H_

#include <ruby.h>
#include <symengine/cwrapper.h>

#include "symengine.h"
#include "symengine_utils.h"

VALUE crealmpfr_init(VALUE self, VALUE num_value, VALUE prec_value);
VALUE crealmpfr_to_float(VALUE self);

#endif //RUBY_REAL_MPFR_H_
11 changes: 11 additions & 0 deletions ext/symengine/symengine.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "ruby_constant.h"
#include "ruby_complex.h"
#include "ruby_complex_double.h"
#include "ruby_real_mpfr.h"
#include "ruby_function.h"
#include "ruby_ntheory.h"
#include "ruby_utils.h"
Expand Down Expand Up @@ -85,6 +86,16 @@ void Init_symengine() {
rb_define_method(c_complex_double, "real", ccomplex_double_real_part, 0);
rb_define_method(c_complex_double, "imaginary", ccomplex_double_imaginary_part, 0);

//RealMPFR class
c_real_mpfr = rb_define_class_under(m_symengine, "RealMPFR", c_basic);
rb_define_alloc_func(c_real_mpfr, cbasic_alloc);
rb_define_method(c_real_mpfr, "initialize", crealmpfr_init, 2);
rb_define_method(c_real_mpfr, "to_f", crealmpfr_to_float, 0);

//ComplexMPC class
c_complex_mpc = rb_define_class_under(m_symengine, "ComplexMPC", c_basic);
rb_define_alloc_func(c_complex_mpc, cbasic_alloc);

//Constant class
c_constant = rb_define_class_under(m_symengine, "Constant", c_basic);

Expand Down
2 changes: 2 additions & 0 deletions ext/symengine/symengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ VALUE c_real_double;
VALUE c_rational;
VALUE c_complex;
VALUE c_complex_double;
VALUE c_real_mpfr;
VALUE c_complex_mpc;
VALUE c_constant;
VALUE c_add;
VALUE c_mul;
Expand Down
14 changes: 13 additions & 1 deletion ext/symengine/symengine_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ void sympify(VALUE operand2, basic_struct *cbasic_operand2) {
basic_struct *temp;
VALUE new_operand2, num, den;
VALUE real, imag;
VALUE a, b;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reuse a, b for num, den and real, imag?

double f;
char *c;

switch(TYPE(operand2)) {
case T_FIXNUM:
Expand Down Expand Up @@ -59,10 +61,16 @@ void sympify(VALUE operand2, basic_struct *cbasic_operand2) {
break;

case T_DATA:
c = rb_obj_classname(operand2);
if(strcmp(c, "BigDecimal") == 0){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this comment, #49 (comment)
It's much better than comparing strings.

c = RSTRING_PTR( rb_funcall(operand2, rb_intern("to_s"), 1, rb_str_new2("F")) );
real_mpfr_set_str(cbasic_operand2, c, 200);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SymEngine might not have been compiled with MPFR. So you cannot expect real_mpfr_set_str to be available all the time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried having #ifdef HAVE_SYMENGINE_MPFR , but it does not reach the code even though the symengine was built with MPFR and MPC. How can I find whether symengine has been build with MPFR and/or MPC.

Also, how can I translate that into ruby, because I would need to have a way to check that in the spec as well.

Can we have a function in the cwrapper, which returns true or false, depending on the flag? Then have a Ruby Wrapper for that, to access it from Ruby?

This is the solution I am currently thinking of. What do you think?

@isuruf @abinashmeher999

break;
}

Data_Get_Struct(operand2, basic_struct, temp);
basic_assign(cbasic_operand2, temp);
break;

}
}

Expand All @@ -87,12 +95,16 @@ VALUE Klass_of_Basic(const basic_struct *basic_ptr) {
return c_integer;
case SYMENGINE_REAL_DOUBLE:
return c_real_double;
case SYMENGINE_REAL_MPFR:
return c_real_mpfr;
case SYMENGINE_RATIONAL:
return c_rational;
case SYMENGINE_COMPLEX:
return c_complex;
case SYMENGINE_COMPLEX_DOUBLE:
return c_complex_double;
case SYMENGINE_COMPLEX_MPC:
return c_complex_mpc;
case SYMENGINE_CONSTANT:
return c_constant;
case SYMENGINE_ADD:
Expand Down
71 changes: 71 additions & 0 deletions spec/real_mpfr_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'bigdecimal'

describe SymEngine::RealMPFR do
describe 'Convert BigDemcimal to RealMPFR' do
shared_examples 'simple real check' do
subject { SymEngine(value) }

it { is_expected.to be_a SymEngine::RealMPFR }
its(:to_f) { is_expected.to eq value.to_f }
end

context 'positive BigDecimal' do
let(:value) { BigDecimal("12.3") }

it_behaves_like 'simple real check'
end

context 'negative BigDecimal' do
let(:value) { BigDecimal("-12.3") }

it_behaves_like 'simple real check'
end
end

describe 'Initialize RealMPFR' do
shared_examples 'simple real check' do
subject { SymEngine::RealMPFR.new(value, 200) }

it { is_expected.to be_a SymEngine::RealMPFR }
its(:to_f) { is_expected.to eq value.to_f }
end

context 'positive BigDecimal' do
let(:value) { BigDecimal("12.3") }

it_behaves_like 'simple real check'
end

context 'negative BigDecimal' do
let(:value) { BigDecimal("-12.3") }

it_behaves_like 'simple real check'
end

context 'positive Float' do
let(:value) { 12.3 }

it_behaves_like 'simple real check'
end

context 'negative Float' do
let(:value) { 12.3 }

it_behaves_like 'simple real check'
end

context 'String representation of positive value' do
let(:value) { "12.3" }

it_behaves_like 'simple real check'
end

context 'String representation of negative value' do
let(:value) { "-12.3" }

it_behaves_like 'simple real check'
end
end


end