summaryrefslogtreecommitdiff
path: root/range.c
diff options
context:
space:
mode:
authortarui <tarui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-09-05 19:06:08 +0000
committertarui <tarui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-09-05 19:06:08 +0000
commit9ca738927293df1c7a2a1ed7e2d6cf89527b5438 (patch)
treeca0329dafd191b1b315e7f3e5f90926cfa916487 /range.c
parent19dec2a19c25f4cfb2511c313fde969436437bd0 (diff)
range.c: Range#cover? accepts Range object. [Feature #14473]
* range.c (range_cover): add code for range argument. If the argument is a Range, check it is or is not covered by the reciver. If it can be treated as a sequence, this method treats it that way. * test/ruby/test_range.rb (class TestRange): add tests for this feature. This patch is written by Owen Stephens. thank you! git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64640 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'range.c')
-rw-r--r--range.c61
1 files changed, 57 insertions, 4 deletions
diff --git a/range.c b/range.c
index 0629d2ee38..2763b323eb 100644
--- a/range.c
+++ b/range.c
@@ -1334,10 +1334,12 @@ range_include_internal(VALUE range, VALUE val)
return Qundef;
}
+static int r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val);
/*
* call-seq:
- * rng.cover?(obj) -> true or false
+ * rng.cover?(obj) -> true or false
+ * rng.cover?(range) -> true or false
*
* Returns <code>true</code> if +obj+ is between the begin and end of
* the range.
@@ -1345,9 +1347,21 @@ range_include_internal(VALUE range, VALUE val)
* This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
* and <code>begin <= obj < end</code> when #exclude_end? is +true+.
*
- * ("a".."z").cover?("c") #=> true
- * ("a".."z").cover?("5") #=> false
- * ("a".."z").cover?("cc") #=> true
+ * Returns <code>true</code> for a Range when it is covered by the reciver,
+ * by comparing the begin and end values. If the argument can be treated as
+ * a sequence, this method treats it that way. In the specific case of
+ * <code>(a..b).cover?(c...d)</code> with <code>a <= c && b < d</code>,
+ * end of sequence must be calculated, which may exhibit poor performance if
+ * c is non-numeric. Returns <code>false</code> if the begin value of the
+ * Range is larger than the end value.
+ *
+ * Return
+ * ("a".."z").cover?("c") #=> true
+ * ("a".."z").cover?("5") #=> false
+ * ("a".."z").cover?("cc") #=> true
+ * (1..5).cover?(2..3) #=> true
+ * (1..5).cover?(0..6) #=> false
+ * (1..5).cover?(1...6) #=> true
*/
static VALUE
@@ -1357,10 +1371,49 @@ range_cover(VALUE range, VALUE val)
beg = RANGE_BEG(range);
end = RANGE_END(range);
+
+ if (rb_obj_is_kind_of(val, rb_cRange)) {
+ return RBOOL(r_cover_range_p(range, beg, end, val));
+ }
return r_cover_p(range, beg, end, val);
}
static VALUE
+r_call_max(VALUE r)
+{
+ return rb_funcallv(r, rb_intern("max"), 0, 0);
+}
+
+static int
+r_cover_range_p(VALUE range, VALUE beg, VALUE end, VALUE val)
+{
+ VALUE val_beg, val_end, val_max;
+ int cmp_end;
+
+ val_beg = RANGE_BEG(val);
+ val_end = RANGE_END(val);
+
+ if (!NIL_P(end) && NIL_P(val_end)) return FALSE;
+ if (!NIL_P(val_end) && r_less(val_beg, val_end) > -EXCL(val)) return FALSE;
+ if (!r_cover_p(range, beg, end, val_beg)) return FALSE;
+
+ cmp_end = r_less(end, val_end);
+
+ if (EXCL(range) == EXCL(val)) {
+ return cmp_end >= 0;
+ } else if (EXCL(range)) {
+ return cmp_end > 0;
+ } else if (cmp_end >= 0) {
+ return TRUE;
+ }
+
+ val_max = rb_rescue2(r_call_max, val, NULL, Qnil, rb_eTypeError, (VALUE)0);
+ if (val_max == Qnil) return FALSE;
+
+ return r_less(end, val_max) >= 0;
+}
+
+static VALUE
r_cover_p(VALUE range, VALUE beg, VALUE end, VALUE val)
{
if (r_less(beg, val) <= 0) {