{% for p in [64, 32] %} # Cast integer as floating number value with keeping binary structure. private def int_as_float(i : Int{{ p }}) # Integer uses two's complement to represent signed number, but # floating number value uses sign bit. This difference causes a # problem that it reproduce incorrect value when you sum positive # number and negative number. It fixes this problem. if i < 0 i = -i -i.unsafe_as(Float{{ p }}) else i.unsafe_as(Float{{ p }}) end end # Cast floating number value as integer with keeping binary structure. private def float_as_int(f : Float{{ p }}) if f < 0 f = -f -f.unsafe_as(Int{{ p }}) else f.unsafe_as(Int{{ p }}) end end private def bsearch_internal(from : Float{{ p }}, to, exclusive, &block) bsearch_internal from, to.to_f{{ p }}, exclusive do |value| yield value end end private def bsearch_internal(from, to : Float{{ p }}, exclusive) bsearch_internal from.to_f{{ p }}, to, exclusive do |value| yield value end end private def bsearch_internal(from : Float{{ p }}, to : Float{{ p }}, exclusive) from = float_as_int from to = float_as_int to to -= 1 if exclusive bsearch_internal(from, to, false){ |i| yield int_as_float i } .try{ |i| int_as_float i } end private def bsearch_internal(from : Int{{ p }}, to : Int{{ p }}, exclusive) saved_to = to satisfied = nil while from < to mid = (from < 0) == (to < 0) ? from + ((to - from) >> 1) : (from < -to) ? -(((- from - to - 1) >> 1) + 1) : ((from + to) >> 1) if yield mid satisfied = mid to = mid else from = mid + 1 end end if !exclusive && from == saved_to && yield from satisfied = from end satisfied end {% end %} struct Range(B, E) # By using binary search, returns the first element # for which the passed block returns a truthy value. # # If the block returns a falsey value, the element to be found lies # behind. If the block returns a truthy value, the element to be found # is itself or lies in front. # # Returns `nil` if the block didn't return a truthy value for any element. # # ``` # (0..10).bsearch { |x| x >= 5 } # => 5 # (0..Float64::INFINITY).bsearch { |x| x ** 4 >= 256 } # => 4 # ``` def bsearch(&block : B | E -> _) from = self.begin to = self.end # If the range consists of floating value, # it uses specialized implementation for floating value. # This implementation is very fast. For example, # `(1..1e300).bsearch{ false }` loops over 2000 times in # popular implementation, but in this implementation loops 65 times # at most. {% for v in %w(from to) %} if {{ v.id }}.is_a?(Float::Primitive) return bsearch_internal from, to, self.excludes_end? do |value| yield value end end {% end %} saved_to = to satisfied = nil while from < to mid = from + ((to - from) >> 1) if yield mid satisfied = mid to = mid else from = mid + 1 end end if !self.excludes_end? && from == saved_to && yield from satisfied = from end satisfied end end