1 /** Extensions to std.algorithm.searching. 2 Copyright: Per Nordlöw 2018-. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: $(WEB Per Nordlöw) 5 */ 6 module nxt.searching_ex; 7 8 /** This function returns the index of the `value` if it exist among `values`, 9 `size_t.max` otherwise. 10 11 TODO: Should we extend to isRandomAccessRange support? In that case we don't 12 get static array support by default. 13 */ 14 size_t binarySearch(R, E)(const R[] values, in E value) 15 if (is(typeof(values[0].init == E.init))) // TODO: SortedRange support 16 { 17 // value is not in the array if the array is empty 18 if (values.length == 0) { return typeof(return).max; } 19 20 immutable mid = values.length / 2; // mid offset 21 if (value == values[mid]) 22 return mid; // direct hit 23 else if (value < values[mid]) 24 return binarySearch(values[0 .. mid], value); // recurse left 25 else 26 { 27 const index = binarySearch(values[mid + 1 .. $], value); // recurse right 28 if (index != typeof(return).max) 29 return index + mid + 1; // adjust the index; it is 0-based in the right-hand side slice. 30 return index; 31 } 32 } 33 34 /// 35 @safe pure nothrow @nogc unittest 36 { 37 const int[9] x = [1, 3, 5, 6, 8, 9, 10, 13, 15]; 38 assert(x.binarySearch(0) == size_t.max); 39 assert(x.binarySearch(1) == 0); 40 assert(x.binarySearch(2) == size_t.max); 41 assert(x.binarySearch(3) == 1); 42 assert(x.binarySearch(4) == size_t.max); 43 assert(x.binarySearch(5) == 2); 44 assert(x.binarySearch(6) == 3); 45 assert(x.binarySearch(7) == size_t.max); 46 assert(x.binarySearch(8) == 4); 47 assert(x.binarySearch(9) == 5); 48 assert(x.binarySearch(10) == 6); 49 assert(x.binarySearch(11) == size_t.max); 50 assert(x.binarySearch(12) == size_t.max); 51 assert(x.binarySearch(13) == 7); 52 assert(x.binarySearch(14) == size_t.max); 53 assert(x.binarySearch(15) == 8); 54 } 55 56 import std.range : ElementType, SearchPolicy, SortedRange; 57 import std.traits: isInstanceOf; 58 59 /** Same as `range.contains()` but also outputs `index` where last occurrence of 60 `key` is either currently stored (if `true` is returned) or should be stored 61 (if `false` is returned) in order to preserve sortedness of `range`. 62 63 The elements of `range` are assumed to be sorted in default (ascending) 64 order. 65 66 TODO: Move to member of `SortedRange` either as a new name or as an 67 `contains`-overload take an extra `index` as argument. 68 */ 69 bool containsStoreIndex(SearchPolicy sp = SearchPolicy.binarySearch, R, V) 70 (R range, V value, out size_t index) 71 if (is(typeof(ElementType!R.init == V.init)) && 72 isInstanceOf!(SortedRange, R)) // TODO: check for comparsion function 73 { 74 // TODO: should we optimize for this case? 75 // if (range.empty) 76 // { 77 // index = 0; 78 // return false; // no hit 79 // } 80 index = range.length - range.upperBound!sp(value).length; // always larger than zero 81 if (index >= 1 && range[index - 1] == value) 82 { 83 --index; // make index point to last occurrence of `value` 84 assert(range.contains(value)); // assert same behaviour as existing contains 85 return true; 86 } 87 assert(!range.contains(value)); // assert same behaviour as existing contains 88 return false; 89 } 90 91 /// 92 @safe pure nothrow @nogc unittest 93 { 94 const int[0] x; 95 size_t index; 96 import std.range : assumeSorted; 97 assert(!x[].assumeSorted.containsStoreIndex(int.min, index) && index == 0); 98 assert(!x[].assumeSorted.containsStoreIndex(-1, index) && index == 0); 99 assert(!x[].assumeSorted.containsStoreIndex(0, index) && index == 0); 100 assert(!x[].assumeSorted.containsStoreIndex(1, index) && index == 0); 101 assert(!x[].assumeSorted.containsStoreIndex(int.max, index) && index == 0); 102 } 103 104 /// 105 @safe pure nothrow @nogc unittest 106 { 107 const int[2] x = [1, 3]; 108 size_t index; 109 import std.range : assumeSorted; 110 assert(!x[].assumeSorted.containsStoreIndex(int.min, index) && index == 0); 111 assert(!x[].assumeSorted.containsStoreIndex(-1, index) && index == 0); 112 assert(!x[].assumeSorted.containsStoreIndex(0, index) && index == 0); 113 assert( x[].assumeSorted.containsStoreIndex(1, index) && index == 0); 114 assert(!x[].assumeSorted.containsStoreIndex(2, index) && index == 1); 115 assert( x[].assumeSorted.containsStoreIndex(3, index) && index == 1); 116 assert(!x[].assumeSorted.containsStoreIndex(4, index) && index == 2); 117 assert(!x[].assumeSorted.containsStoreIndex(5, index) && index == 2); 118 assert(!x[].assumeSorted.containsStoreIndex(int.max, index) && index == 2); 119 }