1 /******************************************************************************* 2 3 Based upon Doug Lea's Java collection package 4 5 Copyright: 6 Copyright (c) 2008 Kris Bell. 7 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 8 All rights reserved. 9 10 License: 11 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 12 See LICENSE_TANGO.txt for details. 13 14 Version: Apr 2008: Initial release 15 16 Authors: Kris 17 18 *******************************************************************************/ 19 20 module ocean.util.container.Slink; 21 22 import ocean.core.Enforce; 23 24 import ocean.meta.types.Qualifiers; 25 import ocean.meta.types.Typedef; 26 27 import ocean.util.container.model.IContainer; 28 29 /******************************************************************************* 30 31 Slink instances provide standard linked list next-fields, and 32 support standard operations upon them. Slink structures are pure 33 implementation tools, and perform no argument checking, no result 34 screening, and no synchronization. They rely on user-level classes 35 (see HashSet, for example) to do such things. 36 37 Still, Slink is made `public' so that you can use it to build other 38 kinds of containers 39 40 Note that when K is specified, support for keys are enabled. When 41 Identity is stipulated as 'true', those keys are compared using an 42 identity-comparison instead of equality (using 'is'). Similarly, if 43 HashCache is set true, an additional attribute is create in order to 44 retain the hash of K 45 46 *******************************************************************************/ 47 48 private 49 { 50 mixin(Typedef!(int, "KeyDummy")); 51 } 52 53 struct Slink (V, K=KeyDummy, bool Identity = false, bool HashCache = false) 54 { 55 alias Slink!(V, K, Identity, HashCache) Type; 56 alias Type *Ref; 57 alias Compare!(V) Comparator; 58 59 Ref next; // pointer to next 60 V value; // element value 61 62 static if (HashCache == true) 63 { 64 hash_t cache; // retain hash value? 65 } 66 67 /*********************************************************************** 68 69 add support for keys also? 70 71 ***********************************************************************/ 72 73 static if (!is(typeof(K) == KeyDummy)) 74 { 75 K key; 76 77 final Ref set (K k, V v, Ref n) 78 { 79 key = k; 80 return set (v, n); 81 } 82 83 final hash_t hash() 84 { 85 return typeid(K).getHash(&key); 86 } 87 88 final Ref findKey (K key) 89 { 90 static if (Identity == true) 91 { 92 for (auto p= &this; p; p = p.next) 93 if (key is p.key) 94 return p; 95 } 96 else 97 { 98 for (auto p= &this; p; p = p.next) 99 if (key == p.key) 100 return p; 101 } 102 return null; 103 } 104 105 final Ref findPair (K key, V value) 106 { 107 static if (Identity == true) 108 { 109 for (auto p= &this; p; p = p.next) 110 if (key is p.key && value == p.value) 111 return p; 112 } 113 else 114 { 115 for (auto p= &this; p; p = p.next) 116 if (key == p.key && value == p.value) 117 return p; 118 } 119 return null; 120 } 121 122 final int indexKey (K key) 123 { 124 int i = 0; 125 static if (Identity == true) 126 { 127 for (auto p= &this; p; p = p.next, ++i) 128 if (key is p.key) 129 return i; 130 } 131 else 132 { 133 for (auto p= &this; p; p = p.next, ++i) 134 if (key == p.key) 135 return i; 136 } 137 return -1; 138 } 139 140 final int indexPair (K key, V value) 141 { 142 int i = 0; 143 static if (Identity == true) 144 { 145 for (auto p= &this; p; p = p.next, ++i) 146 if (key is p.key && value == p.value) 147 return i; 148 } 149 else 150 { 151 for (auto p= &this; p; p = p.next, ++i) 152 if (key == p.key && value == p.value) 153 return i; 154 } 155 return -1; 156 } 157 158 final int countKey (K key) 159 { 160 int c = 0; 161 static if (Identity == true) 162 { 163 for (auto p= &this; p; p = p.next) 164 if (key is p.key) 165 ++c; 166 } 167 else 168 { 169 for (auto p= &this; p; p = p.next) 170 if (key == p.key) 171 ++c; 172 } 173 return c; 174 } 175 176 final int countPair (K key, V value) 177 { 178 int c = 0; 179 static if (Identity == true) 180 { 181 for (auto p= &this; p; p = p.next) 182 if (key is p.key && value == p.value) 183 ++c; 184 } 185 else 186 { 187 for (auto p= &this; p; p = p.next) 188 if (key == p.key && value == p.value) 189 ++c; 190 } 191 return c; 192 } 193 } 194 195 /*********************************************************************** 196 197 Set to point to n as next cell 198 199 param: n, the new next cell 200 201 ***********************************************************************/ 202 203 final Ref set (V v, Ref n) 204 { 205 next = n; 206 value = v; 207 return &this; 208 } 209 210 /*********************************************************************** 211 212 Splice in p between current cell and whatever it was 213 previously pointing to 214 215 param: p, the cell to splice 216 217 ***********************************************************************/ 218 219 final void attach (Ref p) 220 { 221 if (p) 222 p.next = next; 223 next = p; 224 } 225 226 /*********************************************************************** 227 228 Cause current cell to skip over the current next() one, 229 effectively removing the next element from the list 230 231 ***********************************************************************/ 232 233 final void detachNext() 234 { 235 if (next) 236 next = next.next; 237 } 238 239 /*********************************************************************** 240 241 Linear search down the list looking for element 242 243 param: element to look for 244 Returns: the cell containing element, or null if no such 245 246 ***********************************************************************/ 247 248 final Ref find (V element) 249 { 250 for (auto p = &this; p; p = p.next) 251 if (element == p.value) 252 return p; 253 return null; 254 } 255 256 /*********************************************************************** 257 258 Return the number of cells traversed to find first occurrence 259 of a cell with element() element, or -1 if not present 260 261 ***********************************************************************/ 262 263 final int index (V element) 264 { 265 int i; 266 for (auto p = &this; p; p = p.next, ++i) 267 if (element == p.value) 268 return i; 269 270 return -1; 271 } 272 273 /*********************************************************************** 274 275 Count the number of occurrences of element in list 276 277 ***********************************************************************/ 278 279 final int count (V element) 280 { 281 int c; 282 for (auto p = &this; p; p = p.next) 283 if (element == p.value) 284 ++c; 285 return c; 286 } 287 288 /*********************************************************************** 289 290 Return the number of cells in the list 291 292 ***********************************************************************/ 293 294 final int count () 295 { 296 int c; 297 for (auto p = &this; p; p = p.next) 298 ++c; 299 return c; 300 } 301 302 /*********************************************************************** 303 304 Return the cell representing the last element of the list 305 (i.e., the one whose next() is null 306 307 ***********************************************************************/ 308 309 final Ref tail () 310 { 311 auto p = &this; 312 while (p.next) 313 p = p.next; 314 return p; 315 } 316 317 /*********************************************************************** 318 319 Return the nth cell of the list, or null if no such 320 321 ***********************************************************************/ 322 323 final Ref nth (long n) 324 { 325 enforce(n >= 0); 326 327 auto p = &this; 328 for (long i; i < n && p; ++i) 329 p = p.next; 330 return p; 331 } 332 333 /*********************************************************************** 334 335 Make a copy of the list; i.e., a new list containing new cells 336 but including the same elements in the same order 337 338 ***********************************************************************/ 339 340 final Ref copy (scope Ref delegate() alloc) 341 { 342 auto newlist = dup (alloc); 343 auto current = newlist; 344 345 for (auto p = next; p; p = p.next) 346 { 347 current.next = p.dup (alloc); 348 current = current.next; 349 } 350 current.next = null; 351 return newlist; 352 } 353 354 /*********************************************************************** 355 356 dup is shallow; i.e., just makes a copy of the current cell 357 358 ***********************************************************************/ 359 360 private Ref dup (scope Ref delegate() alloc) 361 { 362 auto ret = alloc(); 363 static if (is(typeof(K) == KeyDummy)) 364 ret.set (value, next); 365 else 366 ret.set (key, value, next); 367 return ret; 368 } 369 370 /*********************************************************************** 371 372 Basic linkedlist merge algorithm. 373 Merges the lists head by fst and snd with respect to cmp 374 375 param: fst head of the first list 376 param: snd head of the second list 377 param: cmp a Comparator used to compare elements 378 Returns: the merged ordered list 379 380 ***********************************************************************/ 381 382 static Ref merge (Ref fst, Ref snd, Comparator cmp) 383 { 384 auto a = fst; 385 auto b = snd; 386 Ref hd = null; 387 Ref current = null; 388 389 for (;;) 390 { 391 if (a is null) 392 { 393 if (hd is null) 394 hd = b; 395 else 396 current.next = b; 397 return hd; 398 } 399 else 400 if (b is null) 401 { 402 if (hd is null) 403 hd = a; 404 else 405 current.next = a; 406 return hd; 407 } 408 409 int diff = cmp (a.value, b.value); 410 if (diff <= 0) 411 { 412 if (hd is null) 413 hd = a; 414 else 415 current.next = a; 416 current = a; 417 a = a.next; 418 } 419 else 420 { 421 if (hd is null) 422 hd = b; 423 else 424 current.next = b; 425 current = b; 426 b = b.next; 427 } 428 } 429 } 430 431 /*********************************************************************** 432 433 Standard list splitter, used by sort. 434 Splits the list in half. Returns the head of the second half 435 436 param: s the head of the list 437 Returns: the head of the second half 438 439 ***********************************************************************/ 440 441 static Ref split (Ref s) 442 { 443 auto fast = s; 444 auto slow = s; 445 446 if (fast is null || fast.next is null) 447 return null; 448 449 while (fast) 450 { 451 fast = fast.next; 452 if (fast && fast.next) 453 { 454 fast = fast.next; 455 slow = slow.next; 456 } 457 } 458 459 auto r = slow.next; 460 slow.next = null; 461 return r; 462 463 } 464 465 /*********************************************************************** 466 467 Standard merge sort algorithm 468 469 param: s the list to sort 470 param: cmp, the comparator to use for ordering 471 Returns: the head of the sorted list 472 473 ***********************************************************************/ 474 475 static Ref sort (Ref s, Comparator cmp) 476 { 477 if (s is null || s.next is null) 478 return s; 479 else 480 { 481 auto right = split (s); 482 auto left = s; 483 left = sort (left, cmp); 484 right = sort (right, cmp); 485 return merge (left, right, cmp); 486 } 487 } 488 489 } 490 491 version (unittest) 492 { 493 import ocean.core.Test : NamedTest; 494 } 495 496 unittest 497 { 498 auto t = new NamedTest("Test nth() with an index out of bounds"); 499 500 static immutable total_items = 11; 501 static immutable index_out_of_bounds = total_items * 2; 502 503 auto slink = new Slink!(int); 504 t.test!("==")(slink.nth(0).value, 0); 505 t.test!("==")(slink.count(), 1); 506 507 auto tail = slink; 508 for (int i = 1; i < total_items; ++i) 509 { 510 auto item = new Slink!(int); 511 item.value = tail.value + 1; 512 tail.set(tail.value, item); 513 tail = item; 514 } 515 516 // Self-verification 517 t.test!("==")(slink.count(), total_items); 518 for (int i = 0; i < total_items; ++i) 519 { 520 t.test!("==")(slink.nth(i).value, i); 521 } 522 523 t.test!("==")(slink.nth(total_items), null); 524 t.test!("==")(slink.nth(index_out_of_bounds), null); 525 }