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 */ 13 public struct SortedList(E, alias cmp = "a < b", bool allowDuplicates = true, alias equal = "a == b") { 14 private E[] _array; ///The array where the elements are stored. 15 private size_t begin; ///The position of the current front iteration. 16 private size_t end; ///The position of the current back iteration. 17 /** 18 * Constructs a sorted list out from an array. 19 */ 20 public this(E[] _array) @safe nothrow pure { 21 this._array.reserve = _array.length; 22 foreach (E key; _array) { 23 put(key); 24 } 25 assert(this._array.length == _array.length); 26 assert(this._array.length == end); 27 } 28 /** 29 * Constructs a sorted list out from a range. 30 */ 31 public this(Range)(Range src) { 32 foreach (E key; src) { 33 put(key); 34 } 35 //assert(this._array.length == _array.length); 36 assert(this._array.length == end); 37 } 38 /** 39 * Adds a new element while keeping the order intact. 40 * 41 * TO DO: Maybe even more optimizations? Maybe look where the element would fit, then shift that position? 42 */ 43 E put(E a) @safe nothrow pure { 44 static if(!allowDuplicates){ 45 if(has(a)) return E.init; 46 } 47 _array.length = _array.length + 1; 48 for(sizediff_t i = _array.length - 2 ; i >= 0 ; i--) { 49 E b = _array[i]; 50 if(binaryFun!cmp(a, b)) { //first position found 51 _array[i + 1] = a; 52 end++; 53 return a; 54 } else { 55 _array[i + 1] = b; 56 } 57 } 58 _array[0] = a; 59 end++; 60 return a; 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 amount of elements found in the set. 101 */ 102 size_t hasRange(R)(R range) @nogc @safe pure nothrow { 103 size_t result; 104 foreach (key; range) { 105 if(has(key)) result++; 106 } 107 return result; 108 } 109 /** 110 * Returns the index of the given element, or throws an ElementNotFoundException if not found. 111 */ 112 size_t which(E a) @safe pure { 113 foreach_reverse(i, b; _array) { 114 if(binaryFun!equal(a,b)) return i; 115 else if(binaryFun!cmp(a,b)) break; 116 } 117 throw new ElementNotFoundException("Element not found!"); 118 } 119 /** 120 * Returns a slice from the list, but the slicing is done by element. 121 * Search is done in a way that if either cmp or equals is true to an element, its position will be chosen 122 * for the slice. 123 */ 124 SortedList!(E, cmp, allowDuplicates, equal) sliceByElem(E from, E to) @safe pure { 125 size_t f, t = _array.length; 126 E a = from; 127 foreach_reverse(size_t i ,E b; _array) { 128 if(binaryFun!cmp(a,b) || binaryFun!equal(a,b)){ 129 f = i; 130 break; 131 } 132 } 133 a = to; 134 foreach_reverse(size_t i ,E b; _array) { 135 if(binaryFun!cmp(a,b) || binaryFun!equal(a,b)){ 136 t = i; 137 break; 138 } 139 } 140 return opSlice(f, t); 141 } 142 /** 143 * Set operators. 144 * Enables math operations on sets, like unions and intersections. 145 * Could work on ranges in general as long as they implement some basic functions, like iteration. 146 */ 147 SortedList!(E, cmp, allowDuplicates, equal) opBinary(string op, R)(R rhs) { 148 static if(op == "|" || op == "~") {//Union 149 SortedList!(E, cmp, allowDuplicates, equal) result; 150 result.end = this.end; 151 result._array = this._array.dup; 152 foreach(e ; rhs){ 153 result.put(e); 154 } 155 return result; 156 } else static if(op == "&" || op == "*") {//Intersection 157 SortedList!(E, cmp, allowDuplicates, equal) result; 158 foreach(e ; rhs){ 159 if(this.has(e)) result.put(e); 160 } 161 return result; 162 } else static if(op == "-" || op == "/") {//Complement 163 SortedList!(E, cmp, allowDuplicates, equal) result = SortedList!(E, cmp, allowDuplicates, equal)(this); 164 foreach(e ; rhs){ 165 result.removeByElem(e); 166 } 167 return result; 168 } else static if(op == "^"){//Difference 169 SortedList!(E, cmp, allowDuplicates, equal) result = this | rhs; 170 SortedList!(E, cmp, allowDuplicates, equal) common = this & rhs; 171 foreach(e ; common){ 172 result.removeByElem(e); 173 } 174 return result; 175 } else static assert(0, "Operator " ~ op ~ "not supported"); 176 } 177 /** 178 * Set operators. 179 */ 180 SortedList!(E, cmp, allowDuplicates, equal) opOpAssign(string op)(E value) { 181 static if(op == "~=") {//Append 182 put(value); 183 } else static if(op == "-=" || op == "/=") { 184 removeByElem(value); 185 } else static assert(0, "Operator " ~ op ~ "not supported"); 186 return this; 187 } 188 /** 189 * Set operators. 190 */ 191 SortedList!(E, cmp, allowDuplicates, equal) opOpAssign(string op, R)(R range) { 192 static if(op == "~=" || op == "|=") {//Append 193 foreach(val; range) 194 put(val); 195 } else static if(op == "-=" || op == "/=") { 196 foreach(val; range) 197 removeByElem(val); 198 } else static assert(0, "Operator " ~ op ~ "not supported"); 199 return this; 200 } 201 } 202 /** 203 * Returns a copy of this struct. 204 */ 205 @property auto save() @nogc @safe nothrow pure { 206 return this; 207 } 208 /** 209 * Returns the element at the front. 210 */ 211 @property E front() @nogc @safe nothrow pure { 212 return _array[begin]; 213 } 214 /+/** 215 * Returns the element at the front. 216 */ 217 @property ref E frontRef() @nogc @safe nothrow pure { 218 return _array[begin]; 219 }+/ 220 /** 221 * Returns the element at the back. 222 */ 223 @property E back() @nogc @safe nothrow pure { 224 return _array[end - 1]; 225 } 226 /+/** 227 * Returns the element at the back. 228 */ 229 @property ref E backRef() @nogc @safe nothrow pure { 230 return _array[end - 1]; 231 }+/ 232 /** 233 * Returns the element at begin and increments the position by one. 234 */ 235 E moveFront() @nogc @safe nothrow pure { 236 E result = _array[begin]; 237 popFront(); 238 return result; 239 } 240 /** 241 * Increments the front iteration position by one 242 */ 243 void popFront() @nogc @safe nothrow pure { 244 if(begin < end) begin++; 245 } 246 /** 247 * Decrements the back iteration position by one 248 */ 249 void popBack() @nogc @safe nothrow pure { 250 if(begin < end) end--; 251 } 252 /** 253 * Returns true when the end of the list have been reached. 254 */ 255 @property bool empty() @nogc @safe nothrow pure { 256 return begin == end; 257 } 258 /** 259 * Returns the n-th element. 260 */ 261 E opIndex(size_t n) @nogc @safe nothrow pure { 262 return _array[n]; 263 } 264 /** 265 * Moves to the n-th position and returns the element of that position. 266 */ 267 E moveAt(size_t n) @nogc @safe nothrow pure { 268 begin = n; 269 return _array[begin]; 270 } 271 /** 272 * Returns the length of the list. 273 */ 274 @property size_t length() @nogc @safe nothrow pure { 275 return _array.length; 276 } 277 alias opDollar = length; 278 /** 279 * Sets the reserve of the underlying array and returns the current reserve. 280 */ 281 @property size_t reserve(size_t amount) @safe pure nothrow { 282 return _array.reserve(amount); 283 } 284 /** 285 * Returns a slice from the list. 286 */ 287 SortedList!(E, cmp, allowDuplicates, equal) opSlice(size_t i0, size_t i1) @safe nothrow pure { 288 return SortedList!(E, cmp, allowDuplicates, equal)(_array[i0..i1]); 289 } 290 /** 291 * Returns a copy of the underlying array. 292 */ 293 @property E[] arrayOf() @safe nothrow pure const { 294 return _array.dup; 295 } 296 string toString() { 297 import std.conv : to; 298 return to!string(_array); 299 } 300 } 301 302 @safe unittest { 303 import std.stdio : writeln; 304 alias SortedIntList = SortedList!(int, "a < b", true); 305 alias SortedIntSet = SortedList!(int, "a < b", false); 306 SortedIntList sil; 307 sil.put(5); 308 assert(sil.arrayOf == [5], sil.toString); 309 sil.put(3); 310 assert(sil.arrayOf == [5, 3], sil.toString); 311 sil.put(7); 312 assert(sil.arrayOf == [7, 5, 3], sil.toString); 313 sil.put(-1); 314 assert(sil.arrayOf == [7, 5, 3, -1], sil.toString); 315 sil.remove(2); 316 assert(sil.arrayOf == [7, 5, -1], sil.toString); 317 sil.put(2); 318 assert(sil.arrayOf == [7 ,5, 2, -1], sil.toString); 319 SortedIntSet sis; 320 sis.put(5); 321 assert(sis.arrayOf == [5], sis.toString); 322 sis.put(5); 323 assert(sis.arrayOf == [5], sis.toString); 324 sis.put(3); 325 assert(sis.arrayOf == [5, 3], sis.toString); 326 sis.put(3); 327 assert(sis.arrayOf == [5, 3], sis.toString); 328 sis.put(7); 329 assert(sis.arrayOf == [7, 5, 3], sis.toString); 330 sis.put(7); 331 assert(sis.arrayOf == [7, 5, 3], sis.toString); 332 sis.put(-2); 333 assert(sis.arrayOf == [7, 5, 3, -2], sis.toString); 334 sis.put(11); 335 assert(sis.arrayOf == [11, 7, 5, 3, -2], sis.toString); 336 assert(!sis.has(12), sis.toString); 337 assert(sis.has(11), sis.toString); 338 sis.remove(0); 339 assert(!sis.has(11), sis.toString); 340 assert(sis.arrayOf == [7, 5, 3, -2], sis.toString); 341 sis.put(1); 342 assert(sis.arrayOf == [7, 5, 3, 1, -2], sis.toString); 343 sis.put(1); 344 assert(sis.arrayOf == [7, 5, 3, 1, -2], sis.toString); 345 foreach(e; sil) { 346 writeln(e); 347 } 348 foreach(e; sis) { 349 writeln(e); 350 } 351 } 352 353 @safe unittest { 354 import std.stdio : writeln; 355 alias SortedIntList = SortedList!(int, "a > b", true); 356 alias SortedIntSet = SortedList!(int, "a > b", false); 357 SortedIntList sil; 358 sil.put(5); 359 assert(sil.arrayOf == [5], sil.toString); 360 sil.put(3); 361 assert(sil.arrayOf == [3, 5], sil.toString); 362 sil.put(7); 363 assert(sil.arrayOf == [3, 5, 7], sil.toString); 364 sil.put(-1); 365 assert(sil.arrayOf == [-1, 3, 5, 7], sil.toString); 366 sil.remove(2); 367 assert(sil.arrayOf == [-1, 3, 7], sil.toString); 368 sil.put(2); 369 assert(sil.arrayOf == [-1,2, 3, 7], sil.toString); 370 SortedIntSet sis; 371 sis.put(5); 372 assert(sis.arrayOf == [5], sis.toString); 373 sis.put(5); 374 assert(sis.arrayOf == [5], sis.toString); 375 sis.put(3); 376 assert(sis.arrayOf == [3, 5], sis.toString); 377 sis.put(3); 378 assert(sis.arrayOf == [3, 5], sis.toString); 379 sis.put(7); 380 assert(sis.arrayOf == [3, 5, 7], sis.toString); 381 sis.put(7); 382 assert(sis.arrayOf == [3, 5, 7], sis.toString); 383 sis.put(-2); 384 assert(sis.arrayOf == [-2, 3, 5, 7], sis.toString); 385 sis.put(11); 386 assert(sis.arrayOf == [-2, 3, 5, 7, 11], sis.toString); 387 assert(!sis.has(12), sis.toString); 388 assert(sis.has(11), sis.toString); 389 sis.remove(0); 390 assert(!sis.has(-2), sis.toString); 391 assert(sis.arrayOf == [3, 5, 7, 11], sis.toString); 392 sis.put(1); 393 assert(sis.arrayOf == [1, 3, 5, 7, 11], sis.toString); 394 sis.put(1); 395 assert(sis.arrayOf == [1, 3, 5, 7, 11], sis.toString); 396 foreach(e; sil) { 397 writeln(e); 398 } 399 foreach(e; sis) { 400 writeln(e); 401 } 402 } 403 404 unittest { 405 //test set operators 406 alias IntSet = SortedList!(int, "a > b", false, "a == b"); 407 IntSet a = IntSet([1, 3, 5, 7, 9]), b = IntSet([1, 5, 9]), c = IntSet([3, 7]); 408 IntSet union_ab = a | b, union_ac = a | c, union_bc = b | c; 409 IntSet intrsctn_ab = a & b, intrsctn_ac = a & c; 410 IntSet cmplmnt_ab = a - b, cmplmnt_ac = a - c; 411 IntSet diff_ab = a ^ b, diff_ac = a ^ c, diff_bc = b ^ c; 412 assert(union_ab.hasRange([1, 3, 5, 7, 9]) == 5); 413 assert(union_ac.hasRange([1, 3, 5, 7, 9]) == 5); 414 assert(union_bc.hasRange([1, 3, 5, 7, 9]) == 5); 415 assert(intrsctn_ab.hasRange([1, 5, 9]) == 3); 416 assert(intrsctn_ac.hasRange([3, 7]) == 2); 417 assert(cmplmnt_ab.hasRange([3, 7]) == 2); 418 assert(cmplmnt_ac.hasRange([1, 5, 9]) == 3); 419 assert(diff_ab.hasRange([3, 7]) == 2); 420 assert(diff_ac.hasRange([1, 5, 9]) == 3); 421 assert(diff_bc.hasRange([1, 3, 5, 7, 9]) == 5); 422 }