From 019a2ddff4da41f8803229765eab705324ef67df Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 21 Jun 2022 16:53:17 +0900 Subject: [PATCH] mruby-rational/rational.c: implement Rational#**; close #5724 --- mrbgems/mruby-rational/src/rational.c | 56 +++++++++++++++++++++++++ mrbgems/mruby-rational/test/rational.rb | 15 +++++++ 2 files changed, 71 insertions(+) diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c index 5ba0a3129..6c9b238af 100644 --- a/mrbgems/mruby-rational/src/rational.c +++ b/mrbgems/mruby-rational/src/rational.c @@ -4,6 +4,7 @@ #include #ifndef MRB_NO_FLOAT +#include mrb_value mrb_complex_new(mrb_state *, mrb_float, mrb_float); #endif mrb_bool mrb_complex_eq(mrb_state *mrb, mrb_value, mrb_value); @@ -675,6 +676,60 @@ rational_div(mrb_state *mrb, mrb_value x) return mrb_rational_div(mrb, x, y); } +static mrb_value +rational_pow(mrb_state *mrb, mrb_value x) +{ + mrb_value y = mrb_get_arg1(mrb); + struct mrb_rational *p1 = rational_ptr(mrb, x); +#ifndef MRB_NO_FLOAT + double d1, d2; + + switch (mrb_type(y)) { + case MRB_TT_RATIONAL: + { + struct mrb_rational *p2 = rational_ptr(mrb, y); + if (p2->numerator == 0) { + return mrb_rational_new(mrb, 1, 1); + } + if (p2->numerator == p2->denominator) { + return x; + } + if (p2->denominator == 1) { + return rational_new_i(mrb, (mrb_int)pow((mrb_float)p1->numerator, (mrb_float)p2->numerator), + (mrb_int)pow((mrb_float)p1->denominator, (mrb_float)p2->numerator)); + } + d1 = rat_float(p1); + d2 = rat_float(p2); + } + break; + case MRB_TT_FLOAT: + { + d1 = rat_float(p1); + d2 = mrb_float(y); + } + break; + case MRB_TT_INTEGER: + { + mrb_int i = mrb_integer(y); + if (i == 0) { + return mrb_rational_new(mrb, 1, 1); + } + if (i == 1) { + return x; + } + return rational_new_i(mrb, (mrb_int)pow((mrb_float)p1->numerator, (mrb_float)i), + (mrb_int)pow((mrb_float)p1->denominator, (mrb_float)i)); + } + break; + default: + mrb_raisef(mrb, E_TYPE_ERROR, "%T cannot convert to Rational", y); + } + return mrb_float_value(mrb, pow(d1, d2)); +#else + mrb_raisef(mrb, E_NOTIMP_ERROR, "Rational#** not implemented with MRB_NO_FLOAT"); +#endif +} + void mrb_mruby_rational_gem_init(mrb_state *mrb) { struct RClass *rat; @@ -699,6 +754,7 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb) mrb_define_method(mrb, rat, "*", rational_mul, MRB_ARGS_REQ(1)); mrb_define_method(mrb, rat, "/", rational_div, MRB_ARGS_REQ(1)); mrb_define_method(mrb, rat, "quo", rational_div, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, rat, "**", rational_pow, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->integer_class, "to_r", fix_to_r, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->kernel_module, "Rational", rational_m, MRB_ARGS_ARG(1,1)); } diff --git a/mrbgems/mruby-rational/test/rational.rb b/mrbgems/mruby-rational/test/rational.rb index b807ef83f..0038f141b 100644 --- a/mrbgems/mruby-rational/test/rational.rb +++ b/mrbgems/mruby-rational/test/rational.rb @@ -331,3 +331,18 @@ assert 'Rational#frozen?' do assert_predicate(Rational(2,3), :frozen?) assert_predicate(4/5r, :frozen?) end + +assert 'Rational#**' do + assert_rational(1r, (14/2r)**0) + assert_rational(14/2r, (14/2r)**1) + assert_rational(49r, (14/2r)**2) + assert_rational(27r, (6/2r)**3) + assert_float(2.0, (4r)**(1/2r)) + assert_rational(4r, (4r)**(2/2r)) + assert_rational(16r, (4r)**(4/2r)) + assert_float(1.0, (4r)**(0.0)) + assert_float(2.0, (4r)**(0.5)) + assert_float(4.0, (4r)**(1.0)) + assert_float(16.0, (4r)**(2.0)) + assert_float(3.5**1.5, (7/2r)**(1.5)) +end