summaryrefslogtreecommitdiff
path: root/range.c
diff options
context:
space:
mode:
Diffstat (limited to 'range.c')
-rw-r--r--range.c472
1 files changed, 377 insertions, 95 deletions
diff --git a/range.c b/range.c
index 6d90ecf4f3..a6bf0fca51 100644
--- a/range.c
+++ b/range.c
@@ -78,7 +78,7 @@ range_modify(VALUE range)
rb_check_frozen(range);
/* Ranges are immutable, so that they should be initialized only once. */
if (RANGE_EXCL(range) != Qnil) {
- rb_name_err_raise("`initialize' called twice", range, ID2SYM(idInitialize));
+ rb_name_err_raise("'initialize' called twice", range, ID2SYM(idInitialize));
}
}
@@ -224,8 +224,8 @@ recursive_eql(VALUE range, VALUE obj, int recur)
* Returns +true+ if and only if:
*
* - +other+ is a range.
- * - <tt>other.begin eql? self.begin</tt>.
- * - <tt>other.end eql? self.end</tt>.
+ * - <tt>other.begin.eql?(self.begin)</tt>.
+ * - <tt>other.end.eql?(self.end)</tt>.
* - <tt>other.exclude_end? == self.exclude_end?</tt>.
*
* Otherwise returns +false+.
@@ -532,7 +532,11 @@ range_step(int argc, VALUE *argv, VALUE range)
rb_raise(rb_eTypeError, "can't iterate from %s",
rb_obj_classname(b));
}
- range_each_func(range, step_i, (VALUE)iter);
+ if (!NIL_P(e))
+ range_each_func(range, step_i, (VALUE)iter);
+ else
+ for (;; b = rb_funcallv(b, id_succ, 0, 0))
+ step_i(b, (VALUE)iter);
}
}
return range;
@@ -603,6 +607,10 @@ double_as_int64(double d)
static int
is_integer_p(VALUE v)
{
+ if (rb_integer_type_p(v)) {
+ return true;
+ }
+
ID id_integer_p;
VALUE is_int;
CONST_ID(id_integer_p, "integer?");
@@ -645,27 +653,30 @@ bsearch_integer_range(VALUE beg, VALUE end, int excl)
VALUE low = rb_to_int(beg);
VALUE high = rb_to_int(end);
- VALUE mid, org_high;
+ VALUE mid;
ID id_div;
CONST_ID(id_div, "div");
- if (excl) high = rb_funcall(high, '-', 1, INT2FIX(1));
- org_high = high;
+ if (!excl) high = rb_funcall(high, '+', 1, INT2FIX(1));
+ low = rb_funcall(low, '-', 1, INT2FIX(1));
- 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));
+ /*
+ * This loop must continue while low + 1 < high.
+ * Instead of checking low + 1 < high, check low < mid, where mid = (low + high) / 2.
+ * This is to avoid the cost of calculating low + 1 on each iteration.
+ * Note that this condition replacement is valid because Integer#div always rounds
+ * towards negative infinity.
+ */
+ while (mid = rb_funcall(rb_funcall(high, '+', 1, low), id_div, 1, INT2FIX(2)),
+ rb_cmpint(rb_funcall(low, id_cmp, 1, mid), low, mid) < 0) {
BSEARCH_CHECK(mid);
if (smaller) {
high = mid;
}
else {
- low = rb_funcall(mid, '+', 1, INT2FIX(1));
+ low = mid;
}
}
- if (rb_equal(low, org_high)) {
- BSEARCH_CHECK(low);
- if (!smaller) return Qnil;
- }
return satisfied;
}
@@ -692,52 +703,58 @@ range_bsearch(VALUE range)
* by the mantissa. This is true with or without implicit bit.
*
* Finding the average of two ints needs to be careful about
- * potential overflow (since float to long can use 64 bits)
- * as well as the fact that -1/2 can be 0 or -1 in C89.
+ * potential overflow (since float to long can use 64 bits).
+ *
+ * The half-open interval (low, high] indicates where the target is located.
+ * The loop continues until low and high are adjacent.
+ *
+ * -1/2 can be either 0 or -1 in C89. However, when low and high are not adjacent,
+ * the rounding direction of mid = (low + high) / 2 does not affect the result of
+ * the binary search.
*
* Note that -0.0 is mapped to the same int as 0.0 as we don't want
* (-1...0.0).bsearch to yield -0.0.
*/
-#define BSEARCH(conv) \
+#define BSEARCH(conv, excl) \
do { \
RETURN_ENUMERATOR(range, 0, 0); \
- if (EXCL(range)) high--; \
- org_high = high; \
- while (low < high) { \
+ if (!(excl)) high++; \
+ low--; \
+ while (low + 1 < high) { \
mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \
- : (low < -high) ? -((-1 - low - high)/2 + 1) : (low + high) / 2; \
+ : (low + high) / 2; \
BSEARCH_CHECK(conv(mid)); \
if (smaller) { \
high = mid; \
} \
else { \
- low = mid + 1; \
+ low = mid; \
} \
} \
- if (low == org_high) { \
- BSEARCH_CHECK(conv(low)); \
- if (!smaller) return Qnil; \
- } \
return satisfied; \
} while (0)
+#define BSEARCH_FIXNUM(beg, end, excl) \
+ do { \
+ long low = FIX2LONG(beg); \
+ long high = FIX2LONG(end); \
+ long mid; \
+ BSEARCH(INT2FIX, (excl)); \
+ } while (0)
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
- long low = FIX2LONG(beg);
- long high = FIX2LONG(end);
- long mid, org_high;
- BSEARCH(INT2FIX);
+ BSEARCH_FIXNUM(beg, end, EXCL(range));
}
#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)
else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) {
int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));
int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));
- int64_t mid, org_high;
- BSEARCH(int64_as_double_to_num);
+ int64_t mid;
+ BSEARCH(int64_as_double_to_num, EXCL(range));
}
#endif
else if (is_integer_p(beg) && is_integer_p(end)) {
@@ -751,9 +768,15 @@ range_bsearch(VALUE range)
VALUE mid = rb_funcall(beg, '+', 1, diff);
BSEARCH_CHECK(mid);
if (smaller) {
- return bsearch_integer_range(beg, mid, 0);
+ if (FIXNUM_P(beg) && FIXNUM_P(mid)) {
+ BSEARCH_FIXNUM(beg, mid, false);
+ }
+ else {
+ return bsearch_integer_range(beg, mid, false);
+ }
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ beg = mid;
}
}
else if (NIL_P(beg) && is_integer_p(end)) {
@@ -763,9 +786,15 @@ range_bsearch(VALUE range)
VALUE mid = rb_funcall(end, '+', 1, diff);
BSEARCH_CHECK(mid);
if (!smaller) {
- return bsearch_integer_range(mid, end, 0);
+ if (FIXNUM_P(mid) && FIXNUM_P(end)) {
+ BSEARCH_FIXNUM(mid, end, false);
+ }
+ else {
+ return bsearch_integer_range(mid, end, false);
+ }
}
diff = rb_funcall(diff, '*', 1, LONG2FIX(2));
+ end = mid;
}
}
else {
@@ -798,7 +827,12 @@ sym_each_i(VALUE v, VALUE arg)
* (1..4).size # => 4
* (1...4).size # => 3
* (1..).size # => Infinity
- * ('a'..'z').size #=> nil
+ * ('a'..'z').size # => nil
+ *
+ * If +self+ is not iterable, raises an exception:
+ *
+ * (0.5..2.5).size # TypeError
+ * (..1).size # TypeError
*
* Related: Range#count.
*/
@@ -807,7 +841,8 @@ static VALUE
range_size(VALUE range)
{
VALUE b = RANGE_BEG(range), e = RANGE_END(range);
- if (rb_obj_is_kind_of(b, rb_cNumeric)) {
+
+ if (RB_INTEGER_TYPE_P(b)) {
if (rb_obj_is_kind_of(e, rb_cNumeric)) {
return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));
}
@@ -815,10 +850,10 @@ range_size(VALUE range)
return DBL2NUM(HUGE_VAL);
}
}
- else if (NIL_P(b)) {
- if (rb_obj_is_kind_of(e, rb_cNumeric)) {
- return DBL2NUM(HUGE_VAL);
- }
+
+ if (!discrete_object_p(b)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(b));
}
return Qnil;
@@ -835,7 +870,6 @@ range_size(VALUE range)
* (1...4).to_a # => [1, 2, 3]
* ('a'..'d').to_a # => ["a", "b", "c", "d"]
*
- * Range#entries is an alias for Range#to_a.
*/
static VALUE
@@ -996,6 +1030,144 @@ range_each(VALUE range)
return range;
}
+RBIMPL_ATTR_NORETURN()
+static void
+range_reverse_each_bignum_beginless(VALUE end)
+{
+ RUBY_ASSERT(RBIGNUM_NEGATIVE_P(end));
+
+ for (;; end = rb_big_minus(end, INT2FIX(1))) {
+ rb_yield(end);
+ }
+ UNREACHABLE;
+}
+
+static void
+range_reverse_each_bignum(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(RBIGNUM_POSITIVE_P(beg) == RBIGNUM_POSITIVE_P(end));
+
+ VALUE c;
+ while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {
+ rb_yield(end);
+ if (c == INT2FIX(0)) break;
+ end = rb_big_minus(end, INT2FIX(1));
+ }
+}
+
+static void
+range_reverse_each_positive_bignum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (FIXNUM_P(end) || RBIGNUM_NEGATIVE_P(end)) return;
+
+ if (NIL_P(beg) || FIXNUM_P(beg) || RBIGNUM_NEGATIVE_P(beg)) {
+ beg = LONG2NUM(FIXNUM_MAX + 1);
+ }
+
+ range_reverse_each_bignum(beg, end);
+}
+
+static void
+range_reverse_each_fixnum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (!FIXNUM_P(beg)) {
+ if (!NIL_P(beg) && RBIGNUM_POSITIVE_P(beg)) return;
+
+ beg = LONG2FIX(FIXNUM_MIN);
+ }
+
+ if (!FIXNUM_P(end)) {
+ if (RBIGNUM_NEGATIVE_P(end)) return;
+
+ end = LONG2FIX(FIXNUM_MAX);
+ }
+
+ long b = FIX2LONG(beg);
+ long e = FIX2LONG(end);
+ for (long i = e; i >= b; --i) {
+ rb_yield(LONG2FIX(i));
+ }
+}
+
+static void
+range_reverse_each_negative_bignum_section(VALUE beg, VALUE end)
+{
+ RUBY_ASSERT(!NIL_P(end));
+
+ if (FIXNUM_P(end) || RBIGNUM_POSITIVE_P(end)) {
+ end = LONG2NUM(FIXNUM_MIN - 1);
+ }
+
+ if (NIL_P(beg)) {
+ range_reverse_each_bignum_beginless(end);
+ }
+
+ if (FIXNUM_P(beg) || RBIGNUM_POSITIVE_P(beg)) return;
+
+ range_reverse_each_bignum(beg, end);
+}
+
+/*
+ * call-seq:
+ * reverse_each {|element| ... } -> self
+ * reverse_each -> an_enumerator
+ *
+ * With a block given, passes each element of +self+ to the block in reverse order:
+ *
+ * a = []
+ * (1..4).reverse_each {|element| a.push(element) } # => 1..4
+ * a # => [4, 3, 2, 1]
+ *
+ * a = []
+ * (1...4).reverse_each {|element| a.push(element) } # => 1...4
+ * a # => [3, 2, 1]
+ *
+ * With no block given, returns an enumerator.
+ *
+ */
+
+static VALUE
+range_reverse_each(VALUE range)
+{
+ RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);
+
+ VALUE beg = RANGE_BEG(range);
+ VALUE end = RANGE_END(range);
+ int excl = EXCL(range);
+
+ if (NIL_P(end)) {
+ rb_raise(rb_eTypeError, "can't iterate from %s",
+ rb_obj_classname(end));
+ }
+
+ if (FIXNUM_P(beg) && FIXNUM_P(end)) {
+ if (excl) {
+ if (end == LONG2FIX(FIXNUM_MIN)) return range;
+
+ end = rb_int_minus(end, INT2FIX(1));
+ }
+
+ range_reverse_each_fixnum_section(beg, end);
+ }
+ else if ((NIL_P(beg) || RB_INTEGER_TYPE_P(beg)) && RB_INTEGER_TYPE_P(end)) {
+ if (excl) {
+ end = rb_int_minus(end, INT2FIX(1));
+ }
+ range_reverse_each_positive_bignum_section(beg, end);
+ range_reverse_each_fixnum_section(beg, end);
+ range_reverse_each_negative_bignum_section(beg, end);
+ }
+ else {
+ return rb_call_super(0, NULL);
+ }
+
+ return range;
+}
+
/*
* call-seq:
* self.begin -> object
@@ -1100,11 +1272,11 @@ rb_int_range_last(int argc, VALUE *argv, VALUE range)
int x;
long n;
- assert(argc > 0);
+ RUBY_ASSERT(argc > 0);
b = RANGE_BEG(range);
e = RANGE_END(range);
- assert(RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e));
+ RUBY_ASSERT(RB_INTEGER_TYPE_P(b) && RB_INTEGER_TYPE_P(e));
x = EXCL(range);
@@ -1678,7 +1850,6 @@ range_inspect(VALUE range)
}
static VALUE range_include_internal(VALUE range, VALUE val);
-static VALUE range_string_cover_internal(VALUE range, VALUE val);
VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
/*
@@ -1723,8 +1894,6 @@ VALUE rb_str_include_range_p(VALUE beg, VALUE end, VALUE val, VALUE exclusive);
static VALUE
range_eqq(VALUE range, VALUE val)
{
- VALUE ret = range_string_cover_internal(range, val);
- if (!UNDEF_P(ret)) return ret;
return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);
}
@@ -1756,8 +1925,6 @@ range_eqq(VALUE range, VALUE val)
* ('a'..'d').cover?('cc') # => true
*
* Related: Range#cover?.
- *
- * Range#member? is an alias for Range#include?.
*/
static VALUE
@@ -1768,36 +1935,24 @@ range_include(VALUE range, VALUE val)
return rb_call_super(1, &val);
}
-static VALUE
-range_string_cover_internal(VALUE range, VALUE val)
+static inline bool
+range_integer_edge_p(VALUE beg, VALUE end)
{
- VALUE beg = RANGE_BEG(range);
- VALUE end = RANGE_END(range);
- int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
- linear_object_p(beg) || linear_object_p(end);
+ return (!NIL_P(rb_check_to_integer(beg, "to_int")) ||
+ !NIL_P(rb_check_to_integer(end, "to_int")));
+}
- if (nv ||
- !NIL_P(rb_check_to_integer(beg, "to_int")) ||
- !NIL_P(rb_check_to_integer(end, "to_int"))) {
- return r_cover_p(range, beg, end, val);
- }
- else if (RB_TYPE_P(beg, T_STRING) || RB_TYPE_P(end, T_STRING)) {
- if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) {
- return r_cover_p(range, beg, end, val);
- }
- if (NIL_P(beg)) {
- VALUE r = rb_funcall(val, id_cmp, 1, end);
- if (NIL_P(r)) return Qfalse;
- if (RANGE_EXCL(range)) {
- return RBOOL(rb_cmpint(r, val, end) < 0);
- }
- return RBOOL(rb_cmpint(r, val, end) <= 0);
- }
- else if (NIL_P(end)) {
- VALUE r = rb_funcall(beg, id_cmp, 1, val);
- if (NIL_P(r)) return Qfalse;
- return RBOOL(rb_cmpint(r, beg, val) <= 0);
- }
+static inline bool
+range_string_range_p(VALUE beg, VALUE end)
+{
+ return RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING);
+}
+
+static inline VALUE
+range_include_fallback(VALUE beg, VALUE end, VALUE val)
+{
+ if (NIL_P(beg) && NIL_P(end)) {
+ if (linear_object_p(val)) return Qtrue;
}
if (NIL_P(beg) || NIL_P(end)) {
@@ -1815,20 +1970,14 @@ range_include_internal(VALUE range, VALUE val)
int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
linear_object_p(beg) || linear_object_p(end);
- if (nv ||
- !NIL_P(rb_check_to_integer(beg, "to_int")) ||
- !NIL_P(rb_check_to_integer(end, "to_int"))) {
+ if (nv || range_integer_edge_p(beg, end)) {
return r_cover_p(range, beg, end, val);
}
- else if (RB_TYPE_P(beg, T_STRING) && RB_TYPE_P(end, T_STRING)) {
+ else if (range_string_range_p(beg, end)) {
return rb_str_include_range_p(beg, end, val, RANGE_EXCL(range));
}
- if (NIL_P(beg) || NIL_P(end)) {
- rb_raise(rb_eTypeError, "cannot determine inclusion in beginless/endless ranges");
- }
-
- return Qundef;
+ return range_include_fallback(beg, end, val);
}
static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
@@ -1855,7 +2004,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0) # => false
* r.cover?(5) # => false
* r.cover?('foo') # => false
-
+ *
* r = ('a'..'d')
* r.cover?('a') # => true
* r.cover?('d') # => true
@@ -1876,7 +2025,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0) # => false
* r.cover?(4) # => false
* r.cover?('foo') # => false
-
+ *
* r = ('a'...'d')
* r.cover?('a') # => true
* r.cover?('c') # => true
@@ -1892,7 +2041,7 @@ static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
* r.cover?(0..4) # => false
* r.cover?(1..5) # => false
* r.cover?('a'..'d') # => false
-
+ *
* r = (1...4)
* r.cover?(1..3) # => true
* r.cover?(1..4) # => false
@@ -2114,17 +2263,137 @@ range_count(int argc, VALUE *argv, VALUE range)
* Infinity. Just let it loop. */
return rb_call_super(argc, argv);
}
- else if (NIL_P(RANGE_END(range))) {
+
+ VALUE beg = RANGE_BEG(range), end = RANGE_END(range);
+
+ if (NIL_P(beg) || NIL_P(end)) {
/* We are confident that the answer is Infinity. */
return DBL2NUM(HUGE_VAL);
}
- else if (NIL_P(RANGE_BEG(range))) {
- /* We are confident that the answer is Infinity. */
- return DBL2NUM(HUGE_VAL);
+
+ if (is_integer_p(beg)) {
+ VALUE size = range_size(range);
+ if (!NIL_P(size)) {
+ return size;
+ }
}
- else {
- return rb_call_super(argc, argv);
+
+ return rb_call_super(argc, argv);
+}
+
+static bool
+empty_region_p(VALUE beg, VALUE end, int excl)
+{
+ if (NIL_P(beg)) return false;
+ if (NIL_P(end)) return false;
+ int less = r_less(beg, end);
+ /* empty range */
+ if (less > 0) return true;
+ if (excl && less == 0) return true;
+ return false;
+}
+
+/*
+ * call-seq:
+ * overlap?(range) -> true or false
+ *
+ * Returns +true+ if +range+ overlaps with +self+, +false+ otherwise:
+ *
+ * (0..2).overlap?(1..3) #=> true
+ * (0..2).overlap?(3..4) #=> false
+ * (0..).overlap?(..0) #=> true
+ *
+ * With non-range argument, raises TypeError.
+ *
+ * (1..3).overlap?(1) # TypeError
+ *
+ * Returns +false+ if an internal call to <tt><=></tt> returns +nil+;
+ * that is, the operands are not comparable.
+ *
+ * (1..3).overlap?('a'..'d') # => false
+ *
+ * Returns +false+ if +self+ or +range+ is empty. "Empty range" means
+ * that its begin value is larger than, or equal for an exclusive
+ * range, its end value.
+ *
+ * (4..1).overlap?(2..3) # => false
+ * (4..1).overlap?(..3) # => false
+ * (4..1).overlap?(2..) # => false
+ * (2...2).overlap?(1..2) # => false
+ *
+ * (1..4).overlap?(3..2) # => false
+ * (..4).overlap?(3..2) # => false
+ * (1..).overlap?(3..2) # => false
+ * (1..2).overlap?(2...2) # => false
+ *
+ * Returns +false+ if the begin value one of +self+ and +range+ is
+ * larger than, or equal if the other is an exclusive range, the end
+ * value of the other:
+ *
+ * (4..5).overlap?(2..3) # => false
+ * (4..5).overlap?(2...4) # => false
+ *
+ * (1..2).overlap?(3..4) # => false
+ * (1...3).overlap?(3..4) # => false
+ *
+ * Returns +false+ if the end value one of +self+ and +range+ is
+ * larger than, or equal for an exclusive range, the end value of the
+ * other:
+ *
+ * (4..5).overlap?(2..3) # => false
+ * (4..5).overlap?(2...4) # => false
+ *
+ * (1..2).overlap?(3..4) # => false
+ * (1...3).overlap?(3..4) # => false
+ *
+ * Note that the method wouldn't make any assumptions about the beginless
+ * range being actually empty, even if its upper bound is the minimum
+ * possible value of its type, so all this would return +true+:
+ *
+ * (...-Float::INFINITY).overlap?(...-Float::INFINITY) # => true
+ * (..."").overlap?(..."") # => true
+ * (...[]).overlap?(...[]) # => true
+ *
+ * Even if those ranges are effectively empty (no number can be smaller than
+ * <tt>-Float::INFINITY</tt>), they are still considered overlapping
+ * with themselves.
+ *
+ * Related: Range#cover?.
+ */
+
+static VALUE
+range_overlap(VALUE range, VALUE other)
+{
+ if (!rb_obj_is_kind_of(other, rb_cRange)) {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Range)",
+ rb_class_name(rb_obj_class(other)));
}
+
+ VALUE self_beg = RANGE_BEG(range);
+ VALUE self_end = RANGE_END(range);
+ int self_excl = EXCL(range);
+ VALUE other_beg = RANGE_BEG(other);
+ VALUE other_end = RANGE_END(other);
+ int other_excl = EXCL(other);
+
+ if (empty_region_p(self_beg, other_end, other_excl)) return Qfalse;
+ if (empty_region_p(other_beg, self_end, self_excl)) return Qfalse;
+
+ if (!NIL_P(self_beg) && !NIL_P(other_beg)) {
+ VALUE cmp = rb_funcall(self_beg, id_cmp, 1, other_beg);
+ if (NIL_P(cmp)) return Qfalse;
+ /* if both begin values are equal, no more comparisons needed */
+ if (rb_cmpint(cmp, self_beg, other_beg) == 0) return Qtrue;
+ }
+ else if (NIL_P(self_beg) && NIL_P(other_beg)) {
+ VALUE cmp = rb_funcall(self_end, id_cmp, 1, other_end);
+ return RBOOL(!NIL_P(cmp));
+ }
+
+ if (empty_region_p(self_beg, self_end, self_excl)) return Qfalse;
+ if (empty_region_p(other_beg, other_end, other_excl)) return Qfalse;
+
+ return Qtrue;
}
/* A \Range object represents a collection of values
@@ -2307,6 +2576,7 @@ range_count(int argc, VALUE *argv, VALUE range)
* - {Comparing}[rdoc-ref:Range@Methods+for+Comparing]
* - {Iterating}[rdoc-ref:Range@Methods+for+Iterating]
* - {Converting}[rdoc-ref:Range@Methods+for+Converting]
+ * - {Methods for Working with JSON}[rdoc-ref:Range@Methods+for+Working+with+JSON]
*
* === Methods for Creating a \Range
*
@@ -2341,7 +2611,7 @@ range_count(int argc, VALUE *argv, VALUE range)
* - #%: Requires argument +n+; calls the block with each +n+-th element of +self+.
* - #each: Calls the block with each element of +self+.
* - #step: Takes optional argument +n+ (defaults to 1);
- calls the block with each +n+-th element of +self+.
+ * calls the block with each +n+-th element of +self+.
*
* === Methods for Converting
*
@@ -2349,6 +2619,16 @@ range_count(int argc, VALUE *argv, VALUE range)
* - #to_a (aliased as #entries): Returns elements of +self+ in an array.
* - #to_s: Returns a string representation of +self+ (uses #to_s).
*
+ * === Methods for Working with \JSON
+ *
+ * - ::json_create: Returns a new \Range object constructed from the given object.
+ * - #as_json: Returns a 2-element hash representing +self+.
+ * - #to_json: Returns a \JSON string representing +self+.
+ *
+ * To make these methods available:
+ *
+ * require 'json/add/range'
+ *
*/
void
@@ -2373,6 +2653,7 @@ Init_Range(void)
rb_define_method(rb_cRange, "each", range_each, 0);
rb_define_method(rb_cRange, "step", range_step, -1);
rb_define_method(rb_cRange, "%", range_percent_step, 1);
+ rb_define_method(rb_cRange, "reverse_each", range_reverse_each, 0);
rb_define_method(rb_cRange, "bsearch", range_bsearch, 0);
rb_define_method(rb_cRange, "begin", range_begin, 0);
rb_define_method(rb_cRange, "end", range_end, 0);
@@ -2393,4 +2674,5 @@ Init_Range(void)
rb_define_method(rb_cRange, "include?", range_include, 1);
rb_define_method(rb_cRange, "cover?", range_cover, 1);
rb_define_method(rb_cRange, "count", range_count, -1);
+ rb_define_method(rb_cRange, "overlap?", range_overlap, 1);
}