summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-array-ext
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-array-ext')
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb57
-rw-r--r--mrbgems/mruby-array-ext/src/array.c22
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb49
3 files changed, 92 insertions, 36 deletions
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index 1e6d4d581..716eabe06 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -595,31 +595,58 @@ class Array
def bsearch(&block)
return to_enum :bsearch unless block_given?
+ if idx = bsearch_index(&block)
+ self[idx]
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.bsearch_index {|x| block } -> int or nil
+ #
+ # By using binary search, finds an index of a value from this array which
+ # meets the given condition in O(log n) where n is the size of the array.
+ #
+ # It supports two modes, depending on the nature of the block and they are
+ # exactly the same as in the case of #bsearch method with the only difference
+ # being that this method returns the index of the element instead of the
+ # element itself. For more details consult the documentation for #bsearch.
+
+ def bsearch_index(&block)
+ return to_enum :bsearch_index unless block_given?
+
low = 0
- high = self.size
+ high = size
satisfied = false
+
while low < high
- mid = low + ((high - low) / 2).truncate
- val = self[mid]
- v = block.call(val)
- if v.is_a?(Integer)
- return val if v == 0
- smaller = v < 0
- elsif v == true
+ mid = ((low+high)/2).truncate
+ res = block.call self[mid]
+
+ case res
+ when 0 # find-any mode: Found!
+ return mid
+ when Numeric # find-any mode: Continue...
+ in_lower_half = res < 0
+ when true # find-min mode
+ in_lower_half = true
satisfied = true
- smaller = true
- elsif v == false || v.nil?
- smaller = false
+ when false, nil # find-min mode
+ in_lower_half = false
+ else
+ raise TypeError, 'invalid block result (must be numeric, true, false or nil)'
end
- if smaller
+
+ if in_lower_half
high = mid
else
low = mid + 1
end
end
- return nil if low == self.size
- return nil unless satisfied
- self[low]
+
+ satisfied ? low : nil
end
##
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index 6919186bd..72e5080f1 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -174,7 +174,7 @@ static mrb_value
mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int i, j, k, len;
+ mrb_int i, j, k, len, alen = ARY_LEN(a);
mrb_value index;
mrb_value val;
mrb_value *ptr;
@@ -185,7 +185,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
switch (mrb_type(index)) {
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, a->len, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
goto delete_pos_len;
}
else {
@@ -202,25 +202,25 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
i = mrb_fixnum(index);
delete_pos_len:
- if (i < 0) i += a->len;
- if (i < 0 || a->len < i) return mrb_nil_value();
+ if (i < 0) i += alen;
+ if (i < 0 || alen < i) return mrb_nil_value();
if (len < 0) return mrb_nil_value();
- if (a->len == i) return mrb_ary_new(mrb);
- if (len > a->len - i) len = a->len - i;
+ if (alen == i) return mrb_ary_new(mrb);
+ if (len > alen - i) len = alen - i;
ary = mrb_ary_new_capa(mrb, len);
-
+ ptr = ARY_PTR(a);
for (j = i, k = 0; k < len; ++j, ++k) {
- mrb_ary_push(mrb, ary, a->ptr[j]);
+ mrb_ary_push(mrb, ary, ptr[j]);
}
- ptr = a->ptr + i;
- for (j = i; j < a->len - len; ++j) {
+ ptr += i;
+ for (j = i; j < alen - len; ++j) {
*ptr = *(ptr+len);
++ptr;
}
- mrb_ary_resize(mrb, self, a->len - len);
+ mrb_ary_resize(mrb, self, alen - len);
return ary;
}
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 95a796cf9..c0db1b1cc 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -228,18 +228,47 @@ end
assert("Array#bsearch") do
# Find minimum mode
- a = [0, 4, 7, 10, 12]
- assert_include [4, 7], a.bsearch {|x| x >= 4 }
- assert_equal 7, a.bsearch {|x| x >= 6 }
- assert_equal 0, a.bsearch {|x| x >= -1 }
- assert_nil a.bsearch {|x| x >= 100 }
+ a = [0, 2, 4]
+ assert_equal 0, a.bsearch{ |x| x >= -1 }
+ assert_equal 0, a.bsearch{ |x| x >= 0 }
+ assert_equal 2, a.bsearch{ |x| x >= 1 }
+ assert_equal 2, a.bsearch{ |x| x >= 2 }
+ assert_equal 4, a.bsearch{ |x| x >= 3 }
+ assert_equal 4, a.bsearch{ |x| x >= 4 }
+ assert_nil a.bsearch{ |x| x >= 5 }
# Find any mode
- a = [0, 4, 7, 10, 12]
- assert_include [4, 7], a.bsearch {|x| 1 - (x / 4).truncate }
- assert_nil a.bsearch {|x| 4 - (x / 2).truncate }
- assert_equal(nil, a.bsearch {|x| 1 })
- assert_equal(nil, a.bsearch {|x| -1 })
+ a = [0, 4, 8]
+ def between(lo, x, hi)
+ if x < lo
+ 1
+ elsif x > hi
+ -1
+ else
+ 0
+ end
+ end
+ assert_nil a.bsearch{ |x| between(-3, x, -1) }
+ assert_equal 0, a.bsearch{ |x| between(-1, x, 1) }
+ assert_nil a.bsearch{ |x| between( 1, x, 3) }
+ assert_equal 4, a.bsearch{ |x| between( 3, x, 5) }
+ assert_nil a.bsearch{ |x| between( 5, x, 7) }
+ assert_equal 8, a.bsearch{ |x| between( 7, x, 9) }
+ assert_nil a.bsearch{ |x| between( 9, x, 11) }
+
+ assert_equal 0, a.bsearch{ |x| between( 0, x, 3) }
+ assert_equal 4, a.bsearch{ |x| between( 0, x, 4) }
+ assert_equal 4, a.bsearch{ |x| between( 4, x, 8) }
+ assert_equal 8, a.bsearch{ |x| between( 5, x, 8) }
+
+ # Invalid block result
+ assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do
+ a.bsearch{ 'I like to watch the world burn' }
+ end
+end
+
+assert("Array#bsearch_index") do
+ # tested through Array#bsearch
end
assert("Array#delete_if") do