1 /** 2 This module implements a generic doubly-linked list container. 3 It can be used as a queue, dequeue or stack. 4 5 This module is a submodule of $(MREF std, container). 6 7 Source: $(PHOBOSSRC std/container/dlist.d) 8 9 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. 10 11 License: Distributed under the Boost Software License, Version 1.0. 12 (See accompanying file LICENSE_1_0.txt or copy at $(HTTP 13 boost.org/LICENSE_1_0.txt)). 14 15 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 16 17 $(SCRIPT inhibitQuickIndex = 1;) 18 */ 19 module std.container.dlist; 20 21 /// 22 @safe unittest 23 { 24 import std.algorithm.comparison : equal; 25 import std.container : DList; 26 27 auto s = DList!int(1, 2, 3); 28 assert(equal(s[], [1, 2, 3])); 29 30 s.removeFront(); 31 assert(equal(s[], [2, 3])); 32 s.removeBack(); 33 assert(equal(s[], [2])); 34 35 s.insertFront([4, 5]); 36 assert(equal(s[], [4, 5, 2])); 37 s.insertBack([6, 7]); 38 assert(equal(s[], [4, 5, 2, 6, 7])); 39 40 // If you want to apply range operations, simply slice it. 41 import std.algorithm.searching : countUntil; 42 import std.range : popFrontN, popBackN, walkLength; 43 44 auto sl = DList!int([1, 2, 3, 4, 5]); 45 assert(countUntil(sl[], 2) == 1); 46 47 auto r = sl[]; 48 popFrontN(r, 2); 49 popBackN(r, 2); 50 assert(r.equal([3])); 51 assert(walkLength(r) == 1); 52 53 // DList.Range can be used to remove elements from the list it spans 54 auto nl = DList!int([1, 2, 3, 4, 5]); 55 for (auto rn = nl[]; !rn.empty;) 56 if (rn.front % 2 == 0) 57 nl.popFirstOf(rn); 58 else 59 rn.popFront(); 60 assert(equal(nl[], [1, 3, 5])); 61 auto rs = nl[]; 62 rs.popFront(); 63 nl.remove(rs); 64 assert(equal(nl[], [1])); 65 } 66 67 import std.range.primitives; 68 import std.traits; 69 70 public import std.container.util; 71 72 /+ 73 A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode"). 74 75 Also used for parts of the code that don't depend on the payload type. 76 +/ 77 private struct BaseNode 78 { 79 private BaseNode* _prev = null; 80 private BaseNode* _next = null; 81 82 /+ 83 Gets the payload associated with this node. 84 This is trusted because all nodes are associated with a payload, even 85 the sentinel node. 86 It is also not possible to mix Nodes in DLists of different types. 87 This function is implemented as a member function here, as UFCS does not 88 work with pointers. 89 +/ inoutBaseNode90 ref inout(T) getPayload(T)() inout @trusted 91 { 92 return (cast(inout(DList!T.PayNode)*)&this)._payload; 93 } 94 95 // Helper: Given nodes p and n, connects them. 96 static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure 97 { 98 p._next = n; 99 n._prev = p; 100 } 101 } 102 103 /+ 104 The base DList Range. Contains Range primitives that don't depend on payload type. 105 +/ 106 private struct DRange 107 { 108 @safe unittest 109 { 110 static assert(isBidirectionalRange!DRange); 111 static assert(is(ElementType!DRange == BaseNode*)); 112 } 113 114 nothrow @safe pure: 115 private BaseNode* _first; 116 private BaseNode* _last; 117 118 private this(BaseNode* first, BaseNode* last) 119 { 120 assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments"); 121 _first = first; 122 _last = last; 123 } 124 private this(BaseNode* n) 125 { 126 this(n, n); 127 } 128 129 @property 130 bool empty() const scope 131 { 132 assert((_first is null) == (_last is null), "DList.Range: Invalidated state"); 133 return !_first; 134 } 135 136 @property BaseNode* front() return scope 137 { 138 assert(!empty, "DList.Range.front: Range is empty"); 139 return _first; 140 } 141 142 void popFront() scope 143 { 144 assert(!empty, "DList.Range.popFront: Range is empty"); 145 if (_first is _last) 146 { 147 _first = _last = null; 148 } 149 else 150 { 151 assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state"); 152 _first = _first._next; 153 } 154 } 155 156 @property BaseNode* back() return scope 157 { 158 assert(!empty, "DList.Range.front: Range is empty"); 159 return _last; 160 } 161 162 void popBack() scope 163 { 164 assert(!empty, "DList.Range.popBack: Range is empty"); 165 if (_first is _last) 166 { 167 _first = _last = null; 168 } 169 else 170 { 171 assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state"); 172 _last = _last._prev; 173 } 174 } 175 176 /// Forward range primitive. 177 @property DRange save() return scope { return this; } 178 } 179 180 /** 181 Implements a doubly-linked list. 182 183 `DList` uses reference semantics. 184 */ 185 struct DList(T) 186 { 187 import std.range : Take; 188 189 /* 190 A Node with a Payload. A PayNode. 191 */ 192 struct PayNode 193 { 194 BaseNode _base; 195 alias _base this; 196 197 T _payload = T.init; 198 199 this (BaseNode _base, T _payload) 200 { 201 this._base = _base; 202 this._payload = _payload; 203 } 204 205 inout(BaseNode)* asBaseNode() inout @trusted 206 { 207 return &_base; 208 } 209 } 210 211 //The sentinel node 212 private BaseNode* _root; 213 214 private 215 { 216 //Construct as new PayNode, and returns it as a BaseNode. 217 static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null) 218 { 219 return (new PayNode(BaseNode(prev, next), arg)).asBaseNode(); 220 } 221 222 void initialize() nothrow @safe pure 223 { 224 if (_root) return; 225 //Note: We allocate a PayNode for safety reasons. 226 _root = (new PayNode()).asBaseNode(); 227 _root._next = _root._prev = _root; 228 } 229 ref inout(BaseNode*) _first() @property @safe nothrow pure inout 230 { 231 assert(_root, "Root pointer must not be null"); 232 return _root._next; 233 } 234 ref inout(BaseNode*) _last() @property @safe nothrow pure inout 235 { 236 assert(_root, "Root pointer must not be null"); 237 return _root._prev; 238 } 239 } //end private 240 241 /** 242 Constructor taking a number of nodes 243 */ 244 this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) 245 { 246 insertBack(values); 247 } 248 249 /** 250 Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 251 */ 252 this(Stuff)(Stuff stuff) 253 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 254 { 255 insertBack(stuff); 256 } 257 258 /** 259 Comparison for equality. 260 261 Complexity: $(BIGOH min(n, n1)) where `n1` is the number of 262 elements in `rhs`. 263 */ 264 bool opEquals()(ref const DList rhs) const 265 if (is(typeof(front == front))) 266 { 267 const lhs = this; 268 const lroot = lhs._root; 269 const rroot = rhs._root; 270 271 if (lroot is rroot) return true; 272 if (lroot is null) return rroot is rroot._next; 273 if (rroot is null) return lroot is lroot._next; 274 275 const(BaseNode)* pl = lhs._first; 276 const(BaseNode)* pr = rhs._first; 277 while (true) 278 { 279 if (pl is lroot) return pr is rroot; 280 if (pr is rroot) return false; 281 282 // !== because of NaN 283 if (!(pl.getPayload!T() == pr.getPayload!T())) return false; 284 285 pl = pl._next; 286 pr = pr._next; 287 } 288 } 289 290 /** 291 Defines the container's primary range, which embodies a bidirectional range. 292 */ 293 struct Range 294 { 295 static assert(isBidirectionalRange!Range); 296 297 DRange _base; 298 alias _base this; 299 300 private this(BaseNode* first, BaseNode* last) 301 { 302 _base = DRange(first, last); 303 } 304 private this(BaseNode* n) 305 { 306 this(n, n); 307 } 308 309 @property ref T front() 310 { 311 return _base.front.getPayload!T(); 312 } 313 314 @property ref T back() 315 { 316 return _base.back.getPayload!T(); 317 } 318 319 //Note: shadows base DRange.save. 320 //Necessary for static covariance. 321 @property Range save() { return this; } 322 } 323 324 /** 325 Property returning `true` if and only if the container has no 326 elements. 327 328 Complexity: $(BIGOH 1) 329 */ 330 bool empty() @property const nothrow 331 { 332 return _root is null || _root is _first; 333 } 334 335 /** 336 Removes all contents from the `DList`. 337 338 Postcondition: `empty` 339 340 Complexity: $(BIGOH 1) 341 */ 342 void clear() 343 { 344 //remove actual elements. 345 remove(this[]); 346 } 347 348 /** 349 Duplicates the container. The elements themselves are not transitively 350 duplicated. 351 352 Complexity: $(BIGOH n). 353 */ 354 @property DList dup() 355 { 356 return DList(this[]); 357 } 358 359 /** 360 Returns a range that iterates over all elements of the container, in 361 forward order. 362 363 Complexity: $(BIGOH 1) 364 */ 365 Range opSlice() 366 { 367 if (empty) 368 return Range(null, null); 369 else 370 return Range(_first, _last); 371 } 372 373 /** 374 Forward to `opSlice().front`. 375 376 Complexity: $(BIGOH 1) 377 */ 378 @property ref inout(T) front() inout 379 { 380 assert(!empty, "DList.front: List is empty"); 381 return _first.getPayload!T(); 382 } 383 384 /** 385 Forward to `opSlice().back`. 386 387 Complexity: $(BIGOH 1) 388 */ 389 @property ref inout(T) back() inout 390 { 391 assert(!empty, "DList.back: List is empty"); 392 return _last.getPayload!T(); 393 } 394 395 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 396 /+ BEGIN CONCAT FUNCTIONS HERE +/ 397 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 398 399 /** 400 Returns a new `DList` that's the concatenation of `this` and its 401 argument `rhs`. 402 */ 403 DList opBinary(string op, Stuff)(Stuff rhs) 404 if (op == "~" && is(typeof(insertBack(rhs)))) 405 { 406 auto ret = this.dup; 407 ret.insertBack(rhs); 408 return ret; 409 } 410 411 /** 412 Returns a new `DList` that's the concatenation of the argument `lhs` 413 and `this`. 414 */ 415 DList opBinaryRight(string op, Stuff)(Stuff lhs) 416 if (op == "~" && is(typeof(insertFront(lhs)))) 417 { 418 auto ret = this.dup; 419 ret.insertFront(lhs); 420 return ret; 421 } 422 423 /** 424 Appends the contents of the argument `rhs` into `this`. 425 */ 426 DList opOpAssign(string op, Stuff)(Stuff rhs) 427 if (op == "~" && is(typeof(insertBack(rhs)))) 428 { 429 insertBack(rhs); 430 return this; 431 } 432 433 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 434 /+ BEGIN INSERT FUNCTIONS HERE +/ 435 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 436 437 /** 438 Inserts `stuff` to the front/back of the container. `stuff` can be a 439 value convertible to `T` or a range of objects convertible to $(D 440 T). The stable version behaves the same, but guarantees that ranges 441 iterating over the container are never invalidated. 442 443 Returns: The number of elements inserted 444 445 Complexity: $(BIGOH log(n)) 446 */ 447 size_t insertFront(Stuff)(Stuff stuff) 448 { 449 initialize(); 450 return insertAfterNode(_root, stuff); 451 } 452 453 /// ditto 454 size_t insertBack(Stuff)(Stuff stuff) 455 { 456 initialize(); 457 return insertBeforeNode(_root, stuff); 458 } 459 460 /// ditto 461 alias insert = insertBack; 462 463 /// ditto 464 alias stableInsert = insert; 465 466 /// ditto 467 alias stableInsertFront = insertFront; 468 469 /// ditto 470 alias stableInsertBack = insertBack; 471 472 /** 473 Inserts `stuff` after range `r`, which must be a non-empty range 474 previously extracted from this container. 475 476 `stuff` can be a value convertible to `T` or a range of objects 477 convertible to `T`. The stable version behaves the same, but 478 guarantees that ranges iterating over the container are never 479 invalidated. 480 481 Returns: The number of values inserted. 482 483 Complexity: $(BIGOH k + m), where `k` is the number of elements in 484 `r` and `m` is the length of `stuff`. 485 */ 486 size_t insertBefore(Stuff)(Range r, Stuff stuff) 487 { 488 if (r._first) 489 return insertBeforeNode(r._first, stuff); 490 else 491 { 492 initialize(); 493 return insertAfterNode(_root, stuff); 494 } 495 } 496 497 /// ditto 498 alias stableInsertBefore = insertBefore; 499 500 /// ditto 501 size_t insertAfter(Stuff)(Range r, Stuff stuff) 502 { 503 if (r._last) 504 return insertAfterNode(r._last, stuff); 505 else 506 { 507 initialize(); 508 return insertBeforeNode(_root, stuff); 509 } 510 } 511 512 /// ditto 513 alias stableInsertAfter = insertAfter; 514 515 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 516 /+ BEGIN REMOVE FUNCTIONS HERE +/ 517 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ 518 519 /** 520 Picks one value in an unspecified position in the container, removes 521 it from the container, and returns it. The stable version behaves the same, 522 but guarantees that ranges iterating over the container are never invalidated. 523 524 Precondition: `!empty` 525 526 Returns: The element removed. 527 528 Complexity: $(BIGOH 1). 529 */ 530 T removeAny() 531 { 532 import std.algorithm.mutation : move; 533 534 assert(!empty, "DList.removeAny: List is empty"); 535 auto result = move(back); 536 removeBack(); 537 return result; 538 } 539 /// ditto 540 alias stableRemoveAny = removeAny; 541 542 /** 543 Removes the value at the front/back of the container. The stable version 544 behaves the same, but guarantees that ranges iterating over the 545 container are never invalidated. 546 547 Precondition: `!empty` 548 549 Complexity: $(BIGOH 1). 550 */ 551 void removeFront() 552 { 553 assert(!empty, "DList.removeFront: List is empty"); 554 assert(_root is _first._prev, "DList: Inconsistent state"); 555 BaseNode.connect(_root, _first._next); 556 } 557 558 /// ditto 559 alias stableRemoveFront = removeFront; 560 561 /// ditto 562 void removeBack() 563 { 564 assert(!empty, "DList.removeBack: List is empty"); 565 assert(_last._next is _root, "DList: Inconsistent state"); 566 BaseNode.connect(_last._prev, _root); 567 } 568 569 /// ditto 570 alias stableRemoveBack = removeBack; 571 572 /** 573 Removes `howMany` values at the front or back of the 574 container. Unlike the unparameterized versions above, these functions 575 do not throw if they could not remove `howMany` elements. Instead, 576 if $(D howMany > n), all elements are removed. The returned value is 577 the effective number of elements removed. The stable version behaves 578 the same, but guarantees that ranges iterating over the container are 579 never invalidated. 580 581 Returns: The number of elements removed 582 583 Complexity: $(BIGOH howMany). 584 */ 585 size_t removeFront(size_t howMany) 586 { 587 if (!_root) return 0; 588 size_t result; 589 auto p = _first; 590 while (p !is _root && result < howMany) 591 { 592 p = p._next; 593 ++result; 594 } 595 BaseNode.connect(_root, p); 596 return result; 597 } 598 599 /// ditto 600 alias stableRemoveFront = removeFront; 601 602 /// ditto 603 size_t removeBack(size_t howMany) 604 { 605 if (!_root) return 0; 606 size_t result; 607 auto p = _last; 608 while (p !is _root && result < howMany) 609 { 610 p = p._prev; 611 ++result; 612 } 613 BaseNode.connect(p, _root); 614 return result; 615 } 616 617 /// ditto 618 alias stableRemoveBack = removeBack; 619 620 /** 621 Removes all elements belonging to `r`, which must be a range 622 obtained originally from this container. 623 624 Returns: A range spanning the remaining elements in the container that 625 initially were right after `r`. 626 627 Complexity: $(BIGOH 1) 628 */ 629 Range remove(Range r) 630 { 631 if (r.empty) 632 return r; 633 634 assert(_root !is null, "Cannot remove from an un-initialized List"); 635 assert(r._first, "Remove: Range is empty"); 636 637 BaseNode.connect(r._first._prev, r._last._next); 638 auto after = r._last._next; 639 if (after is _root) 640 return Range(null, null); 641 else 642 return Range(after, _last); 643 } 644 645 /// ditto 646 Range linearRemove(Range r) 647 { 648 return remove(r); 649 } 650 651 /// ditto 652 alias stableRemove = remove; 653 654 /** 655 Removes first element of `r`, wich must be a range obtained originally 656 from this container, from both DList instance and range `r`. 657 658 Compexity: $(BIGOH 1) 659 */ 660 void popFirstOf(ref Range r) 661 { 662 assert(_root !is null, "Cannot remove from an un-initialized List"); 663 assert(r._first, "popFirstOf: Range is empty"); 664 auto prev = r._first._prev; 665 auto next = r._first._next; 666 r.popFront(); 667 BaseNode.connect(prev, next); 668 } 669 670 /** 671 Removes last element of `r`, wich must be a range obtained originally 672 from this container, from both DList instance and range `r`. 673 674 Compexity: $(BIGOH 1) 675 */ 676 void popLastOf(ref Range r) 677 { 678 assert(_root !is null, "Cannot remove from an un-initialized List"); 679 assert(r._first, "popLastOf: Range is empty"); 680 auto prev = r._last._prev; 681 auto next = r._last._next; 682 r.popBack(); 683 BaseNode.connect(prev, next); 684 } 685 686 /** 687 `linearRemove` functions as `remove`, but also accepts ranges that are 688 result the of a `take` operation. This is a convenient way to remove a 689 fixed amount of elements from the range. 690 691 Complexity: $(BIGOH r.walkLength) 692 */ 693 Range linearRemove(Take!Range r) 694 { 695 assert(_root !is null, "Cannot remove from an un-initialized List"); 696 assert(r.source._first, "Remove: Range is empty"); 697 698 BaseNode* first = r.source._first; 699 BaseNode* last = null; 700 do 701 { 702 last = r.source._first; 703 r.popFront(); 704 } while ( !r.empty ); 705 706 return remove(Range(first, last)); 707 } 708 709 /// ditto 710 alias stableLinearRemove = linearRemove; 711 712 /** 713 Removes the first occurence of an element from the list in linear time. 714 715 Returns: True if the element existed and was successfully removed, false otherwise. 716 717 Params: 718 value = value of the node to be removed 719 720 Complexity: $(BIGOH n) 721 */ 722 bool linearRemoveElement(T value) 723 { 724 auto n1 = findNodeByValue(_root, value); 725 if (n1) 726 { 727 auto n2 = n1._next._next; 728 BaseNode.connect(n1, n2); 729 return true; 730 } 731 732 return false; 733 } 734 735 736 private: 737 738 BaseNode* findNodeByValue(BaseNode* n, T value) 739 { 740 if (!n) return null; 741 auto ahead = n._next; 742 while (ahead && ahead.getPayload!T() != value) 743 { 744 n = ahead; 745 ahead = n._next; 746 if (ahead == _last._next) return null; 747 } 748 return n; 749 } 750 751 // Helper: Inserts stuff before the node n. 752 size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff) 753 if (isImplicitlyConvertible!(Stuff, T)) 754 { 755 auto p = createNode(stuff, n._prev, n); 756 n._prev._next = p; 757 n._prev = p; 758 return 1; 759 } 760 // ditto 761 size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff) 762 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 763 { 764 if (stuff.empty) return 0; 765 size_t result; 766 Range r = createRange(stuff, result); 767 BaseNode.connect(n._prev, r._first); 768 BaseNode.connect(r._last, n); 769 return result; 770 } 771 772 // Helper: Inserts stuff after the node n. 773 size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff) 774 if (isImplicitlyConvertible!(Stuff, T)) 775 { 776 auto p = createNode(stuff, n, n._next); 777 n._next._prev = p; 778 n._next = p; 779 return 1; 780 } 781 // ditto 782 size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff) 783 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 784 { 785 if (stuff.empty) return 0; 786 size_t result; 787 Range r = createRange(stuff, result); 788 BaseNode.connect(r._last, n._next); 789 BaseNode.connect(n, r._first); 790 return result; 791 } 792 793 // Helper: Creates a chain of nodes from the range stuff. 794 Range createRange(Stuff)(ref Stuff stuff, ref size_t result) 795 { 796 BaseNode* first = createNode(stuff.front); 797 BaseNode* last = first; 798 ++result; 799 for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() ) 800 { 801 auto p = createNode(stuff.front, last); 802 last = last._next = p; 803 ++result; 804 } 805 return Range(first, last); 806 } 807 } 808 809 @safe unittest 810 { 811 import std.algorithm.comparison : equal; 812 813 auto e = DList!int(); 814 auto b = e.linearRemoveElement(1); 815 assert(b == false); 816 assert(e.empty()); 817 auto a = DList!int(-1, 1, 2, 1, 3, 4); 818 b = a.linearRemoveElement(1); 819 assert(equal(a[], [-1, 2, 1, 3, 4])); 820 assert(b == true); 821 b = a.linearRemoveElement(-1); 822 assert(b == true); 823 assert(equal(a[], [2, 1, 3, 4])); 824 b = a.linearRemoveElement(1); 825 assert(b == true); 826 assert(equal(a[], [2, 3, 4])); 827 b = a.linearRemoveElement(2); 828 assert(b == true); 829 b = a.linearRemoveElement(20); 830 assert(b == false); 831 assert(equal(a[], [3, 4])); 832 b = a.linearRemoveElement(4); 833 assert(b == true); 834 assert(equal(a[], [3])); 835 b = a.linearRemoveElement(3); 836 assert(b == true); 837 assert(a.empty()); 838 a.linearRemoveElement(3); 839 } 840 841 @safe unittest 842 { 843 import std.algorithm.comparison : equal; 844 845 //Tests construction signatures 846 alias IntList = DList!int; 847 auto a0 = IntList(); 848 auto a1 = IntList(0); 849 auto a2 = IntList(0, 1); 850 auto a3 = IntList([0]); 851 auto a4 = IntList([0, 1]); 852 853 assert(a0[].empty); 854 assert(equal(a1[], [0])); 855 assert(equal(a2[], [0, 1])); 856 assert(equal(a3[], [0])); 857 assert(equal(a4[], [0, 1])); 858 } 859 860 @safe unittest 861 { 862 import std.algorithm.comparison : equal; 863 864 alias IntList = DList!int; 865 IntList list = IntList([0,1,2,3]); 866 assert(equal(list[],[0,1,2,3])); 867 list.insertBack([4,5,6,7]); 868 assert(equal(list[],[0,1,2,3,4,5,6,7])); 869 870 list = IntList(); 871 list.insertFront([0,1,2,3]); 872 assert(equal(list[],[0,1,2,3])); 873 list.insertFront([4,5,6,7]); 874 assert(equal(list[],[4,5,6,7,0,1,2,3])); 875 } 876 877 @safe unittest 878 { 879 import std.algorithm.comparison : equal; 880 import std.range : take; 881 882 alias IntList = DList!int; 883 IntList list = IntList([0,1,2,3]); 884 auto range = list[]; 885 for ( ; !range.empty; range.popFront()) 886 { 887 int item = range.front; 888 if (item == 2) 889 { 890 list.stableLinearRemove(take(range, 1)); 891 break; 892 } 893 } 894 assert(equal(list[],[0,1,3])); 895 896 list = IntList([0,1,2,3]); 897 range = list[]; 898 for ( ; !range.empty; range.popFront()) 899 { 900 int item = range.front; 901 if (item == 2) 902 { 903 list.stableLinearRemove(take(range,2)); 904 break; 905 } 906 } 907 assert(equal(list[],[0,1])); 908 909 list = IntList([0,1,2,3]); 910 range = list[]; 911 for ( ; !range.empty; range.popFront()) 912 { 913 int item = range.front; 914 if (item == 0) 915 { 916 list.stableLinearRemove(take(range,2)); 917 break; 918 } 919 } 920 assert(equal(list[],[2,3])); 921 922 list = IntList([0,1,2,3]); 923 range = list[]; 924 for ( ; !range.empty; range.popFront()) 925 { 926 int item = range.front; 927 if (item == 1) 928 { 929 list.stableLinearRemove(take(range,2)); 930 break; 931 } 932 } 933 assert(equal(list[],[0,3])); 934 } 935 936 @safe unittest 937 { 938 import std.algorithm.comparison : equal; 939 940 auto dl = DList!int([1, 2, 3, 4, 5]); 941 auto r = dl[]; 942 r.popFront(); 943 dl.popFirstOf(r); 944 assert(equal(dl[], [1, 3, 4, 5])); 945 assert(equal(r, [3, 4, 5])); 946 r.popBack(); 947 dl.popLastOf(r); 948 assert(equal(dl[], [1, 3, 5])); 949 assert(equal(r, [3])); 950 dl = DList!int([0]); 951 r = dl[]; 952 dl.popFirstOf(r); 953 assert(dl.empty); 954 dl = DList!int([0]); 955 r = dl[]; 956 dl.popLastOf(r); 957 assert(dl.empty); 958 } 959 960 @safe unittest 961 { 962 import std.algorithm.comparison : equal; 963 964 auto dl = DList!string(["a", "b", "d"]); 965 dl.insertAfter(dl[], "e"); // insert at the end 966 assert(equal(dl[], ["a", "b", "d", "e"])); 967 auto dlr = dl[]; 968 dlr.popBack(); dlr.popBack(); 969 dl.insertAfter(dlr, "c"); // insert after "b" 970 assert(equal(dl[], ["a", "b", "c", "d", "e"])); 971 } 972 973 @safe unittest 974 { 975 import std.algorithm.comparison : equal; 976 977 auto dl = DList!string(["a", "b", "d"]); 978 dl.insertBefore(dl[], "e"); // insert at the front 979 assert(equal(dl[], ["e", "a", "b", "d"])); 980 auto dlr = dl[]; 981 dlr.popFront(); dlr.popFront(); 982 dl.insertBefore(dlr, "c"); // insert before "b" 983 assert(equal(dl[], ["e", "a", "c", "b", "d"])); 984 } 985 986 @safe unittest 987 { 988 auto d = DList!int([1, 2, 3]); 989 d.front = 5; //test frontAssign 990 assert(d.front == 5); 991 auto r = d[]; 992 r.back = 1; 993 assert(r.back == 1); 994 } 995 996 // https://issues.dlang.org/show_bug.cgi?id=8895 997 @safe unittest 998 { 999 auto a = make!(DList!int)(1,2,3,4); 1000 auto b = make!(DList!int)(1,2,3,4); 1001 auto c = make!(DList!int)(1,2,3,5); 1002 auto d = make!(DList!int)(1,2,3,4,5); 1003 assert(a == b); // this better terminate! 1004 assert(!(a == c)); 1005 assert(!(a == d)); 1006 } 1007 1008 @safe unittest 1009 { 1010 auto d = DList!int([1, 2, 3]); 1011 d.front = 5; //test frontAssign 1012 assert(d.front == 5); 1013 auto r = d[]; 1014 r.back = 1; 1015 assert(r.back == 1); 1016 } 1017 1018 @safe unittest 1019 { 1020 auto a = DList!int(); 1021 assert(a.removeFront(10) == 0); 1022 a.insert([1, 2, 3]); 1023 assert(a.removeFront(10) == 3); 1024 assert(a[].empty); 1025 } 1026 1027 @safe unittest 1028 { 1029 import std.algorithm.comparison : equal; 1030 1031 //Verify all flavors of ~ 1032 auto a = DList!int(); 1033 auto b = DList!int(); 1034 auto c = DList!int([1, 2, 3]); 1035 auto d = DList!int([4, 5, 6]); 1036 1037 assert((a ~ b[])[].empty); 1038 assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6])); 1039 assert(c[].equal([1, 2, 3])); 1040 assert(d[].equal([4, 5, 6])); 1041 1042 assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6])); 1043 assert(c[].equal([1, 2, 3])); 1044 assert(d[].equal([4, 5, 6])); 1045 1046 a~=c[]; 1047 assert(a[].equal([1, 2, 3])); 1048 assert(c[].equal([1, 2, 3])); 1049 1050 a~=d[]; 1051 assert(a[].equal([1, 2, 3, 4, 5, 6])); 1052 assert(d[].equal([4, 5, 6])); 1053 1054 a~=[7, 8, 9]; 1055 assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); 1056 1057 //trick test: 1058 auto r = c[]; 1059 c.removeFront(); 1060 c.removeBack(); 1061 } 1062 1063 @safe unittest 1064 { 1065 import std.algorithm.comparison : equal; 1066 1067 // https://issues.dlang.org/show_bug.cgi?id=8905 1068 auto a = DList!int([1, 2, 3, 4]); 1069 auto r = a[]; 1070 a.stableRemoveBack(); 1071 a.stableInsertBack(7); 1072 assert(a[].equal([1, 2, 3, 7])); 1073 } 1074 1075 // https://issues.dlang.org/show_bug.cgi?id=12566 1076 @safe unittest 1077 { 1078 auto dl2 = DList!int([2,7]); 1079 dl2.removeFront(); 1080 assert(dl2[].walkLength == 1); 1081 dl2.removeBack(); 1082 assert(dl2.empty, "not empty?!"); 1083 } 1084 1085 // https://issues.dlang.org/show_bug.cgi?id=13076 1086 @safe unittest 1087 { 1088 DList!int list; 1089 assert(list.empty); 1090 list.clear(); 1091 } 1092 1093 // https://issues.dlang.org/show_bug.cgi?id=13425 1094 @safe unittest 1095 { 1096 import std.range : drop, take; 1097 auto list = DList!int([1,2,3,4,5]); 1098 auto r = list[].drop(4); // r is a view of the last element of list 1099 assert(r.front == 5 && r.walkLength == 1); 1100 r = list.linearRemove(r.take(1)); 1101 assert(r.empty); // fails 1102 } 1103 1104 // https://issues.dlang.org/show_bug.cgi?id=14300 1105 @safe unittest 1106 { 1107 interface ITest {} 1108 static class Test : ITest {} 1109 1110 DList!ITest().insertBack(new Test()); 1111 } 1112 1113 // https://issues.dlang.org/show_bug.cgi?id=15263 1114 @safe unittest 1115 { 1116 import std.range : iota; 1117 auto a = DList!int(); 1118 a.insertFront(iota(0, 5)); // can insert range with non-ref front 1119 assert(a.front == 0 && a.back == 4); 1120 } 1121