mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
range.c: Make Range#bsearch support endless ranges
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63195 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
db1bdecb0d
commit
d658a8d568
2 changed files with 74 additions and 46 deletions
115
range.c
115
range.c
|
@ -18,7 +18,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
VALUE rb_cRange;
|
VALUE rb_cRange;
|
||||||
static ID id_beg, id_end, id_excl, id_integer_p, id_div;
|
static ID id_beg, id_end, id_excl, id_integer_p, id_add, id_mul, id_div;
|
||||||
#define id_cmp idCmp
|
#define id_cmp idCmp
|
||||||
#define id_succ idSucc
|
#define id_succ idSucc
|
||||||
|
|
||||||
|
@ -526,6 +526,62 @@ is_integer_p(VALUE v)
|
||||||
return RTEST(is_int) && is_int != Qundef;
|
return RTEST(is_int) && is_int != Qundef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
bsearch_integer_range(VALUE beg, VALUE end, int excl)
|
||||||
|
{
|
||||||
|
VALUE satisfied = Qnil;
|
||||||
|
int smaller;
|
||||||
|
|
||||||
|
#define BSEARCH_CHECK(expr) \
|
||||||
|
do { \
|
||||||
|
VALUE val = (expr); \
|
||||||
|
VALUE v = rb_yield(val); \
|
||||||
|
if (FIXNUM_P(v)) { \
|
||||||
|
if (v == INT2FIX(0)) return val; \
|
||||||
|
smaller = (SIGNED_VALUE)v < 0; \
|
||||||
|
} \
|
||||||
|
else if (v == Qtrue) { \
|
||||||
|
satisfied = val; \
|
||||||
|
smaller = 1; \
|
||||||
|
} \
|
||||||
|
else if (v == Qfalse || v == Qnil) { \
|
||||||
|
smaller = 0; \
|
||||||
|
} \
|
||||||
|
else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \
|
||||||
|
int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)); \
|
||||||
|
if (!cmp) return val; \
|
||||||
|
smaller = cmp < 0; \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE \
|
||||||
|
" (must be numeric, true, false or nil)", \
|
||||||
|
rb_obj_class(v)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
VALUE low = rb_to_int(beg);
|
||||||
|
VALUE high = rb_to_int(end);
|
||||||
|
VALUE mid, org_high;
|
||||||
|
if (excl) high = rb_funcall(high, '-', 1, INT2FIX(1));
|
||||||
|
org_high = high;
|
||||||
|
|
||||||
|
while (rb_cmpint(rb_funcall(low, id_cmp, 1, high), low, high) < 0) {
|
||||||
|
mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2));
|
||||||
|
BSEARCH_CHECK(mid);
|
||||||
|
if (smaller) {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
low = rb_funcall(mid, '+', 1, INT2FIX(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rb_equal(low, org_high)) {
|
||||||
|
BSEARCH_CHECK(low);
|
||||||
|
if (!smaller) return Qnil;
|
||||||
|
}
|
||||||
|
return satisfied;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* rng.bsearch {|obj| block } -> value
|
* rng.bsearch {|obj| block } -> value
|
||||||
|
@ -598,33 +654,6 @@ range_bsearch(VALUE range)
|
||||||
* (-1...0.0).bsearch to yield -0.0.
|
* (-1...0.0).bsearch to yield -0.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BSEARCH_CHECK(expr) \
|
|
||||||
do { \
|
|
||||||
VALUE val = (expr); \
|
|
||||||
VALUE v = rb_yield(val); \
|
|
||||||
if (FIXNUM_P(v)) { \
|
|
||||||
if (v == INT2FIX(0)) return val; \
|
|
||||||
smaller = (SIGNED_VALUE)v < 0; \
|
|
||||||
} \
|
|
||||||
else if (v == Qtrue) { \
|
|
||||||
satisfied = val; \
|
|
||||||
smaller = 1; \
|
|
||||||
} \
|
|
||||||
else if (v == Qfalse || v == Qnil) { \
|
|
||||||
smaller = 0; \
|
|
||||||
} \
|
|
||||||
else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \
|
|
||||||
int cmp = rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)); \
|
|
||||||
if (!cmp) return val; \
|
|
||||||
smaller = cmp < 0; \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE \
|
|
||||||
" (must be numeric, true, false or nil)", \
|
|
||||||
rb_obj_class(v)); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define BSEARCH(conv) \
|
#define BSEARCH(conv) \
|
||||||
do { \
|
do { \
|
||||||
RETURN_ENUMERATOR(range, 0, 0); \
|
RETURN_ENUMERATOR(range, 0, 0); \
|
||||||
|
@ -661,35 +690,27 @@ range_bsearch(VALUE range)
|
||||||
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
|
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
|
||||||
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
|
else if (RB_TYPE_P(beg, T_FLOAT) || RB_TYPE_P(end, T_FLOAT)) {
|
||||||
int64_t low = double_as_int64(RFLOAT_VALUE(rb_Float(beg)));
|
int64_t low = double_as_int64(RFLOAT_VALUE(rb_Float(beg)));
|
||||||
int64_t high = double_as_int64(RFLOAT_VALUE(rb_Float(end)));
|
int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
|
||||||
int64_t mid, org_high;
|
int64_t mid, org_high;
|
||||||
BSEARCH(int64_as_double_to_num);
|
BSEARCH(int64_as_double_to_num);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (is_integer_p(beg) && is_integer_p(end)) {
|
else if (is_integer_p(beg) && is_integer_p(end)) {
|
||||||
VALUE low = rb_to_int(beg);
|
|
||||||
VALUE high = rb_to_int(end);
|
|
||||||
VALUE mid, org_high;
|
|
||||||
RETURN_ENUMERATOR(range, 0, 0);
|
RETURN_ENUMERATOR(range, 0, 0);
|
||||||
if (EXCL(range)) high = rb_funcall(high, '-', 1, INT2FIX(1));
|
return bsearch_integer_range(beg, end, EXCL(range));
|
||||||
org_high = high;
|
}
|
||||||
|
else if (is_integer_p(beg) && NIL_P(end)) {
|
||||||
while (rb_cmpint(rb_funcall(low, id_cmp, 1, high), low, high) < 0) {
|
VALUE diff = LONG2FIX(1);
|
||||||
mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2));
|
RETURN_ENUMERATOR(range, 0, 0);
|
||||||
|
while (1) {
|
||||||
|
VALUE mid = rb_funcall(beg, id_add, 1, diff);
|
||||||
BSEARCH_CHECK(mid);
|
BSEARCH_CHECK(mid);
|
||||||
if (smaller) {
|
if (smaller) {
|
||||||
high = mid;
|
return bsearch_integer_range(beg, mid, 0);
|
||||||
}
|
}
|
||||||
else {
|
diff = rb_funcall(diff, id_mul, 1, LONG2FIX(2));
|
||||||
low = rb_funcall(mid, '+', 1, INT2FIX(1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rb_equal(low, org_high)) {
|
|
||||||
BSEARCH_CHECK(low);
|
|
||||||
if (!smaller) return Qnil;
|
|
||||||
}
|
|
||||||
return satisfied;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
|
rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
|
||||||
}
|
}
|
||||||
|
@ -1363,6 +1384,8 @@ Init_Range(void)
|
||||||
id_end = rb_intern("end");
|
id_end = rb_intern("end");
|
||||||
id_excl = rb_intern("excl");
|
id_excl = rb_intern("excl");
|
||||||
id_integer_p = rb_intern("integer?");
|
id_integer_p = rb_intern("integer?");
|
||||||
|
id_add = rb_intern("+");
|
||||||
|
id_mul = rb_intern("*");
|
||||||
id_div = rb_intern("div");
|
id_div = rb_intern("div");
|
||||||
|
|
||||||
rb_cRange = rb_struct_define_without_accessor(
|
rb_cRange = rb_struct_define_without_accessor(
|
||||||
|
|
|
@ -581,6 +581,8 @@ class TestRange < Test::Unit::TestCase
|
||||||
|
|
||||||
ary = [0, 100, 100, 100, 200]
|
ary = [0, 100, 100, 100, 200]
|
||||||
assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
|
assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
|
||||||
|
|
||||||
|
assert_equal(1_000_001, (0...).bsearch {|i| i > 1_000_000 })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bsearch_for_float
|
def test_bsearch_for_float
|
||||||
|
@ -632,6 +634,8 @@ class TestRange < Test::Unit::TestCase
|
||||||
|
|
||||||
assert_in_delta(1.0, (0.0..inf).bsearch {|x| Math.log(x) >= 0 })
|
assert_in_delta(1.0, (0.0..inf).bsearch {|x| Math.log(x) >= 0 })
|
||||||
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
|
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
|
||||||
|
|
||||||
|
assert_equal(1_000_000.0.next_float, (0.0..).bsearch {|x| x > 1_000_000 })
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_bsearch_values(range, search, a)
|
def check_bsearch_values(range, search, a)
|
||||||
|
@ -733,6 +737,7 @@ class TestRange < Test::Unit::TestCase
|
||||||
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
|
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
|
||||||
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
|
assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
|
||||||
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
|
assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
|
||||||
|
assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
|
||||||
|
|
||||||
assert_raise(TypeError) { ("a".."z").bsearch {} }
|
assert_raise(TypeError) { ("a".."z").bsearch {} }
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue