diff --git a/numeric.c b/numeric.c index a7769ca255..17b6b92982 100644 --- a/numeric.c +++ b/numeric.c @@ -5241,6 +5241,44 @@ rb_int_s_isqrt(VALUE self, VALUE num) } } +static VALUE +rb_int_s_from_digits(int argc, VALUE *argv, VALUE klass) { + VALUE digits, base, array; + long size, levels, level; + rb_check_arity(argc, 1, 2); + digits = rb_to_array_type(argv[0]); + base = argc == 2 ? rb_to_int(argv[1]) : INT2FIX(10); + if (FIXNUM_P(base) && FIX2LONG(base) < 2) + rb_raise(rb_eArgError, "invalid radix %ld", FIX2LONG(base)); + else if (RB_TYPE_P(base, T_BIGNUM) && BIGNUM_NEGATIVE_P(base)) + rb_raise(rb_eArgError, "negative radix"); + array = rb_ary_dup(digits); + size = RARRAY_LEN(array); + levels = bit_length(size); + for (level = 0; level < levels; level++) { + long stride = 2 << level; + long i, n = (size + stride - 1) / stride; + if (level) base = rb_int_mul(base, base); + for (i = 0; i < n; i++) { + VALUE div = INT2FIX(0); + VALUE mod = rb_to_int(RARRAY_AREF(array, 2 * i)); + RARRAY_ASET(array, 2 * i, INT2FIX(0)); + if (2 * i + 1 < size) { + div = rb_to_int(RARRAY_AREF(array, 2 * i + 1)); + RARRAY_ASET(array, 2 * i + 1, INT2FIX(0)); + } + if (div == INT2FIX(0)) { + RARRAY_ASET(array, i, mod); + } else { + div = rb_to_int(div); + RARRAY_ASET(array, i, rb_int_plus(rb_int_mul(div, base), mod)); + } + } + } + return RARRAY_AREF(array, 0); +} + + /* * Document-class: ZeroDivisionError * @@ -5393,6 +5431,7 @@ Init_Numeric(void) rb_undef_alloc_func(rb_cInteger); rb_undef_method(CLASS_OF(rb_cInteger), "new"); rb_define_singleton_method(rb_cInteger, "sqrt", rb_int_s_isqrt, 1); + rb_define_singleton_method(rb_cInteger, "from_digits", rb_int_s_from_digits, -1); rb_define_method(rb_cInteger, "to_s", int_to_s, -1); rb_define_alias(rb_cInteger, "inspect", "to_s"); diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb index 1b485760e3..c8c84c28c2 100644 --- a/test/ruby/test_integer.rb +++ b/test/ruby/test_integer.rb @@ -477,6 +477,21 @@ def to_int assert_equal([0, 1], 10.digits(o)) end + def test_from_digits + assert_equal(Integer.from_digits([0]), 0) + assert_equal(Integer.from_digits([1]), 1) + assert_equal(Integer.from_digits([4, 3, 2, 1]), 1234) + assert_equal(Integer.from_digits([1, 0, 1, 1, 1], 2), 29) + assert_equal(Integer.from_digits((9**999).digits), 9**999) + assert_equal(Integer.from_digits([1, 1, 1], 1 << 128), (1 << 256) | (1 << 128) | 1) + assert_raise(TypeError) { Integer.from_digits("4321") } + assert_raise(TypeError) { Integer.from_digits([4, 3, 2, 1], "10") } + assert_raise(TypeError) { Integer.from_digits([4, "3", 2, 1]) } + assert_raise(ArgumentError) { Integer.from_digits([4, 3, 2, 1], 1) } + assert_raise(ArgumentError) { Integer.from_digits([4, 3, 2, 1], 0) } + assert_raise(ArgumentError) { Integer.from_digits([4, 3, 2, 1], -9**999) } + end + def test_square_root assert_raise(TypeError) {Integer.sqrt("x")} assert_raise(Math::DomainError) {Integer.sqrt(-1)}