PageRenderTime 149ms CodeModel.GetById 41ms app.highlight 69ms RepoModel.GetById 31ms app.codeStats 0ms

/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 &gt;= 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}