summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-08-23 22:07:39 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-08-23 22:07:39 +0000
commit2f6c0e3be38b057239eb044426c152ac5633c88f (patch)
tree694c7ce5c54b257050d1417974c970ec6cfd7651
parent89339af9c19c28eaa9e2814fb75aa09768971f0e (diff)
* array.c (rb_ary_shuffle_bang, rb_ary_sample): add optional
argument random. [ruby-dev:41923] [EXPERIMENTAL] * random.c (rb_random_{int32,real,bytes}): fallback to normal method invocation. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29083 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog8
-rw-r--r--array.c45
-rw-r--r--random.c35
-rw-r--r--test/ruby/test_array.rb17
-rw-r--r--test/ruby/test_rand.rb1
5 files changed, 88 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 04eb818ce7..ac55987353 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Tue Aug 24 07:07:28 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * array.c (rb_ary_shuffle_bang, rb_ary_sample): add optional
+ argument random. [ruby-dev:41923] [EXPERIMENTAL]
+
+ * random.c (rb_random_{int32,real,bytes}): fallback to normal
+ method invocation.
+
Tue Aug 24 06:08:10 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
* include/ruby/version.h (RUBY_API_VERSION_*): renamed and moved
diff --git a/array.c b/array.c
index d047565022..172a653aa2 100644
--- a/array.c
+++ b/array.c
@@ -3730,22 +3730,30 @@ rb_ary_flatten(int argc, VALUE *argv, VALUE ary)
return result;
}
-#define RAND_UPTO(max) (long)(rb_genrand_real()*(max))
+#define OPTHASH_GIVEN_P(opts) \
+ (argc > 0 && !NIL_P(opts = rb_check_hash_type(argv[argc-1])) && (--argc, 1))
+static VALUE sym_random;
+
+#define RAND_UPTO(max) (long)(rb_random_real(randgen)*(max))
/*
* call-seq:
- * ary.shuffle! -> ary
+ * ary.shuffle! -> ary
+ * ary.shuffle!(random: rng) -> ary
*
* Shuffles elements in +self+ in place.
+ * If +rng+ is given, it will be used as the random number generator.
*/
-
static VALUE
-rb_ary_shuffle_bang(VALUE ary)
+rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary)
{
- VALUE *ptr;
+ VALUE *ptr, opts, randgen = Qnil;
long i = RARRAY_LEN(ary);
+ if (OPTHASH_GIVEN_P(opts)) {
+ randgen = rb_hash_lookup2(opts, sym_random, randgen);
+ }
rb_ary_modify(ary);
ptr = RARRAY_PTR(ary);
while (i) {
@@ -3760,27 +3768,34 @@ rb_ary_shuffle_bang(VALUE ary)
/*
* call-seq:
- * ary.shuffle -> new_ary
+ * ary.shuffle -> new_ary
+ * ary.shuffle(random: rng) -> new_ary
*
* Returns a new array with elements of this array shuffled.
*
* a = [ 1, 2, 3 ] #=> [1, 2, 3]
* a.shuffle #=> [2, 3, 1]
+ *
+ * If +rng+ is given, it will be used as the random number generator.
+ *
+ * a.shuffle(Random.new(1)) #=> [1, 3, 2]
*/
static VALUE
-rb_ary_shuffle(VALUE ary)
+rb_ary_shuffle(int argc, VALUE *argv, VALUE ary)
{
ary = rb_ary_dup(ary);
- rb_ary_shuffle_bang(ary);
+ rb_ary_shuffle_bang(argc, argv, ary);
return ary;
}
/*
* call-seq:
- * ary.sample -> obj
- * ary.sample(n) -> new_ary
+ * ary.sample -> obj
+ * ary.sample(random: rng) -> obj
+ * ary.sample(n) -> new_ary
+ * ary.sample(n, random: rng) -> new_ary
*
* Choose a random element or +n+ random elements from the array. The elements
* are chosen by using random and unique indices into the array in order to
@@ -3788,6 +3803,7 @@ rb_ary_shuffle(VALUE ary)
* contained duplicate elements. If the array is empty the first form returns
* <code>nil</code> and the second form returns an empty array.
*
+ * If +rng+ is given, it will be used as the random number generator.
*/
@@ -3795,9 +3811,13 @@ static VALUE
rb_ary_sample(int argc, VALUE *argv, VALUE ary)
{
VALUE nv, result, *ptr;
+ VALUE opts, randgen = Qnil;
long n, len, i, j, k, idx[10];
len = RARRAY_LEN(ary);
+ if (OPTHASH_GIVEN_P(opts)) {
+ randgen = rb_hash_lookup2(opts, sym_random, randgen);
+ }
if (argc == 0) {
if (len == 0) return Qnil;
i = len == 1 ? 0 : RAND_UPTO(len);
@@ -4595,8 +4615,8 @@ Init_Array(void)
rb_define_method(rb_cArray, "flatten", rb_ary_flatten, -1);
rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, -1);
rb_define_method(rb_cArray, "count", rb_ary_count, -1);
- rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, 0);
- rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, 0);
+ rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, -1);
+ rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, -1);
rb_define_method(rb_cArray, "sample", rb_ary_sample, -1);
rb_define_method(rb_cArray, "cycle", rb_ary_cycle, -1);
rb_define_method(rb_cArray, "permutation", rb_ary_permutation, -1);
@@ -4611,4 +4631,5 @@ Init_Array(void)
rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
id_cmp = rb_intern("<=>");
+ sym_random = ID2SYM(rb_intern("random"));
}
diff --git a/random.c b/random.c
index ef0de1f750..a89fcb8ee9 100644
--- a/random.c
+++ b/random.c
@@ -321,6 +321,7 @@ int_pair_to_real_inclusive(unsigned int a, unsigned int b)
VALUE rb_cRandom;
#define id_minus '-'
#define id_plus '+'
+static ID id_rand, id_bytes;
/* :nodoc: */
static void
@@ -359,6 +360,13 @@ get_rnd(VALUE obj)
return ptr;
}
+static rb_random_t *
+try_get_rnd(VALUE obj)
+{
+ if (!rb_typeddata_is_kind_of(obj, &random_data_type)) return NULL;
+ return DATA_PTR(obj);
+}
+
/* :nodoc: */
static VALUE
random_alloc(VALUE klass)
@@ -869,14 +877,22 @@ rb_rand_internal(unsigned long i)
unsigned int
rb_random_int32(VALUE obj)
{
- rb_random_t *rnd = get_rnd(obj);
+ rb_random_t *rnd = try_get_rnd(obj);
+ if (!rnd) {
+ VALUE lim = ULONG2NUM(0xffffffff);
+ return NUM2ULONG(rb_funcall2(obj, id_rand, 1, &lim));
+ }
return genrand_int32(&rnd->mt);
}
double
rb_random_real(VALUE obj)
{
- rb_random_t *rnd = get_rnd(obj);
+ rb_random_t *rnd = try_get_rnd(obj);
+ if (!rnd) {
+ VALUE v = rb_funcall2(obj, id_rand, 0, 0);
+ return NUM2DBL(v);
+ }
return genrand_real(&rnd->mt);
}
@@ -895,11 +911,17 @@ random_bytes(VALUE obj, VALUE len)
VALUE
rb_random_bytes(VALUE obj, long n)
{
- rb_random_t *rnd = get_rnd(obj);
- VALUE bytes = rb_str_new(0, n);
- char *ptr = RSTRING_PTR(bytes);
+ rb_random_t *rnd = try_get_rnd(obj);
+ VALUE bytes;
+ char *ptr;
unsigned int r, i;
+ if (!rnd) {
+ VALUE len = LONG2NUM(n);
+ return rb_funcall2(obj, id_bytes, 1, &len);
+ }
+ bytes = rb_str_new(0, n);
+ ptr = RSTRING_PTR(bytes);
for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) {
r = genrand_int32(&rnd->mt);
i = SIZEOF_INT32;
@@ -1245,4 +1267,7 @@ Init_Random(void)
rb_define_singleton_method(rb_cRandom, "new_seed", random_seed, 0);
rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0);
rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0);
+
+ id_rand = rb_intern("rand");
+ id_bytes = rb_intern("bytes");
}
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index 3e32175012..f95d8870a1 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -1891,6 +1891,12 @@ class TestArray < Test::Unit::TestCase
100.times do
assert_equal([0, 1, 2], [2, 1, 0].shuffle.sort)
end
+
+ gen = Random.new(0)
+ srand(0)
+ 100.times do
+ assert_equal([0, 1, 2].shuffle, [0, 1, 2].shuffle(random: gen))
+ end
end
def test_sample
@@ -1907,7 +1913,7 @@ class TestArray < Test::Unit::TestCase
(0..20).each do |n|
100.times do
b = a.sample(n)
- assert_equal([n, 18].min, b.uniq.size)
+ assert_equal([n, 18].min, b.size)
assert_equal(a, (a | b).sort)
assert_equal(b.sort, (a & b).sort)
end
@@ -1920,6 +1926,15 @@ class TestArray < Test::Unit::TestCase
end
assert_raise(ArgumentError, '[ruby-core:23374]') {[1, 2].sample(-1)}
+
+ gen = Random.new(0)
+ srand(0)
+ a = (1..18).to_a
+ (0..20).each do |n|
+ 100.times do |i|
+ assert_equal(a.sample(n), a.sample(n, random: gen), "#{i}/#{n}")
+ end
+ end
end
def test_cycle
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index 40b291f959..c3282cf804 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -171,6 +171,7 @@ class TestRand < Test::Unit::TestCase
def test_shuffle
srand(0)
assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle)
+ assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle(random: Random.new(0)))
end
def test_big_seed