Skip to content

Commit 5dce32d

Browse files
committed
integer.c: Implement Integer#digits
1 parent 99001cf commit 5dce32d

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

numeric.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4475,6 +4475,111 @@ rb_int_bit_length(VALUE num)
44754475
return Qnil;
44764476
}
44774477

4478+
/*
4479+
* Document-method: Integer#digits(base=10)
4480+
* call-seq:
4481+
* int.digits -> [int]
4482+
* int.digits(base) -> [int]
4483+
*
4484+
* Returns the array including the digits extracted by place-value notation
4485+
* with radix +base+ of +int+.
4486+
*
4487+
* +base+ should be greater than or equal to 2.
4488+
*
4489+
* 12345.digits #=> [5, 4, 3, 2, 1]
4490+
* 12345.digits(7) #=> [4, 6, 6, 0, 5]
4491+
* -12345.digits(7) #=> [4, 6, 6, 0, 5]
4492+
* 12345.digits(100) #=> [45, 23, 1]
4493+
*
4494+
*/
4495+
4496+
static VALUE
4497+
rb_fix_digits(VALUE fix, long base)
4498+
{
4499+
VALUE digits;
4500+
long x = FIX2LONG(fix);
4501+
4502+
if (base < 2)
4503+
rb_raise(rb_eArgError, "invalid radix %ld", base);
4504+
4505+
if (x == 0) {
4506+
return rb_ary_new_from_args(1, INT2FIX(0));
4507+
}
4508+
else if (x < 0)
4509+
x = -x;
4510+
4511+
digits = rb_ary_new();
4512+
while (x > 0) {
4513+
long q = x % base;
4514+
rb_ary_push(digits, LONG2NUM(q));
4515+
x /= base;
4516+
}
4517+
4518+
return digits;
4519+
}
4520+
4521+
static VALUE
4522+
rb_int_digits_bigbase(VALUE num, VALUE base)
4523+
{
4524+
VALUE digits;
4525+
4526+
if (RB_TYPE_P(base, T_BIGNUM)) {
4527+
base = rb_big_norm(base);
4528+
}
4529+
4530+
if (FIXNUM_P(base) && FIX2LONG(base) < 2)
4531+
rb_raise(rb_eArgError, "invalid radix %ld", FIX2LONG(base));
4532+
else if (RB_TYPE_P(base, T_BIGNUM) && BIGNUM_NEGATIVE_P(base))
4533+
rb_raise(rb_eArgError, "negative radix");
4534+
4535+
if (FIXNUM_P(base) && FIXNUM_P(num))
4536+
return rb_fix_digits(num, FIX2LONG(base));
4537+
4538+
if (FIXNUM_P(num)) {
4539+
long x = FIX2LONG(num);
4540+
if (x < 0) x = -x;
4541+
return rb_ary_new_from_args(1, LONG2FIX(x));
4542+
}
4543+
4544+
digits = rb_ary_new();
4545+
while (!FIXNUM_P(num) || FIX2LONG(num) > 0) {
4546+
VALUE rq = int_divmod(num, base);
4547+
4548+
rb_ary_push(digits, RARRAY_AREF(rq, 1));
4549+
4550+
num = RARRAY_AREF(rq, 0);
4551+
}
4552+
4553+
return digits;
4554+
}
4555+
4556+
static VALUE
4557+
rb_int_digits(int argc, VALUE *argv, VALUE num)
4558+
{
4559+
long base;
4560+
4561+
if (rb_check_arity(argc, 0, 1)) {
4562+
if (!RB_INTEGER_TYPE_P(argv[0]))
4563+
rb_raise(rb_eTypeError, "wrong argument type %s (expected Integer)",
4564+
rb_obj_classname(argv[0]));
4565+
if (RB_TYPE_P(argv[0], T_BIGNUM))
4566+
return rb_int_digits_bigbase(num, argv[0]);
4567+
4568+
base = FIX2LONG(argv[0]);
4569+
if (base < 2)
4570+
rb_raise(rb_eArgError, "invalid radix %ld", base);
4571+
}
4572+
else
4573+
base = 10;
4574+
4575+
if (FIXNUM_P(num))
4576+
return rb_fix_digits(num, base);
4577+
else if (RB_TYPE_P(num, T_BIGNUM))
4578+
return rb_int_digits_bigbase(num, LONG2FIX(base));
4579+
4580+
return Qnil;
4581+
}
4582+
44784583
/*
44794584
* Document-method: Integer#upto
44804585
* call-seq:
@@ -4956,6 +5061,7 @@ Init_Numeric(void)
49565061

49575062
rb_define_method(rb_cInteger, "size", int_size, 0);
49585063
rb_define_method(rb_cInteger, "bit_length", rb_int_bit_length, 0);
5064+
rb_define_method(rb_cInteger, "digits", rb_int_digits, -1);
49595065

49605066
rb_cFixnum = rb_cInteger;
49615067
rb_define_const(rb_cObject, "Fixnum", rb_cInteger);

test/ruby/test_bignum.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,5 +737,20 @@ def obj.coerce(other)
737737
end
738738
assert_equal(T1024 ^ 10, T1024 ^ obj)
739739
end
740+
741+
def test_digits
742+
assert_equal([90, 78, 56, 34, 12], 1234567890.to_bignum.digits(100))
743+
assert_equal([7215, 2413, 6242], T1024P.digits(10_000).first(3))
744+
assert_equal([11], 11.digits(T1024P))
745+
assert_equal([11], -11.digits(T1024P))
746+
assert_equal([T1024P - 1, 1], (T1024P + T1024P - 1).digits(T1024P))
747+
assert_raise(ArgumentError) { T1024P.to_bignum.digits(0) }
748+
assert_raise(ArgumentError) { T1024P.to_bignum.digits(-1) }
749+
assert_raise(ArgumentError) { T1024P.to_bignum.digits(0.to_bignum) }
750+
assert_raise(ArgumentError) { T1024P.to_bignum.digits(1.to_bignum) }
751+
assert_raise(ArgumentError) { T1024P.to_bignum.digits(-T1024P) }
752+
assert_raise(ArgumentError) { 10.digits(0.to_bignum) }
753+
assert_raise(ArgumentError) { 10.digits(1.to_bignum) }
754+
end
740755
end
741756
end

test/ruby/test_integer.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,19 @@ def test_bit_length
331331
assert_equal(i+1, (n+1).bit_length, "#{n+1}.bit_length")
332332
}
333333
end
334+
335+
def test_digits
336+
assert_equal([0], 0.digits)
337+
assert_equal([1], 1.digits)
338+
assert_equal([1], -1.digits)
339+
assert_equal([0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1234567890.digits)
340+
assert_equal([0, 9, 8, 7, 6, 5, 4, 3, 2, 1], -1234567890.digits)
341+
assert_equal([90, 78, 56, 34, 12], 1234567890.digits(100))
342+
assert_equal([90, 78, 56, 34, 12], -1234567890.digits(100))
343+
assert_equal([10, 5, 6, 8, 0, 10, 8, 6, 1], 1234567890.digits(13))
344+
assert_equal([10, 5, 6, 8, 0, 10, 8, 6, 1], -1234567890.digits(13))
345+
assert_raise(ArgumentError) { 10.digits(0) }
346+
assert_raise(ArgumentError) { 10.digits(1) }
347+
assert_raise(TypeError) { 10.digits("a") }
348+
end
334349
end

0 commit comments

Comments
 (0)