1 module collections.sortedlist; 2 3 import collections.commons; 4 import std.functional : binaryFun; 5 6 /** 7 * Implements a sorted list of type T. 8 * The container ensures that all of it's elements are ordered after any insertion. 9 * Has some optimization to stop searching after a given value has passed. 10 * `cmp` can set both the direction of the array, and what parameters should be tested. 11 * If `allowDuplicates` set false, the collection will act like a sorted set over an array, and won't allow any insertion of preexisting values. 12 * Important: The elements stored within this container must have opCmp override with attributes `@safe`, `nothrow`, `pure`. 13 */ 14 public struct SortedList(E, alias cmp = "a < b", bool allowDuplicates = true, alias equal = "a == b") { 15 private E[] _array; ///The array where the elements are stored. 16 private size_t begin; ///The position of the current front iteration. 17 private size_t end; ///The position of the current back iteration. 18 /** 19 * Constructs a sorted list out from an array. 20 */ 21 public this(E[] _array) @safe nothrow pure { 22 this._array.reserve = _array.length; 23 foreach (E key; _array) { 24 put(key); 25 } 26 assert(this._array.length == _array.length); 27 assert(this._array.length == end); 28 } 29 /** 30 * Constructs a sorted list out from a range. 31 */ 32 public this(Range)(Range src) { 33 foreach (E key; src) { 34 put(key); 35 } 36 //assert(this._array.length == _array.length); 37 assert(this._array.length == end); 38 } 39 /** 40 * Adds a new element while keeping the order intact. 41 * 42 * TO DO: Maybe even more optimizations? Maybe look where the element would fit, then shift that position? 43 */ 44 void put(E a) @safe nothrow pure { 45 static if(!allowDuplicates){ 46 if(has(a)) return; 47 } 48 _array.length = _array.length + 1; 49 for(sizediff_t i = _array.length - 2 ; i >= 0 ; i--) { 50 E b = _array[i]; 51 if(binaryFun!cmp(a, b)) { //first position found 52 _array[i + 1] = a; 53 end++; 54 return; 55 } else { 56 _array[i + 1] = b; 57 } 58 } 59 _array[0] = a; 60 end++; 61 } 62 /** 63 * Removes the n-th element while keeping the order intact. 64 * Returns the removed element. 65 */ 66 E remove(size_t n) @safe nothrow pure { 67 E result = _array[n]; 68 if(n != _array.length) { 69 for ( ; n < _array.length - 1 ; n++) { 70 _array[n] = _array[n + 1]; 71 } 72 } 73 _array.length = _array.length - 1; 74 end--; 75 return result; 76 } 77 static if(!allowDuplicates) { 78 /** 79 * Removes the element which is equal with the given one if the template is set to not allow duplicates. 80 * Returns the removed element, or E.init if not found. 81 */ 82 E removeByElem(E a) @safe nothrow pure { 83 foreach_reverse(i, b; _array) { 84 if(binaryFun!equal(a, b)) return this.remove(i); 85 else if(binaryFun!cmp(a, b)) break; 86 } 87 return E.init; 88 } 89 /** 90 * Returns whether the set has the given element. 91 */ 92 bool has(E a) @nogc @safe nothrow pure { 93 foreach_reverse(i, b; _array) { 94 if(binaryFun!equal(a,b)) return true; 95 else if(binaryFun!cmp(a,b)) break; 96 } 97 return false; 98 } 99 /** 100 * Returns the index of the given element, or throws an ElementNotFoundException if not found. 101 */ 102 size_t which(E a) @safe pure { 103 foreach_reverse(i, b; _array) { 104 if(binaryFun!equal(a,b)) return i; 105 else if(binaryFun!cmp(a,b)) break; 106 } 107 throw new ElementNotFoundException("Element not found!"); 108 } 109 /** 110 * Returns a slice from the list, but the slicing is done by element. 111 * Search is done in a way that if either cmp or equals is true to an element, its position will be chosen 112 * for the slice. 113 */ 114 SortedList!(E, cmp, allowDuplicates, equal) sliceByElem(E from, E to) @safe pure { 115 size_t f, t = _array.length; 116 E a = from; 117 foreach_reverse(size_t i ,E b; _array) { 118 if(binaryFun!cmp(a,b) || binaryFun!equal(a,b)){ 119 f = i; 120 break; 121 } 122 } 123 a = to; 124 foreach_reverse(size_t i ,E b; _array) { 125 if(binaryFun!cmp(a,b) || binaryFun!equal(a,b)){ 126 t = i; 127 break; 128 } 129 } 130 return opSlice(f, t); 131 } 132 } 133 /** 134 * Returns a copy of this struct. 135 */ 136 @property auto save() @nogc @safe nothrow pure { 137 return this; 138 } 139 /** 140 * Returns the element at the front. 141 */ 142 @property E front() @nogc @safe nothrow pure { 143 return _array[begin]; 144 } 145 /+/** 146 * Returns the element at the front. 147 */ 148 @property ref E frontRef() @nogc @safe nothrow pure { 149 return _array[begin]; 150 }+/ 151 /** 152 * Returns the element at the back. 153 */ 154 @property E back() @nogc @safe nothrow pure { 155 return _array[end - 1]; 156 } 157 /+/** 158 * Returns the element at the back. 159 */ 160 @property ref E backRef() @nogc @safe nothrow pure { 161 return _array[end - 1]; 162 }+/ 163 /** 164 * Returns the element at begin and increments the position by one. 165 */ 166 E moveFront() @nogc @safe nothrow pure { 167 E result = _array[begin]; 168 popFront(); 169 return result; 170 } 171 /** 172 * Increments the front iteration position by one 173 */ 174 void popFront() @nogc @safe nothrow pure { 175 if(begin < end) begin++; 176 } 177 /** 178 * Decrements the back iteration position by one 179 */ 180 void popBack() @nogc @safe nothrow pure { 181 if(begin < end) end--; 182 } 183 /** 184 * Returns true when the end of the list have been reached. 185 */ 186 @property bool empty() @nogc @safe nothrow pure { 187 return begin == end; 188 } 189 /** 190 * Returns the n-th element. 191 */ 192 E opIndex(size_t n) @nogc @safe nothrow pure { 193 return _array[n]; 194 } 195 /** 196 * Moves to the n-th position and returns the element of that position. 197 */ 198 E moveAt(size_t n) @nogc @safe nothrow pure { 199 begin = n; 200 return _array[begin]; 201 } 202 /** 203 * Returns the length of the list. 204 */ 205 @property size_t length() @nogc @safe nothrow pure { 206 return _array.length; 207 } 208 alias opDollar = length; 209 /** 210 * Sets the reserve of the underlying array and returns the current reserve. 211 */ 212 @property size_t reserve(size_t amount) @safe pure nothrow { 213 return _array.reserve(amount); 214 } 215 /** 216 * Returns a slice from the list. 217 */ 218 SortedList!(E, cmp, allowDuplicates, equal) opSlice(size_t i0, size_t i1) @safe nothrow pure { 219 return SortedList!(E, cmp, allowDuplicates, equal)(_array[i0..i1]); 220 } 221 /** 222 * Returns a copy of the underlying array. 223 */ 224 @property E[] arrayOf() @safe nothrow pure const { 225 return _array.dup; 226 } 227 string toString() { 228 import std.conv : to; 229 return to!string(_array); 230 } 231 } 232 233 @safe unittest { 234 import std.stdio : writeln; 235 alias SortedIntList = SortedList!(int, "a < b", true); 236 alias SortedIntSet = SortedList!(int, "a < b", false); 237 SortedIntList sil; 238 sil.put(5); 239 assert(sil.arrayOf == [5], sil.toString); 240 sil.put(3); 241 assert(sil.arrayOf == [5, 3], sil.toString); 242 sil.put(7); 243 assert(sil.arrayOf == [7, 5, 3], sil.toString); 244 sil.put(-1); 245 assert(sil.arrayOf == [7, 5, 3, -1], sil.toString); 246 sil.remove(2); 247 assert(sil.arrayOf == [7, 5, -1], sil.toString); 248 sil.put(2); 249 assert(sil.arrayOf == [7 ,5, 2, -1], sil.toString); 250 SortedIntSet sis; 251 sis.put(5); 252 assert(sis.arrayOf == [5], sis.toString); 253 sis.put(5); 254 assert(sis.arrayOf == [5], sis.toString); 255 sis.put(3); 256 assert(sis.arrayOf == [5, 3], sis.toString); 257 sis.put(3); 258 assert(sis.arrayOf == [5, 3], sis.toString); 259 sis.put(7); 260 assert(sis.arrayOf == [7, 5, 3], sis.toString); 261 sis.put(7); 262 assert(sis.arrayOf == [7, 5, 3], sis.toString); 263 sis.put(-2); 264 assert(sis.arrayOf == [7, 5, 3, -2], sis.toString); 265 sis.put(11); 266 assert(sis.arrayOf == [11, 7, 5, 3, -2], sis.toString); 267 assert(!sis.has(12), sis.toString); 268 assert(sis.has(11), sis.toString); 269 sis.remove(0); 270 assert(!sis.has(11), sis.toString); 271 assert(sis.arrayOf == [7, 5, 3, -2], sis.toString); 272 sis.put(1); 273 assert(sis.arrayOf == [7, 5, 3, 1, -2], sis.toString); 274 sis.put(1); 275 assert(sis.arrayOf == [7, 5, 3, 1, -2], sis.toString); 276 foreach(e; sil) { 277 writeln(e); 278 } 279 foreach(e; sis) { 280 writeln(e); 281 } 282 } 283 284 @safe unittest { 285 import std.stdio : writeln; 286 alias SortedIntList = SortedList!(int, "a > b", true); 287 alias SortedIntSet = SortedList!(int, "a > b", false); 288 SortedIntList sil; 289 sil.put(5); 290 assert(sil.arrayOf == [5], sil.toString); 291 sil.put(3); 292 assert(sil.arrayOf == [3, 5], sil.toString); 293 sil.put(7); 294 assert(sil.arrayOf == [3, 5, 7], sil.toString); 295 sil.put(-1); 296 assert(sil.arrayOf == [-1, 3, 5, 7], sil.toString); 297 sil.remove(2); 298 assert(sil.arrayOf == [-1, 3, 7], sil.toString); 299 sil.put(2); 300 assert(sil.arrayOf == [-1,2, 3, 7], sil.toString); 301 SortedIntSet sis; 302 sis.put(5); 303 assert(sis.arrayOf == [5], sis.toString); 304 sis.put(5); 305 assert(sis.arrayOf == [5], sis.toString); 306 sis.put(3); 307 assert(sis.arrayOf == [3, 5], sis.toString); 308 sis.put(3); 309 assert(sis.arrayOf == [3, 5], sis.toString); 310 sis.put(7); 311 assert(sis.arrayOf == [3, 5, 7], sis.toString); 312 sis.put(7); 313 assert(sis.arrayOf == [3, 5, 7], sis.toString); 314 sis.put(-2); 315 assert(sis.arrayOf == [-2, 3, 5, 7], sis.toString); 316 sis.put(11); 317 assert(sis.arrayOf == [-2, 3, 5, 7, 11], sis.toString); 318 assert(!sis.has(12), sis.toString); 319 assert(sis.has(11), sis.toString); 320 sis.remove(0); 321 assert(!sis.has(-2), sis.toString); 322 assert(sis.arrayOf == [3, 5, 7, 11], sis.toString); 323 sis.put(1); 324 assert(sis.arrayOf == [1, 3, 5, 7, 11], sis.toString); 325 sis.put(1); 326 assert(sis.arrayOf == [1, 3, 5, 7, 11], sis.toString); 327 foreach(e; sil) { 328 writeln(e); 329 } 330 foreach(e; sis) { 331 writeln(e); 332 } 333 }