/Utilities/Collections/Dequeue.cs
C# | 930 lines | 573 code | 67 blank | 290 comment | 44 complexity | deae76d0fff26c3a421b9ed577ac069d MD5 | raw file
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using NUnit.Framework; 5 6namespace Delta.Utilities.Collections 7{ 8 /// <summary> 9 /// System.Collections conform class for a ring-queue. 10 /// </summary> 11 /// <remarks> 12 /// The collection support adding and removing at both ends and automatic 13 /// expansion. The left end of the ring is referred to as head, the right 14 /// end as tail. Add / Remove needs always the same time, expansion takes 15 /// more time, depending on the size. Indexed access and enumeration is fast. 16 /// </remarks> 17 public class Dequeue<T> 18 : ICollection<T>, IEnumerable<T>, ICloneable<Dequeue<T>> 19 { 20 #region IsReadOnly (Public) 21 /// <summary> 22 /// Is read only? Will always return false. 23 /// </summary> 24 public bool IsReadOnly 25 { 26 get 27 { 28 return false; 29 } // get 30 } 31 #endregion 32 33 #region AllowsDuplicates (Public) 34 /// <summary> 35 /// Allows duplicates? Will always return false. 36 /// </summary> 37 public bool AllowsDuplicates 38 { 39 get 40 { 41 return true; 42 } // get 43 } 44 #endregion 45 46 #region IsEmpty (Public) 47 /// <summary> 48 /// Is empty? Will return true if we have at least one item. 49 /// </summary> 50 public bool IsEmpty 51 { 52 get 53 { 54 return count != 0; 55 } // get 56 } 57 #endregion 58 59 #region Item (Public) 60 /// <summary> 61 /// Indexed access to all elements currently in the collection. 62 /// Indexing starts at 0 (head) and ends at Count-1 (tail). 63 /// </summary> 64 /// <returns>Item at this index</returns> 65 public T this[int index] 66 { 67 get 68 { 69 if (index < 0 || 70 index >= Count) 71 { 72 throw new ArgumentOutOfRangeException("index"); 73 } 74 return innerList[(head + index) % Capacity]; 75 } 76 set 77 { 78 if (index < 0 || 79 index >= Count) 80 { 81 throw new ArgumentOutOfRangeException("index"); 82 } 83 innerList[(head + index) % Capacity] = value; 84 version++; 85 } 86 } 87 #endregion 88 89 #region GrowthFactor (Public) 90 /// <summary> 91 /// The current factor by which to grow the collection in case of expansion 92 /// </summary> 93 /// <typeparam name="T">T</typeparam> 94 public double GrowthFactor 95 { 96 get 97 { 98 return growthFactor; 99 } 100 set 101 { 102 growthFactor = value; 103 } 104 } 105 #endregion 106 107 #region Capacity (Public) 108 /// <summary> 109 /// The current amount of cells available to the dequeue 110 /// </summary> 111 /// <typeparam name="T">T</typeparam> 112 public int Capacity 113 { 114 get 115 { 116 return innerList.Length; 117 } 118 set 119 { 120 if (Capacity >= Count) 121 { 122 SetSize(Capacity); 123 } 124 else 125 { 126 throw new ArgumentException("Capacity was smaller than Count!"); 127 } 128 } 129 } 130 #endregion 131 132 #region Version (Public) 133 /// <summary> 134 /// The current version of the dequeue. The version is increased with every 135 /// changing operation. The main use is to invalidate all IEnumerators. 136 /// </summary> 137 /// <typeparam name="T">T</typeparam> 138 public ulong Version 139 { 140 get 141 { 142 return version; 143 } 144 } 145 #endregion 146 147 #region IsSynchronized (Public) 148 /// <summary> 149 /// Returns true. 150 /// </summary> 151 /// <typeparam name="T">T</typeparam> 152 public bool IsSynchronized 153 { 154 get 155 { 156 return true; 157 } 158 } 159 #endregion 160 161 #region Count (Public) 162 /// <summary> 163 /// The current number of elements in the queue 164 /// </summary> 165 /// <typeparam name="T">T</typeparam> 166 public int Count 167 { 168 get 169 { 170 return count; 171 } 172 } 173 #endregion 174 175 #region SyncRoot (Public) 176 /// <summary> 177 /// Returns sync root for this dequeue, which is always this object. 178 /// </summary> 179 public object SyncRoot 180 { 181 get 182 { 183 return this; 184 } 185 } 186 #endregion 187 188 #region Protected 189 190 #region innerList (Protected) 191 /// <summary> 192 /// Inner list for this dequeue 193 /// </summary> 194 /// <typeparam name="T">T</typeparam> 195 protected T[] innerList; 196 #endregion 197 198 #region growthFactor (Protected) 199 /// <summary> 200 /// Grow factor 201 /// </summary> 202 /// <typeparam name="T">T</typeparam> 203 protected double growthFactor; 204 #endregion 205 206 #region head (Protected) 207 /// <summary> 208 /// Head, tail and count of elements 209 /// </summary> 210 /// <typeparam name="T">T</typeparam> 211 protected int head; 212 #endregion 213 214 #region tail (Protected) 215 /// <summary> 216 /// Head, tail and count of elements 217 /// </summary> 218 /// <typeparam name="T">T</typeparam> 219 protected int tail; 220 #endregion 221 222 #region count (Protected) 223 /// <summary> 224 /// Head, tail and count of elements 225 /// </summary> 226 /// <typeparam name="T">T</typeparam> 227 protected int count; 228 #endregion 229 230 #region version (Protected) 231 /// <summary> 232 /// Version, who needs that? 233 /// </summary> 234 /// <typeparam name="T">T</typeparam> 235 protected ulong version; 236 #endregion 237 238 #endregion 239 240 #region Constructors 241 /// <summary> 242 /// Create an empty Dequeue with capacity 32 and growth 2 243 /// </summary> 244 public Dequeue() 245 : this(32, 2.0) 246 { 247 } 248 249 /// <summary> 250 /// Create an empty Dequeue with given capacity and growth 2 251 /// </summary> 252 /// <param name="capacity">the initial capacity of the collection</param> 253 public Dequeue(int capacity) 254 : this(capacity, 2.0) 255 { 256 } 257 258 /// <summary> 259 /// Create an empty Dequeue with given capacity and given growth 260 /// </summary> 261 /// <param name="capacity">The initial capacity of the collection</param> 262 /// <param name="setGrowthFactor">The factor by which to grow the 263 /// collection when the capacity is reached</param> 264 public Dequeue(int capacity, double setGrowthFactor) 265 { 266 if (capacity < 1) 267 { 268 throw new ArgumentException("capacity", "must be at least 1."); 269 } 270 271 innerList = new T[capacity]; 272 growthFactor = setGrowthFactor; 273 head = 0; 274 tail = capacity - 1; 275 /// <summary> 276 /// Count 277 /// </summary> 278 count = 0; 279 version = 0; 280 } 281 282 /// <summary> 283 /// Create a new Dequeue as a copy of the given collection 284 /// </summary> 285 /// <param name="collection">The source collection</param> 286 public Dequeue(ICollection<T> collection) 287 : this(collection, collection.Count) 288 { 289 } 290 291 /// <summary> 292 /// Create a new Dequeue as a copy of the given collection and the 293 /// given capacity. 294 /// </summary> 295 /// <param name="collection">The source collection</param> 296 /// <param name="capacity">The capacity of the new Dequeue 297 /// (must be bigger or equal as C.Count)</param> 298 public Dequeue(ICollection<T> collection, int capacity) 299 : this(capacity, 2.0) 300 { 301 tail = 0; 302 EnqueueTailRange(collection); 303 } 304 #endregion 305 306 #region ICloneable<Dequeue<T>> Members 307 /// <summary> 308 /// Standard implementation. 309 /// </summary> 310 /// <returns>A Dequeue with a shallow copy of this one.</returns> 311 public Dequeue<T> Clone() 312 { 313 Dequeue<T> newDequeue = new Dequeue<T>(this, Capacity); 314 newDequeue.growthFactor = growthFactor; 315 newDequeue.version = Version; 316 return newDequeue; 317 } 318 #endregion 319 320 #region ICollection<T> Members 321 /// <summary> 322 /// Add. Not supported, do not call! 323 /// </summary> 324 /// <param name="item">Item to add</param> 325 public void Add(T item) 326 { 327 throw new Exception("The method or operation is not implemented."); 328 } 329 330 /// <summary> 331 /// Deletes all entries from the collection 332 /// </summary> 333 public void Clear() 334 { 335 head = tail = count = 0; 336 version++; 337 } 338 339 /// <summary> 340 /// Contains 341 /// </summary> 342 /// <param name="item">Item</param> 343 /// <returns>True if the item was found, false otherwise.</returns> 344 public bool Contains(T item) 345 { 346 for (int num = head; num < head + count; num++) 347 { 348 if (innerList[num % Capacity].Equals(item)) 349 { 350 return true; 351 } 352 } 353 354 // Not found 355 return false; 356 } 357 358 /// <summary> 359 /// Implementation of the ICollection.CopyTo function. 360 /// </summary> 361 /// <param name="array">Target array</param> 362 /// <param name="arrayIndex">Start-Index in target array</param> 363 public void CopyTo(T[] array, int arrayIndex) 364 { 365 if (array == null) 366 { 367 throw new ArgumentNullException("array"); 368 } 369 if (arrayIndex < 0) 370 { 371 throw new ArgumentOutOfRangeException( 372 "arrayIndex", 373 "Must be at least zero: " + arrayIndex); 374 } 375 if (arrayIndex > count) 376 { 377 throw new Exception("arrayIndex=" + arrayIndex + 378 " is out of range for target array (count=" + count + 379 ")"); 380 } 381 if (array.Length - arrayIndex < Count) 382 { 383 throw new ArgumentException("Array was to small!"); 384 } 385 if (array.Rank > 1) 386 { 387 throw new ArgumentException("Array was multidimensional!"); 388 } 389 390 for (int num = arrayIndex; num < array.Length; num++) 391 { 392 array[num] = innerList[(head + num) % innerList.Length]; 393 } 394 } 395 396 /// <summary> 397 /// Remove. Do not call, this is not implemented! 398 /// </summary> 399 /// <param name="item">Item</param> 400 /// <returns> 401 /// True if removing the item succeeded, false otherwise. 402 /// </returns> 403 public bool Remove(T item) 404 { 405 throw new Exception("The method or operation is not implemented."); 406 } 407 #endregion 408 409 #region IEnumerable Members 410 /// <summary> 411 /// System. collections. i enumerable. get enumerator 412 /// </summary> 413 /// <returns>System. collections. i enumerator</returns> 414 IEnumerator 415 IEnumerable.GetEnumerator() 416 { 417 return GetEnumerator(); 418 } 419 #endregion 420 421 #region IEnumerable<T> Members 422 /// <summary> 423 /// Standard implementation. 424 /// </summary> 425 /// <returns>A DequeueEnumerator on the current dequeue</returns> 426 public IEnumerator<T> GetEnumerator() 427 { 428 for (int num = head; num < head + count; num++) 429 { 430 yield return innerList[num % innerList.Length]; 431 } 432 } 433 #endregion 434 435 #region Equals (Public) 436 /// <summary> 437 /// Equals 438 /// </summary> 439 /// <param name="that">That</param> 440 /// <returns>True if the other collection (that) is the same.</returns> 441 public bool Equals(ICollection<T> that) 442 { 443 // Only compare if the reference is equal, we could also check 444 // if the elements are equal, but who cares ... 445 return this == that; 446 } 447 #endregion 448 449 #region ContainsCount (Public) 450 /// <summary> 451 /// Contains count 452 /// </summary> 453 /// <param name="item">Item</param> 454 /// <returns>Int</returns> 455 public int ContainsCount(T item) 456 { 457 int contained = 0; 458 for (int num = head; num < head + count; num++) 459 { 460 if (innerList[num % Capacity].Equals(item)) 461 { 462 contained++; 463 } 464 } 465 return contained; 466 } 467 #endregion 468 469 #region ContainsAll (Public) 470 /// <summary> 471 /// Contains all 472 /// </summary> 473 /// <param name="items">Items to check</param> 474 /// <returns>Returns true if all items are in this dequeue.</returns> 475 public bool ContainsAll(IEnumerable<T> items) 476 { 477 foreach (T checkItem in items) 478 { 479 bool found = false; 480 for (int num = head; num < head + count; num++) 481 { 482 if (innerList[num % Capacity].Equals(checkItem)) 483 { 484 found = true; 485 break; 486 } 487 } 488 // Item not found, return false 489 if (found == false) 490 { 491 return false; 492 } 493 } 494 // All items passed, everything is in list 495 return true; 496 } 497 #endregion 498 499 #region Find (Public) 500 /// <summary> 501 /// Find. Do not call, this method is not implemented! 502 /// </summary> 503 /// <param name="item">Item</param> 504 /// <returns>True if the item was found</returns> 505 public bool Find(ref T item) 506 { 507 throw new Exception("The method or operation is not implemented."); 508 } 509 #endregion 510 511 #region FindOrAdd (Public) 512 /// <summary> 513 /// Find or add. Do not call, this method is not implemented! 514 /// </summary> 515 /// <param name="item">Item</param> 516 /// <returns>True if the item was found or could be added.</returns> 517 public bool FindOrAdd(ref T item) 518 { 519 throw new Exception("The method or operation is not implemented."); 520 } 521 #endregion 522 523 #region Update (Public) 524 /// <summary> 525 /// Update. Do not call, this method is not implemented! 526 /// </summary> 527 /// <param name="item">Item</param> 528 /// <returns>True if the item was updated.</returns> 529 public bool Update(T item) 530 { 531 throw new Exception("The method or operation is not implemented."); 532 } 533 #endregion 534 535 #region UpdateOrAdd (Public) 536 /// <summary> 537 /// Update or add. Do not call, this method is not implemented! 538 /// </summary> 539 /// <param name="item">Item</param> 540 /// <returns>True if the item was updated or added.</returns> 541 public bool UpdateOrAdd(T item) 542 { 543 throw new Exception("The method or operation is not implemented."); 544 } 545 #endregion 546 547 #region RemoveWithReturn (Public) 548 /// <summary> 549 /// Remove with return. Do not call, this method is not implemented! 550 /// </summary> 551 /// <param name="item">Item</param> 552 /// <returns>True if the item was removed</returns> 553 public bool RemoveWithReturn(ref T item) 554 { 555 throw new Exception("The method or operation is not implemented."); 556 } 557 #endregion 558 559 #region RemoveAllCopies (Public) 560 /// <summary> 561 /// Remove all copies 562 /// </summary> 563 /// <param name="item">Item</param> 564 public void RemoveAllCopies(T item) 565 { 566 throw new Exception("The method or operation is not implemented."); 567 } 568 #endregion 569 570 #region RemoveAll (Public) 571 /// <summary> 572 /// Remove all 573 /// </summary> 574 /// <param name="items">Items</param> 575 public void RemoveAll(IEnumerable<T> items) 576 { 577 throw new Exception("The method or operation is not implemented."); 578 } 579 #endregion 580 581 #region RetainAll (Public) 582 /// <summary> 583 /// Retain all 584 /// </summary> 585 /// <param name="items">Items</param> 586 public void RetainAll(IEnumerable<T> items) 587 { 588 throw new Exception("The method or operation is not implemented."); 589 } 590 #endregion 591 592 #region AddAll (Public) 593 /// <summary> 594 /// Add all 595 /// </summary> 596 /// <param name="items">Items</param> 597 public void AddAll(IEnumerable<T> items) 598 { 599 throw new Exception("The method or operation is not implemented."); 600 } 601 602 /// <summary> 603 /// Add all 604 /// </summary> 605 /// <param name="items">Items</param> 606 public void AddAll<U>(IEnumerable<U> items) where U : T 607 { 608 throw new Exception("The method or operation is not implemented."); 609 } 610 #endregion 611 612 #region Check (Public) 613 /// <summary> 614 /// Check the queue. Do not call, this method is not implemented! 615 /// </summary> 616 /// <returns>True if the check succeeded, false otherwise.</returns> 617 public bool Check() 618 { 619 throw new Exception("The method or operation is not implemented."); 620 } 621 #endregion 622 623 #region ToArray (Public) 624 /// <summary> 625 /// To array 626 /// </summary> 627 /// <returns>Filled array with the same data as this dequeue</returns> 628 public T[] ToArray() 629 { 630 T[] ret = new T[count]; 631 CopyTo(ret, 0); 632 return ret; 633 } 634 #endregion 635 636 #region EnqueueHead (Public) 637 /// <summary> 638 /// Add the given object to the collections head 639 /// </summary> 640 /// <param name="value">The object to enqueue</param> 641 public void EnqueueHead(T value) 642 { 643 if (Count == Capacity) 644 { 645 SetSize((int)(Capacity * GrowthFactor)); 646 } 647 head--; 648 if (head < 0) 649 { 650 head += Capacity; 651 } 652 innerList[head] = value; 653 count++; 654 version++; 655 } 656 #endregion 657 658 #region EnqueueTail (Public) 659 /// <summary> 660 /// Add the given object to the collections tail 661 /// </summary> 662 /// <param name="value">The object to enqueue</param> 663 public void EnqueueTail(T value) 664 { 665 if (Count == Capacity) 666 { 667 SetSize((int)(Capacity * GrowthFactor)); 668 } 669 tail++; 670 tail %= Capacity; 671 innerList[tail] = value; 672 count++; 673 version++; 674 } 675 #endregion 676 677 #region DequeueHead (Public) 678 /// <summary> 679 /// Retrieve and remove the current head 680 /// </summary> 681 /// <returns>The removed object</returns> 682 public T DequeueHead() 683 { 684 if (Count == 0) 685 { 686 throw new Exception("Dequeue was empty!"); 687 } 688 T r = innerList[head]; 689 head++; 690 head %= Capacity; 691 count--; 692 version++; 693 return r; 694 } 695 #endregion 696 697 #region DequeueTail (Public) 698 /// <summary> 699 /// Retrieve and remove the current tail 700 /// </summary> 701 /// <returns>The removed object</returns> 702 public T DequeueTail() 703 { 704 if (Count == 0) 705 { 706 throw new Exception("Dequeue was empty!"); 707 } 708 T r = innerList[tail]; 709 tail--; 710 if (tail < 0) 711 { 712 tail += Capacity; 713 } 714 count--; 715 version++; 716 return r; 717 } 718 #endregion 719 720 #region EnqueueTailRange (Public) 721 /// <summary> 722 /// Add the given collection to the dequeues tail 723 /// </summary> 724 /// <param name="collection">The source collection</param> 725 public void EnqueueTailRange(ICollection<T> collection) 726 { 727 int cap = Capacity; 728 while (cap < collection.Count) 729 { 730 cap = (int)(cap * GrowthFactor); 731 } 732 if (cap > Capacity) 733 { 734 SetSize(cap); 735 } 736 737 foreach (T obj in collection) 738 { 739 EnqueueTail(obj); 740 } 741 } 742 #endregion 743 744 #region EnqueueHeadRange (Public) 745 /// <summary> 746 /// Add the given collection to the dequeues head. 747 /// To preserve the order in the collection, the entries are 748 /// added in revers order. 749 /// </summary> 750 /// <param name="collection">The source collection</param> 751 public void EnqueueHeadRange(ICollection<T> collection) 752 { 753 int cap = Capacity; 754 while (cap < collection.Count) 755 { 756 cap = (int)(cap * GrowthFactor); 757 } 758 if (cap > Capacity) 759 { 760 SetSize(cap); 761 } 762 763 List<T> tempList = new List<T>(collection); 764 tempList.Reverse(); 765 foreach (T obj in tempList) 766 { 767 EnqueueHead(obj); 768 } 769 } 770 #endregion 771 772 #region TrimToSize (Public) 773 /// <summary> 774 /// Sets the capacity to Count. 775 /// </summary> 776 public void TrimToSize() 777 { 778 SetSize(Count); 779 } 780 #endregion 781 782 #region CopyTo (Public) 783 /// <summary> 784 /// Implementation of the ICollection.CopyTo function. 785 /// </summary> 786 /// <param name="array">Target array</param> 787 /// <param name="index">Start-Index in target array</param> 788 public void CopyTo(Array array, int index) 789 { 790 if (array == null) 791 { 792 throw new ArgumentNullException("array"); 793 } 794 if (index < 0) 795 { 796 throw new ArgumentOutOfRangeException("index", 797 "Must be at least zero: " + index); 798 } 799 if (array.Length - index < Count) 800 { 801 throw new ArgumentException("Array was to small!"); 802 } 803 if (array.Rank > 1) 804 { 805 throw new ArgumentException("Array was multidimensional!"); 806 } 807 808 for (int num = 0; num < Count; num++) 809 { 810 array.SetValue(this[num], num + index); 811 } 812 } 813 #endregion 814 815 #region Methods (Private) 816 817 #region SetSize 818 /// <summary> 819 /// Sets the collections capacity to newSize 820 /// </summary> 821 /// <param name="newSize">the new collection size 822 /// (must be >= Count)</param> 823 protected void SetSize(int newSize) 824 { 825 if (newSize < Count) 826 { 827 throw new ArgumentException("New Size was smaller than Count!"); 828 } 829 830 T[] newInnerList = new T[newSize]; 831 for (int num = 0; num < Count; num++) 832 { 833 newInnerList[num] = this[num]; 834 } 835 head = 0; 836 tail = Count; 837 innerList = newInnerList; 838 version++; 839 } 840 #endregion 841 842 #endregion 843 } 844 845 /// <summary> 846 /// Dequeue tests, needs to be an extra class because Dequeue is generic. 847 /// </summary> 848 internal class DequeueTests 849 { 850 #region TestDequeueGenericPerformance (Static) 851 /// <summary> 852 /// Test dequeue generic performance 853 /// </summary> 854 [Test, Category("LongRunning")] 855 public static void TestDequeueGenericPerformance() 856 { 857 /*finish this when the Profiler in Delta.Utilities is re-enabled 858 // Run 10 million loops 859 const int NumOfElementsToAdd = 10000000; 860 861 Profiler.BeginSection("Test linked lists", "Dequeue"); 862 Dequeue dequeue = new Dequeue(); 863 for (int num = 0; num < NumOfElementsToAdd; num++) 864 { 865 dequeue.EnqueueHead(num); 866 if (num > 10) 867 //Console.WriteLine( 868 dequeue.DequeueTail(); 869 } // for (num) 870 871 Profiler.Add("Dequeue with generics"); 872 Dequeue<int> dequeueGeneric = new Dequeue<int>(); 873 for (int num = 0; num < NumOfElementsToAdd; num++) 874 { 875 dequeueGeneric.EnqueueHead(num); 876 if (num > 10) 877 //Console.WriteLine( 878 dequeueGeneric.DequeueTail(); 879 } // for (num) 880 Profiler.EndSection(); 881 882 Console.WriteLine(Profiler.GetProfilerText()); 883 884 // Results for 10000000 (10 mio) elements to add: 885 // Profiler (Total time: 1,48s) 886 // Dequeue: 949,33ms (64,1%) 887 // Dequeue with generics: 531,11ms (35,9%) 888 */ 889 } 890 #endregion 891 892 #region TestEnqueueHead 893 /// <summary> 894 /// Test enqueue head 895 /// </summary> 896 [Test] 897 public void TestEnqueueHead() 898 { 899 Dequeue<int> dequeue = new Dequeue<int>(); 900 dequeue.EnqueueHead(1); 901 dequeue.EnqueueHead(2); 902 dequeue.EnqueueHead(3); 903 dequeue.EnqueueHead(5); 904 Assert.Equal(5, dequeue.DequeueHead()); 905 Assert.Equal(3, dequeue.DequeueHead()); 906 Assert.Equal(1, dequeue.DequeueTail()); 907 Assert.Equal(1, dequeue.Count); 908 } 909 #endregion 910 911 #region TestEnqueueTail 912 /// <summary> 913 /// Test enqueue tail 914 /// </summary> 915 [Test] 916 public void TestEnqueueTail() 917 { 918 Dequeue<int> dequeue = new Dequeue<int>(); 919 dequeue.EnqueueTail(1); 920 dequeue.EnqueueTail(2); 921 dequeue.EnqueueTail(3); 922 dequeue.EnqueueTail(5); 923 Assert.Equal(5, dequeue.DequeueTail()); 924 Assert.Equal(3, dequeue.DequeueTail()); 925 Assert.Equal(1, dequeue.DequeueHead()); 926 Assert.Equal(1, dequeue.Count); 927 } 928 #endregion 929 } 930}