diff --git a/frontend/util.lua b/frontend/util.lua index f364862d1..fe0ab9fb0 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -491,6 +491,73 @@ function util.arrayReferences(t, n, m, l) return false end +-- A set of binary search implementations for plain arrays. +-- Should be easy to tweak for arrays of hashes (c.f., UIManager:schedule), +-- or arrays sorted in descending order (c.f., ReadHistory). +-- refs: https://en.wikipedia.org/wiki/Binary_search_algorithm +-- https://rosettacode.org/wiki/Binary_search +--- Perform a binary search for `value` in a *sorted* (ascending) `array`. +---- @param array Lua table (array only, sorted, ascending, every value must match the type of `value` and support comparison operators) +---- @param value +---- @return int index of value in array, or a (nil, insertion index) tuple if value was not found. +function util.bsearch(array, value) + local lo = 1 + local hi = #array + while lo <= hi do + -- invariants: value > array[i] for all i < lo + -- value < array[i] for all i > hi + local mid = bit.rshift(lo + hi, 1) + if array[mid] > value then + hi = mid - 1 + elseif array[mid] < value then + lo = mid + 1 + else + return mid + end + end + return nil, lo +end + +--- Perform a leftmost insertion binary search for `value` in a *sorted* (ascending) `array`. +---- @param array Lua table (array only, sorted, ascending, every value must match the type of `value` and support comparison operators) +---- @param value +---- @return int leftmost insertion index of value in array. +function util.bsearch_left(array, value) + local lo = 1 + local hi = #array + while lo <= hi do + -- invariants: value > array[i] for all i < lo + -- value <= array[i] for all i > hi + local mid = bit.rshift(lo + hi, 1) + if array[mid] >= value then + hi = mid - 1 + else + lo = mid + 1 + end + end + return lo +end + +--- Perform a rightmost insertion binary search for `value` in a *sorted* (ascending) `array`. +---- @param array Lua table (array only, sorted, ascending, every value must match the type of `value` and support comparison operators) +---- @param value +---- @return int rightmost insertion index of value in array. +function util.bsearch_right(array, value) + local lo = 1 + local hi = #array + while lo <= hi do + -- invariants: value >= array[i] for all i < low + -- value < array[i] for all i > high + local mid = bit.rshift(lo + hi, 1) + if array[mid] > value then + hi = mid - 1 + else + lo = mid + 1 + end + end + return lo +end + -- Merge t2 into t1, overwriting existing elements if they already exist -- Probably not safe with nested tables (c.f., https://stackoverflow.com/q/1283388) ---- @param t1 Lua table