mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
numeric.c: Add Integer#digits [Feature #12447] [ruby-core:75799]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
c071c05229
commit
9042cf217e
4 changed files with 197 additions and 0 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
Mon Jun 13 20:04:00 2016 Kenta Murata <mrkn@mrkn.jp>
|
||||
|
||||
* 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@ruby-lang.org>
|
||||
|
||||
* include/ruby/ruby.h (RUBY_INTEGER_UNIFICATION): macro to tell if
|
||||
|
|
107
numeric.c
107
numeric.c
|
@ -12,6 +12,7 @@
|
|||
#include "internal.h"
|
||||
#include "ruby/util.h"
|
||||
#include "id.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
@ -4477,6 +4478,111 @@ rb_int_bit_length(VALUE num)
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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:
|
||||
|
@ -4958,6 +5064,7 @@ Init_Numeric(void)
|
|||
|
||||
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;
|
||||
|
|
|
@ -29,6 +29,8 @@ class TestBignum < Test::Unit::TestCase
|
|||
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
|
|||
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
|
||||
|
|
|
@ -331,4 +331,43 @@ class TestInteger < Test::Unit::TestCase
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue