diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c index 8f4289df5..5fa4e5fb1 100644 --- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c +++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -113,6 +114,86 @@ int_powm(mrb_state *mrb, mrb_value x) return mrb_int_value(mrb, result); } +/* + * call-seq: + * digits(base = 10) -> array_of_integers + * + * Returns an array of integers representing the +base+-radix + * digits of +self+; + * the first element of the array represents the least significant digit: + * + * 12345.digits # => [5, 4, 3, 2, 1] + * 12345.digits(7) # => [4, 6, 6, 0, 5] + * 12345.digits(100) # => [45, 23, 1] + * + * Raises an exception if +self+ is negative or +base+ is less than 2. + * + */ + +static mrb_value +int_digits(mrb_state *mrb, mrb_value self) +{ + mrb_int base = 10; + + mrb_get_args(mrb, "|i", &base); + if (base < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "negative radix"); + } + else if (base < 2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base); + } +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(self)) { + mrb_value x = self; + mrb_value zero = mrb_fixnum_value(0); + mrb_value bv = mrb_int_value(mrb, base); + if (mrb_bint_cmp(mrb, x, zero) < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive"); + } + mrb_value digits = mrb_ary_new(mrb); + + while (mrb_bint_cmp(mrb, x, zero) == 0) { + mrb_ary_push(mrb, digits, zero); + return digits; + } + + while (mrb_bint_cmp(mrb, x, zero) > 0) { + mrb_value q = mrb_bint_mod(mrb, x, bv); + mrb_ary_push(mrb, digits, q); + x = mrb_bint_div(mrb, x, bv); + if (!mrb_bigint_p(x)) { + mrb_int n = mrb_integer(x); + while (n > 0) { + mrb_int q = n % base; + mrb_ary_push(mrb, digits, mrb_int_value(mrb, q)); + n /= base; + } + break; + } + } + return digits; + } +#endif + mrb_int n = mrb_integer(self); + if (n < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "number should be positive"); + } + + mrb_value digits = mrb_ary_new(mrb); + + if (n == 0) { + mrb_ary_push(mrb, digits, mrb_fixnum_value(0)); + return digits; + } + + while (n > 0) { + mrb_int q = n % base; + mrb_ary_push(mrb, digits, mrb_int_value(mrb, q)); + n /= base; + } + return digits; +} + #ifndef MRB_NO_FLOAT static mrb_value flo_remainder(mrb_state *mrb, mrb_value self) @@ -136,6 +217,7 @@ mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, i, "remainder", int_remainder, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, i, MRB_SYM(pow), int_powm, MRB_ARGS_ARG(1,1)); + mrb_define_method_id(mrb, i, MRB_SYM(digits), int_digits, MRB_ARGS_OPT(1)); #ifndef MRB_NO_FLOAT struct RClass *f = mrb->float_class;