From 55d17ec9909df199ccb3a967cd8f45987618feee Mon Sep 17 00:00:00 2001 From: Pierre Curto Date: Mon, 3 Jul 2023 20:48:37 +0200 Subject: [PATCH] add the std::sort::binarysearch module Signed-off-by: Pierre Curto --- lib/std/sort/binarysearch.c3 | 66 +++++++++++++++++++++++++++ test/unit/stdlib/sort/binarysearch.c3 | 37 +++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 lib/std/sort/binarysearch.c3 create mode 100644 test/unit/stdlib/sort/binarysearch.c3 diff --git a/lib/std/sort/binarysearch.c3 b/lib/std/sort/binarysearch.c3 new file mode 100644 index 000000000..734f1c747 --- /dev/null +++ b/lib/std/sort/binarysearch.c3 @@ -0,0 +1,66 @@ +module std::sort::binarysearch; + +/* + * Compare x and y and return one of the following values: + * -1 if x < y + * 0 if x == y + * 1 if x > y + */ +def Comparer = fn int (void *x, void *y); + +/** + * Perform a binary search over the sorted array and return the smallest index + * in [0, array.len) where cmp(i) is true and cmp(j) is true for j in [i, array.len). + * @require is_array_or_subarray(array) "array must be an array or subarray" + **/ +macro usz cmp_search(array, x, Comparer cmp) +{ + usz i; + for (usz j = array.len; i < j;) + { + usz half = (i + j) / 2; + int res = cmp(&array[half], &x); + switch { + case res > 0: j = half; + case res < 0: i = half + 1; + default: return half; + } + } + return i; +} + +/** + * Perform a binary search over the sorted array and return the index + * in [0, array.len) where x would be inserted. + * @require is_array_or_subarray(array) "array must be an array or subarray" + **/ +macro usz search(array, x) +{ + usz i; + for (usz j = array.len; i < j;) + { + usz half = (i + j) / 2; + switch { + case greater(array[half], x): j = half; + case less(array[half], x): i = half + 1; + default: return half; + } + } + return i; +} + +macro bool is_array_or_subarray(slice) @private +{ + $switch ($typeof(slice).kindof) + $case POINTER: + var $Inner = $typefrom($typeof(bytes).inner); + $if $Inner.kindof == ARRAY: + return true; + $endif + $case ARRAY: + $case SUBARRAY: + return true; + $default: + return false; + $endswitch +} \ No newline at end of file diff --git a/test/unit/stdlib/sort/binarysearch.c3 b/test/unit/stdlib/sort/binarysearch.c3 new file mode 100644 index 000000000..c5deaeb9b --- /dev/null +++ b/test/unit/stdlib/sort/binarysearch.c3 @@ -0,0 +1,37 @@ +module binarysearch_test @test; +import std::sort::binarysearch; + +struct SearchTest +{ + int[] data; + int x; + int index; +} + +fn void search() +{ + SearchTest[] tcases = { + { {}, 0, 0 }, + { {1, 2, 3}, 1, 0 }, + { {1, 2, 3}, 2, 1 }, + { {1, 2, 3}, 3, 2 }, + { {1, 2, 3}, 4, 3 }, + { {10, 20, 30}, 14, 1 }, + { {10, 20, 30}, 26, 2 }, + }; + + foreach (tc : tcases) + { + usz idx = binarysearch::search(tc.data, tc.x); + assert(idx == tc.index, "%s: got %d; want %d", tc.data, idx, tc.index); + + usz cmp_idx = binarysearch::cmp_search(tc.data, tc.x, &cmp_int); + assert(cmp_idx == tc.index, "%s: got %d; want %d", tc.data, cmp_idx, tc.index); + } +} + +module binarysearch_test; + +fn int cmp_int(void* x, void* y) { + return *(int*)x - *(int*)y; +} \ No newline at end of file