diff --git a/array.c b/array.c
index 7360a68..97f5d55 100644
--- a/array.c
+++ b/array.c
@@ -2158,6 +2158,55 @@ rb_ary_sort(VALUE ary)
     return ary;
 }
 
+/*
+ *  call-seq:
+ *     ary.bsearch {|x| block }  -> elem
+ */
+
+static VALUE
+rb_ary_bsearch(VALUE ary)
+{
+    long low = 0, high = RARRAY_LEN(ary), mid;
+    int smaller, satisfied = 0;
+    VALUE v, val;
+
+    while (low < high) {
+	mid = low + ((high - low) / 2);
+	val = rb_ary_entry(ary, mid);
+	v = rb_yield(val);
+	if (FIXNUM_P(v)) {
+	    if (FIX2INT(v) == 0) return val;
+	    smaller = FIX2INT(v) < 0;
+	}
+	else if (v == Qtrue) {
+	    satisfied = 1;
+	    smaller = 1;
+	}
+	else if (v == Qfalse || v == Qnil) {
+	    smaller = 0;
+	}
+	else if (rb_obj_is_kind_of(v, rb_cNumeric)) {
+	    switch (rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)) < 0) {
+		case 0: return val;
+		case 1: smaller = 1;
+		case -1: smaller = 0;
+	    }
+	}
+	else {
+	    smaller = RTEST(v);
+	}
+	if (smaller) {
+	    high = mid;
+	}
+	else {
+	    low = mid + 1;
+	}
+    }
+    if (low == RARRAY_LEN(ary)) return Qnil;
+    if (!satisfied) return Qnil;
+    return rb_ary_entry(ary, low);
+}
+
 
 static VALUE
 sort_by_i(VALUE i)
@@ -4745,6 +4794,7 @@ Init_Array(void)
     rb_define_method(rb_cArray, "take_while", rb_ary_take_while, 0);
     rb_define_method(rb_cArray, "drop", rb_ary_drop, 1);
     rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
+    rb_define_method(rb_cArray, "bsearch", rb_ary_bsearch, 0);
 
     id_cmp = rb_intern("<=>");
     sym_random = ID2SYM(rb_intern("random"));
diff --git a/range.c b/range.c
index b7ae191..0c67b5e 100644
--- a/range.c
+++ b/range.c
@@ -13,6 +13,11 @@
 #include "ruby/encoding.h"
 #include "internal.h"
 
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#include <math.h>
+
 VALUE rb_cRange;
 static ID id_cmp, id_succ, id_beg, id_end, id_excl;
 
@@ -436,6 +441,273 @@ range_step(int argc, VALUE *argv, VALUE range)
     return range;
 }
 
+/*
+ *  call-seq:
+ *     rng.bsearch {|obj| block }  -> element
+ *
+ *  Finds a value in range which meets the given condition in O(n log n)
+ *  where n = (rng.begin - rng.end), by using binary search.
+ *
+ *  The given block receives a current value, determines if it meets the
+ *  condition and controls search.
+ *  When the condition is satisfied and you want to stop search, the block
+ *  should return zero, and then this method return the value immediately.
+ *  When the condition is satisfied and you want to find minimum bound,
+ *  the block should return true.  When the condition is not satisfied and
+ *  the current value is smaller than wanted, the block should return false,
+ *  nil or an integer greater than zero. When the condition is not satisfied
+ *  and the current value is larger than wanted, the block should return an
+ *  integer less than zero.
+ *  Unless the block returns zero, the search will continue until a minimum
+ *  bound is found or no match is found.  Returns the minimum bound if any,
+ *  or returns nil when no match is found.
+ *
+ *  The block must be monotone; there must be two values a and b so that
+ *  the block returns:
+ *  - false, nil or an integer greater than zero for all x of [begin of
+ *    range, a), and
+ *  - zero or true for all x of [a, b), and
+ *  - an integer less than zero for all x of [b, end of range).
+ *  If the block is not monotone, the result is unspecified.
+ *
+ *  This method takes O(n log n), but it is unspecified which value is
+ *  actually picked up at each iteration.
+ *
+ *     ary = [0, 4, 7, 10, 12]
+ *     (0...ary.size).bsearch {|i| ary[i] >= 4 } #=> 1
+ *     (0...ary.size).bsearch {|i| ary[i] >= 6 } #=> 2
+ *     (0...ary.size).bsearch {|i| ary[i] >= 8 } #=> 3
+ *     (0...ary.size).bsearch {|i| ary[i] >= 100 } #=> nil
+ *
+ *     (0.0...Float::INFINITY).bsearch {|x| Math.log(x) >= 0 } #=> 1.0
+ *
+ *     ary = [0, 100, 100, 100, 200]
+ *     (0..4).bsearch {|i| 100 - i } #=> 1, 2 or 3
+ *     (0..4).bsearch {|i| 300 - i } #=> nil
+ *     (0..4).bsearch {|i|  50 - i } #=> nil
+ */
+
+static VALUE
+range_bsearch(VALUE range)
+{
+    VALUE beg, end;
+    int smaller, satisfied = 0;
+
+#define BSEARCH_CHECK(val) \
+    do { \
+	VALUE v = rb_yield(val); \
+	if (FIXNUM_P(v)) { \
+	    if (FIX2INT(v) == 0) return val; \
+	    smaller = FIX2INT(v) < 0; \
+	} \
+	else if (v == Qtrue) { \
+	    satisfied = 1; \
+	    smaller = 1; \
+	} \
+	else if (v == Qfalse || v == Qnil) { \
+	    smaller = 0; \
+	} \
+	else if (rb_obj_is_kind_of(v, rb_cNumeric)) { \
+	    switch (rb_cmpint(rb_funcall(v, id_cmp, 1, INT2FIX(0)), v, INT2FIX(0)) < 0) { \
+		case 0: return val; \
+		case 1: smaller = 1; \
+		case -1: smaller = 0; \
+	    } \
+	} \
+	else { \
+	    smaller = RTEST(v); \
+	} \
+    } 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;
+	if (EXCL(range)) high--;
+	org_high = high;
+
+	while (low < high) {
+	    mid = low + ((high - low) / 2);
+	    BSEARCH_CHECK(INT2FIX(mid));
+	    if (smaller) {
+		high = mid;
+	    }
+	    else {
+		low = mid + 1;
+	    }
+	}
+	if (low == org_high) {
+	    BSEARCH_CHECK(INT2FIX(low));
+	    if (!smaller) return Qnil;
+	}
+	if (!satisfied) return Qnil;
+	return INT2FIX(low);
+    }
+    else if (TYPE(beg) == T_FLOAT || TYPE(end) == T_FLOAT) {
+	double low  = RFLOAT_VALUE(rb_Float(beg));
+	double high = RFLOAT_VALUE(rb_Float(end));
+	double mid, org_high, last_found_key = 0.0;
+	int count, found = 0;
+#ifdef FLT_RADIX
+#ifdef DBL_MANT_DIG
+#define COUNT (((FLT_RADIX) - 1) * (DBL_MANT_DIG + DBL_MAX_EXP) + 100)
+#else
+#define count (53 + 1023 + 100)
+#endif
+#else
+#define count (53 + 1023 + 100)
+#endif
+	if (isinf(high) && high > 0) {
+	    double nhigh = 1.0, inc;
+	    if (nhigh < low) nhigh = low;
+	    count = COUNT;
+	    while (count >= 0 && !isinf(nhigh)) {
+		BSEARCH_CHECK(DBL2NUM(nhigh));
+		if (smaller) break;
+		high = nhigh;
+		nhigh *= 2;
+		count--;
+	    }
+	    if (isinf(nhigh) || count < 0) {
+		inc = high / 2;
+		count = COUNT;
+		while (count >= 0 && inc > 0) {
+		    nhigh = high + inc;
+		    if (!isinf(nhigh)) {
+			BSEARCH_CHECK(DBL2NUM(nhigh));
+			if (smaller) {
+			    low = high;
+			    high = nhigh;
+			    goto binsearch;
+			}
+			else {
+			    high = nhigh;
+			}
+		    }
+		    inc /= 2;
+		    count--;
+		}
+		high *= 2; /* generate infinity */
+		if (isinf(high) && !EXCL(range)) {
+		    BSEARCH_CHECK(DBL2NUM(high));
+		    if (!satisfied) return Qnil;
+		    if (smaller) return DBL2NUM(high);
+		}
+		return Qnil;
+	    }
+	    high = nhigh;
+	}
+	if (isinf(low) && low < 0) {
+	    double nlow = -1.0, dec;
+	    if (nlow > high) nlow = high;
+	    count = COUNT;
+	    while (count >= 0 && !isinf(nlow)) {
+		BSEARCH_CHECK(DBL2NUM(nlow));
+		if (!smaller) break;
+		low = nlow;
+		nlow *= 2;
+		count--;
+	    }
+	    if (isinf(nlow) || count < 0) {
+		dec = low / 2;
+		count = COUNT;
+		while (count >= 0 && dec < 0) {
+		    nlow = low + dec;
+		    if (!isinf(nlow)) {
+			BSEARCH_CHECK(DBL2NUM(nlow));
+			if (!smaller) {
+			    high = low;
+			    low = nlow;
+			    goto binsearch;
+			}
+			else {
+			    low = nlow;
+			}
+		    }
+		    dec /= 2;
+		    count--;
+		}
+		nlow = low * 2; /* generate infinity */
+		if (isinf(nlow)) {
+		    BSEARCH_CHECK(DBL2NUM(nlow));
+		    if (!satisfied) return Qnil;
+		    if (smaller) return DBL2NUM(nlow);
+		}
+		if (!satisfied) return Qnil;
+		return DBL2NUM(low);
+	    }
+	    low = nlow;
+	}
+
+    binsearch:
+	org_high = high;
+	count = COUNT;
+	while (low < high && count >= 0) {
+	    mid = low + ((high - low) / 2);
+	    BSEARCH_CHECK(DBL2NUM(mid));
+	    if (smaller) {
+		found = 1;
+		last_found_key = high;
+		high = mid;
+	    }
+	    else {
+		low = mid;
+	    }
+	    count--;
+	}
+	BSEARCH_CHECK(DBL2NUM(low));
+	if (!smaller) {
+	    BSEARCH_CHECK(DBL2NUM(high));
+	    if (!smaller) {
+		if (found) {
+		    low = last_found_key;
+		}
+		else {
+		    return Qnil;
+		}
+	    }
+	    low = high;
+	}
+	if (!satisfied) return Qnil;
+	if (EXCL(range) && low >= org_high) return Qnil;
+	return DBL2NUM(low);
+#undef COUNT
+    }
+    else if (!NIL_P(rb_check_to_integer(beg, "to_int")) &&
+	     !NIL_P(rb_check_to_integer(end, "to_int"))) {
+	VALUE low = beg;
+	VALUE high = end;
+	VALUE mid, org_high;
+	if (EXCL(range)) 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), '/', 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;
+	}
+	if (!satisfied) return Qnil;
+	return low;
+    }
+    else {
+	rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));
+    }
+    return range;
+    
+}
+
 static VALUE
 each_i(VALUE v, void *arg)
 {
@@ -1035,6 +1307,7 @@ Init_Range(void)
     rb_define_method(rb_cRange, "hash", range_hash, 0);
     rb_define_method(rb_cRange, "each", range_each, 0);
     rb_define_method(rb_cRange, "step", range_step, -1);
+    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);
     rb_define_method(rb_cRange, "first", range_first, -1);
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 738c7cd..da60363 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -347,4 +347,78 @@ class TestRange < Test::Unit::TestCase
       assert !x.eql?(z)
     }
   end
+
+  def test_bsearch_for_fixnum
+    ary = [3, 4, 7, 9, 12]
+    assert_equal(0, (0...ary.size).bsearch {|i| ary[i] >= 2 })
+    assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 4 })
+    assert_equal(2, (0...ary.size).bsearch {|i| ary[i] >= 6 })
+    assert_equal(3, (0...ary.size).bsearch {|i| ary[i] >= 8 })
+    assert_equal(4, (0...ary.size).bsearch {|i| ary[i] >= 10 })
+    assert_equal(nil, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+    assert_equal(0, (0...ary.size).bsearch {|i| true })
+    assert_equal(nil, (0...ary.size).bsearch {|i| false })
+
+    ary = [0, 100, 100, 100, 200]
+    assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+  end
+
+  def test_bsearch_for_float
+    inf = Float::INFINITY
+    assert_in_delta(10.0, (0.0...100.0).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+    assert_in_delta(10.0, (0.0...inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+    assert_in_delta(-10.0, (-inf..100.0).bsearch {|x| x >= 0 || Math.log(-x / 10) < 0 }, 0.0001)
+    assert_in_delta(10.0, (-inf..inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+    assert_equal(nil, (-inf..5).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+    assert_in_delta(10.0, (-inf.. 10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+    assert_equal(nil,     (-inf...10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+    assert_equal(nil, (-inf..inf).bsearch { false })
+    assert_equal(-inf, (-inf..inf).bsearch { true })
+
+    assert_equal(inf, (0..inf).bsearch {|x| x == inf })
+    assert_equal(nil, (0...inf).bsearch {|x| x == inf })
+
+    v = (-inf..0).bsearch {|x| x != -inf }
+    assert_operator(-Float::MAX, :>=, v)
+    assert_operator(-inf, :<, v)
+
+    v = (0.0..1.0).bsearch {|x| x > 0 } # the nearest positive value to 0.0
+    assert_in_delta(0, v, 0.0001)
+    assert_operator(0, :<, v)
+    assert_equal(0.0, (-1.0..0.0).bsearch {|x| x >= 0 })
+    assert_equal(nil, (-1.0...0.0).bsearch {|x| x >= 0 })
+
+    v = (0..Float::MAX).bsearch {|x| x >= Float::MAX }
+    assert_in_delta(Float::MAX, v)
+    assert_equal(nil, v.infinite?)
+
+    v = (0..inf).bsearch {|x| x >= Float::MAX }
+    assert_in_delta(Float::MAX, v)
+    assert_equal(nil, v.infinite?)
+
+    v = (-Float::MAX..0).bsearch {|x| x > -Float::MAX }
+    assert_operator(-Float::MAX, :<, v)
+    assert_equal(nil, v.infinite?)
+
+    v = (-inf..0).bsearch {|x| x >= -Float::MAX }
+    assert_in_delta(-Float::MAX, v)
+    assert_equal(nil, v.infinite?)
+  end
+
+  def test_bsearch_for_bignum
+    bignum = 2**100
+    ary = [3, 4, 7, 9, 12]
+    assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 2 })
+    assert_equal(bignum + 1, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 4 })
+    assert_equal(bignum + 2, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 6 })
+    assert_equal(bignum + 3, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 8 })
+    assert_equal(bignum + 4, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 10 })
+    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(nil, (bignum...bignum+ary.size).bsearch {|i| false })
+
+    assert_raise(TypeError) { ("a".."z").bsearch {} }
+  end
 end
