[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]