ruby-changes:43321
From: mrkn <ko1@a...>
Date: Mon, 13 Jun 2016 20:43:59 +0900 (JST)
Subject: [ruby-changes:43321] mrkn:r55395 (trunk): numeric.c: Add Integer#digits [Feature #12447]
mrkn 2016-06-13 20:43:54 +0900 (Mon, 13 Jun 2016) New Revision: 55395 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=55395 Log: numeric.c: Add Integer#digits [Feature #12447] [ruby-core:75799] Modified files: trunk/ChangeLog trunk/numeric.c trunk/test/ruby/test_bignum.rb trunk/test/ruby/test_integer.rb Index: test/ruby/test_integer.rb =================================================================== --- test/ruby/test_integer.rb (revision 55394) +++ test/ruby/test_integer.rb (revision 55395) @@ -331,4 +331,43 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L331 assert_equal(i+1, (n+1).bit_length, "#{n+1}.bit_length") } end + + def test_digits + assert_equal([0], 0.digits) + assert_equal([1], 1.digits) + assert_equal([0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1234567890.digits) + assert_equal([90, 78, 56, 34, 12], 1234567890.digits(100)) + assert_equal([10, 5, 6, 8, 0, 10, 8, 6, 1], 1234567890.digits(13)) + end + + def test_digits_for_negative_numbers + assert_raise(Math::DomainError) { -1.digits } + assert_raise(Math::DomainError) { -1234567890.digits } + assert_raise(Math::DomainError) { -1234567890.digits(100) } + assert_raise(Math::DomainError) { -1234567890.digits(13) } + end + + def test_digits_for_invalid_base_numbers + assert_raise(ArgumentError) { 10.digits(-1) } + assert_raise(ArgumentError) { 10.digits(0) } + assert_raise(ArgumentError) { 10.digits(1) } + end + + def test_digits_for_non_integral_base_numbers + assert_equal([1], 1.digits(10r)) + assert_equal([1], 1.digits(10.0)) + assert_raise(RangeError) { 10.digits(10+1i) } + end + + def test_digits_for_non_numeric_base_argument + assert_raise(TypeError) { 10.digits("10") } + assert_raise(TypeError) { 10.digits("a") } + + class << (o = Object.new) + def to_int + 10 + end + end + assert_equal([0, 1], 10.digits(o)) + end end Index: test/ruby/test_bignum.rb =================================================================== --- test/ruby/test_bignum.rb (revision 55394) +++ test/ruby/test_bignum.rb (revision 55395) @@ -29,6 +29,8 @@ class TestBignum < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_bignum.rb#L29 T32P = (T32 - 1).to_bignum # 4294967295 T64 = (2**64).to_bignum # 18446744073709551616 T64P = (T64 - 1).to_bignum # 18446744073709551615 + T128 = (2**128).to_bignum + T128P = (T128 - 1).to_bignum T1024 = (2**1024).to_bignum T1024P = (T1024 - 1).to_bignum @@ -737,5 +739,44 @@ class TestBignum < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_bignum.rb#L739 end assert_equal(T1024 ^ 10, T1024 ^ obj) end + + def test_digits + assert_equal([90, 78, 56, 34, 12], 1234567890.to_bignum.digits(100)) + assert_equal([7215, 2413, 6242], T1024P.digits(10_000).first(3)) + assert_equal([11], 11.digits(T1024P)) + assert_equal([T1024P - 1, 1], (T1024P + T1024P - 1).digits(T1024P)) + end + + def test_digits_for_negative_numbers + assert_raise(Math::DomainError) { -11.digits(T1024P) } + assert_raise(Math::DomainError) { (-T1024P).digits } + assert_raise(Math::DomainError) { (-T1024P).digits(T1024P) } + end + + def test_digits_for_invalid_base_numbers + assert_raise(ArgumentError) { T1024P.to_bignum.digits(0) } + assert_raise(ArgumentError) { T1024P.to_bignum.digits(-1) } + assert_raise(ArgumentError) { T1024P.to_bignum.digits(0.to_bignum) } + assert_raise(ArgumentError) { T1024P.to_bignum.digits(1.to_bignum) } + assert_raise(ArgumentError) { T1024P.to_bignum.digits(-T1024P) } + assert_raise(ArgumentError) { 10.digits(0.to_bignum) } + assert_raise(ArgumentError) { 10.digits(1.to_bignum) } + end + + def test_digits_for_non_integral_base_numbers + assert_equal([11], 11.digits(T128P.to_r)) + assert_equal([11], 11.digits(T128P.to_f)) + + t1024p_digits_in_t32 = [T32P]*32 + assert_equal(t1024p_digits_in_t32, T1024P.digits(T32.to_r)) + assert_equal(t1024p_digits_in_t32, T1024P.digits(T32.to_f)) + + assert_raise(RangeError) { T128P.digits(10+1i) } + end + + def test_digits_for_non_numeric_base_argument + assert_raise(TypeError) { T1024P.digits("10") } + assert_raise(TypeError) { T1024P.digits("a") } + end end end Index: numeric.c =================================================================== --- numeric.c (revision 55394) +++ numeric.c (revision 55395) @@ -12,6 +12,7 @@ https://github.com/ruby/ruby/blob/trunk/numeric.c#L12 #include "internal.h" #include "ruby/util.h" #include "id.h" +#include <assert.h> #include <ctype.h> #include <math.h> #include <stdio.h> @@ -4478,6 +4479,111 @@ rb_int_bit_length(VALUE num) https://github.com/ruby/ruby/blob/trunk/numeric.c#L4479 } /* + * Document-method: Integer#digits(base=10) + * call-seq: + * int.digits -> [int] + * int.digits(base) -> [int] + * + * Returns the array including the digits extracted by place-value notation + * with radix +base+ of +int+. + * + * +base+ should be greater than or equal to 2. + * + * 12345.digits #=> [5, 4, 3, 2, 1] + * 12345.digits(7) #=> [4, 6, 6, 0, 5] + * -12345.digits(7) #=> [4, 6, 6, 0, 5] + * 12345.digits(100) #=> [45, 23, 1] + * + */ + +static VALUE +rb_fix_digits(VALUE fix, long base) +{ + VALUE digits; + long x = FIX2LONG(fix); + + assert(x >= 0); + + if (base < 2) + rb_raise(rb_eArgError, "invalid radix %ld", base); + + if (x == 0) + return rb_ary_new_from_args(1, INT2FIX(0)); + + digits = rb_ary_new(); + while (x > 0) { + long q = x % base; + rb_ary_push(digits, LONG2NUM(q)); + x /= base; + } + + return digits; +} + +static VALUE +rb_int_digits_bigbase(VALUE num, VALUE base) +{ + VALUE digits; + + assert(!rb_num_negative_p(num)); + + if (RB_TYPE_P(base, T_BIGNUM)) + base = rb_big_norm(base); + + 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"); + + if (FIXNUM_P(base) && FIXNUM_P(num)) + return rb_fix_digits(num, FIX2LONG(base)); + + if (FIXNUM_P(num)) + return rb_ary_new_from_args(1, num); + + digits = rb_ary_new(); + while (!FIXNUM_P(num) || FIX2LONG(num) > 0) { + VALUE qr = int_divmod(num, base); + rb_ary_push(digits, RARRAY_AREF(qr, 1)); + num = RARRAY_AREF(qr, 0); + } + + return digits; +} + +static VALUE +rb_int_digits(int argc, VALUE *argv, VALUE num) +{ + VALUE base_value; + long base; + + if (rb_num_negative_p(num)) + rb_raise(rb_eMathDomainError, "out of domain"); + + if (rb_check_arity(argc, 0, 1)) { + base_value = rb_to_int(argv[0]); + if (!RB_INTEGER_TYPE_P(base_value)) + rb_raise(rb_eTypeError, "wrong argument type %s (expected Integer)", + rb_obj_classname(argv[0])); + if (RB_TYPE_P(base_value, T_BIGNUM)) + return rb_int_digits_bigbase(num, base_value); + + base = FIX2LONG(base_value); + if (base < 2) + rb_raise(rb_eArgError, "invalid radix %ld", base); + } + else + base = 10; + + if (FIXNUM_P(num)) + return rb_fix_digits(num, base); + else if (RB_TYPE_P(num, T_BIGNUM)) + return rb_int_digits_bigbase(num, LONG2FIX(base)); + + return Qnil; +} + +/* * Document-method: Integer#upto * call-seq: * int.upto(limit) {|i| block } -> self @@ -4958,6 +5064,7 @@ Init_Numeric(void) https://github.com/ruby/ruby/blob/trunk/numeric.c#L5064 rb_define_method(rb_cInteger, "size", int_size, 0); rb_define_method(rb_cInteger, "bit_length", rb_int_bit_length, 0); + rb_define_method(rb_cInteger, "digits", rb_int_digits, -1); #ifndef RUBY_INTEGER_UNIFICATION rb_cFixnum = rb_cInteger; Index: ChangeLog =================================================================== --- ChangeLog (revision 55394) +++ ChangeLog (revision 55395) @@ -1,3 +1,13 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Mon Jun 13 20:04:00 2016 Kenta Murata <mrkn@m...> + + * numeric.c (rb_int_digits, rb_fix_digits, rb_int_digits_bigbase): + Add Integer#digits to extract columns in place-value notation + [Feature #12447] [ruby-core:75799] + + * test/ruby/test_integer.rb: Add tests for the above change. + + * test/ruby/test_bignum.rb: ditto. + Mon Jun 13 20:34:53 2016 Nobuyoshi Nakada <nobu@r...> * include/ruby/ruby.h (RUBY_INTEGER_UNIFICATION): macro to tell if -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/