Reimplement binary_search_by to be simpler

This commit is contained in:
gingerBill
2023-12-13 01:37:15 +00:00
parent c8cc130744
commit b011487778
+11 -29
View File
@@ -163,38 +163,20 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
@(require_results) @(require_results)
binary_search_by :: proc(array: $A/[]$T, key: T, f: proc(T, T) -> Ordering) -> (index: int, found: bool) #no_bounds_check { binary_search_by :: proc(array: $A/[]$T, key: T, f: proc(T, T) -> Ordering) -> (index: int, found: bool) #no_bounds_check {
// INVARIANTS: n := len(array)
// - 0 <= left <= (left + size = right) <= len(array) left, right := 0, n
// - f returns .Less for everything in array[:left]
// - f returns .Greater for everything in array[right:]
size := len(array)
left := 0
right := size
for left < right { for left < right {
mid := left + size / 2 mid := int(uint(left+right) >> 1)
if f(array[mid], key) == .Less {
// Steps to verify this is in-bounds: left = mid+1
// 1. We note that `size` is strictly positive due to the loop condition } else {
// 2. Therefore `size/2 < size` // .Equal or .Greater
// 3. Adding `left` to both sides yields `(left + size/2) < (left + size)` right = mid
// 4. We know from the invariant that `left + size <= len(array)`
// 5. Therefore `left + size/2 < self.len()`
cmp := f(key, array[mid])
left = mid + 1 if cmp == .Less else left
right = mid if cmp == .Greater else right
switch cmp {
case .Equal: return mid, true
case .Less: right = mid
case .Greater: left = mid + 1
} }
size = right - left
} }
// left == right
return left, false // f(array[left-1], key) == .Less (if left > 0)
return left, left < n && f(array[left], key) == .Equal
} }
@(require_results) @(require_results)