PageRenderTime 75ms CodeModel.GetById 40ms app.highlight 12ms RepoModel.GetById 19ms app.codeStats 0ms

/Utilities/Collections/PriorityQueue.cs

#
C# | 443 lines | 209 code | 32 blank | 202 comment | 10 complexity | e67c46f54882b91d06eb78a901526ace MD5 | raw file
  1using System;
  2using System.Collections;
  3using System.Collections.Generic;
  4using Delta.Utilities.Datatypes;
  5using Delta.Utilities.Helpers;
  6using NUnit.Framework;
  7
  8namespace Delta.Utilities.Collections
  9{
 10	/// <summary>
 11	/// Priority queue with generics.
 12	/// INFO: This is up to 3 times faster than the non generic version
 13	/// Delta.Utilities.Collections.PriorityQueue.cs, Benchmark results:
 14	/// 1000 times adding 500 elements and popping them:
 15	/// BinaryPriorityQueue: 403,76ms (32,6%)
 16	/// PriorityQueue: 429,24ms (34,6%)
 17	/// PriorityQueuePoint: 197,88ms (16%)
 18	/// PriorityQueue with generics (this class): 208,5ms (16,8%)
 19	/// <para />
 20	/// This class is way more flexible than fixed class for just one type like
 21	/// the PriorityQueuePoint class, optimized for a Point structure. Generics
 22	/// are good enough to avoid these special case classes.
 23	/// </summary>
 24	public class PriorityQueue<T>
 25		: ICloneable<PriorityQueue<T>>, IEnumerable<T>
 26		where T : IPriorityQueueData
 27	{
 28		#region Count (Public)
 29		/// <summary>
 30		/// Count, will NOT return numOfElements, because its 1-based.
 31		/// </summary>
 32		/// <typeparam name="T">T</typeparam>
 33		public int Count
 34		{
 35			get
 36			{
 37				return numOfElements - 1;
 38			} // get
 39		}
 40		#endregion
 41
 42		#region IsSynchronized (Public)
 43		/// <summary>
 44		/// Is synchronized
 45		/// </summary>
 46		/// <typeparam name="T">T</typeparam>
 47		public bool IsSynchronized
 48		{
 49			get
 50			{
 51				return data.IsSynchronized;
 52			} // get
 53		}
 54		#endregion
 55
 56		#region SyncRoot (Public)
 57		/// <summary>
 58		/// Sync root
 59		/// </summary>
 60		/// <typeparam name="T">T</typeparam>
 61		public object SyncRoot
 62		{
 63			get
 64			{
 65				return this;
 66			} // get
 67		}
 68		#endregion
 69
 70		#region Private
 71
 72		#region numOfElements (Private)
 73		/// <summary>
 74		/// Number of elements
 75		/// </summary>
 76		private int numOfElements;
 77		#endregion
 78
 79		#region data (Private)
 80		/// <summary>
 81		/// Data
 82		/// </summary>
 83		/// <typeparam name="T">T</typeparam>
 84		private T[] data;
 85		#endregion
 86
 87		#endregion
 88
 89		#region Constructors
 90		/// <summary>
 91		/// Create priority queue
 92		/// </summary>
 93		public PriorityQueue(T[] setInitialData)
 94		{
 95			// must start with 1, because of 2*i and 2*i+1 logic!
 96			numOfElements = 1;
 97			data = setInitialData;
 98		}
 99
100		/// <summary>
101		/// Priority queue
102		/// </summary>
103		public PriorityQueue()
104		{
105			numOfElements = 1;
106			data = new T[128];
107		}
108		#endregion
109
110		#region ICloneable<PriorityQueue<T>> Members
111		/// <summary>
112		/// Clone
113		/// </summary>
114		/// <returns>Clone</returns>
115		public PriorityQueue<T> Clone()
116		{
117			T[] clonedData = (T[])data.Clone();
118			return new PriorityQueue<T>(clonedData);
119		}
120		#endregion
121
122		#region IEnumerable Members
123		/// <summary>
124		/// System. collections. i enumerable. get enumerator
125		/// </summary>
126		/// <returns>System. collections. i enumerator</returns>
127		IEnumerator
128			IEnumerable.GetEnumerator()
129		{
130			return GetEnumerator();
131		}
132		#endregion
133
134		#region IEnumerable<T> Members
135		/// <summary>
136		/// IEnumerator
137		/// </summary>
138		/// <returns>IEnumerator</returns>
139		public IEnumerator<T> GetEnumerator()
140		{
141			for (int num = 1; num < numOfElements; num++)
142			{
143				yield return data[num];
144			}
145		}
146		#endregion
147
148		#region Push (Public)
149		/// <summary>
150		/// Push a new item to the priority queue.
151		/// Could realloc memory if bigger size is required.
152		/// </summary>
153		/// <param name="newData">New Data</param>
154		public void Push(T newData)
155		{
156			// Realloc if we reached the data size
157			if (numOfElements == data.Length)
158			{
159				// Just double the size, realloc happens less often if getting bigger
160				data = (T[])data.Resize(data.Length * 2);
161			}
162
163			// Insert item, search like quicksort (i/2) for item
164			// with higher priority and set it right before it!
165			int i = numOfElements++;
166			while (i > 1 &&
167			       data[i / 2].Priority > newData.Priority)
168			{
169				data[i] = data[i / 2];
170				i /= 2;
171			}
172
173			data[i] = newData;
174		}
175		#endregion
176
177		#region Pop (Public)
178		/// <summary>
179		/// Remove top entry (returns nothing, if you want to use top entry,
180		/// just use pq.data[1])
181		/// </summary>
182		public T Pop()
183		{
184			int i = 1, j;
185
186			if (numOfElements <= 1)
187			{
188				return default(T); // null;
189				//throw new IndexOutOfRangeException(
190				//	"There are no more elements in the PriorityQueue to Pop()");
191			}
192
193			T ret = data[1];
194			T tmp = data[--numOfElements];
195			while (i <= numOfElements / 2)
196			{
197				j = 2 * i;
198				if (j < numOfElements &&
199				    data[j].Priority > data[j + 1].Priority)
200				{
201					j++;
202				}
203				if (data[j].Priority >=
204				    tmp.Priority)
205				{
206					break;
207				}
208				data[i] = data[j];
209				i = j;
210			}
211			data[i] = tmp;
212			return ret;
213		}
214		#endregion
215
216		#region Peek (Public)
217		/// <summary>
218		/// Get the smallest object without removing it.
219		/// </summary>
220		/// <returns>The smalles object without removing it</returns>
221		public T Peek()
222		{
223			if (numOfElements <= 1)
224			{
225				return default(T);
226			}
227
228			return data[1];
229		}
230		#endregion
231
232		#region CopyTo (Public)
233		/// <summary>
234		/// Copy to
235		/// </summary>
236		/// <param name="array">Array</param>
237		/// <param name="index">Index</param>
238		public void CopyTo(Array array, int index)
239		{
240			// Just copy from index to end everything to array
241			for (int num = 0;
242				num < numOfElements &&
243				num + index < array.Length;
244				num++)
245			{
246				array.SetValue(data[num], num + index);
247			}
248		}
249		#endregion
250	}
251
252	/// <summary>
253	/// PriorityQueue tests, must be decleared outside of a generic class.
254	/// </summary>
255	public class PriorityQueueTests
256	{
257		#region Helpers
258
259		#region TestPriorityQueuePointData
260		/// <summary>
261		/// Test PriorityQueue helper class for Points
262		/// </summary>
263		public class TestPriorityQueuePointData : IPriorityQueueData
264		{
265			#region pos (Public)
266			/// <summary>
267			/// Position
268			/// </summary>
269			public Point pos;
270			#endregion
271
272			#region Priority (Public)
273			/// <summary>
274			/// Priority
275			/// </summary>
276			public float Priority
277			{
278				get
279				{
280					return priority;
281				} // get
282			}
283			#endregion
284
285			#region Private
286
287			#region priority (Private)
288			/// <summary>
289			/// Priority
290			/// </summary>
291			private readonly float priority;
292			#endregion
293
294			#endregion
295
296			#region Constructors
297			/// <summary>
298			/// Priority queue point data
299			/// </summary>
300			public TestPriorityQueuePointData(Point setPos, float setPriority)
301			{
302				pos = setPos;
303				priority = setPriority;
304			}
305			#endregion
306		}
307		#endregion
308
309		#endregion
310
311		#region TestPriorityQueueGenericPerformance (Static)
312		/// <summary>
313		/// Test priority queue generic performance
314		/// </summary>
315		[Test]
316		public static void TestPriorityQueueGenericPerformance()
317		{
318			/*fix up, provide a way for the non existing Profiler class
319			const int numOfRepeats = 2; // To eleminate JIT compiler time
320			const int numOfLoops = 1000;// 500;// 1000;
321			const int numOfAdds = 500;// 250;// 500;
322			int bigNum1 = 0, bigNum2 = 0, bigNum3 = 0, bigNum4 = 0, bigNum5 = 0;
323
324			for (int i = 0; i < numOfRepeats; i++)
325			{
326				Profiler.BeginSection("Test", "Check BinaryPriorityQueue");
327				for (int num = 0; num < numOfLoops; num++)
328				{
329					BinaryPriorityQueue testBPQ = new BinaryPriorityQueue();
330					for (int addNum = 0; addNum < numOfAdds; addNum++)
331					{
332						//Profiler.Add("push");
333						testBPQ.Push(num + addNum);
334						//Profiler.Add("pop");
335						if (addNum > 10)
336							bigNum1 += (int)testBPQ.Pop();
337						//Profiler.Add("rest");
338					} // for (addNum, <, ++)
339
340					//if (num == 0)
341					//	Console.WriteLine(ArrayHelper.Write(testBPQ));
342				} // for (num, <, ++)
343				//long profileMiddle = Win32Function.GetPerformanceCounter();
344				Profiler.EndSection();
345
346				// Strangely BinaryPriorityQueue isn't any faster with generics
347				Profiler.BeginSection("generic", "Check BinaryPriorityQueue generic");
348				for (int num = 0; num < numOfLoops; num++)
349				{
350					BinaryPriorityQueue<int> testBPQ = new BinaryPriorityQueue<int>();
351					for (int addNum = 0; addNum < numOfAdds; addNum++)
352					{
353						//Profiler.Add("push");
354						testBPQ.Push(num + addNum);
355						//Profiler.Add("pop");
356						if (addNum > 10)
357							bigNum2 += testBPQ.Pop();
358						//Profiler.Add("rest");
359					} // for (addNum, <, ++)
360
361					//if (num == 0)
362					//	Console.WriteLine(ArrayHelper.Write(testBPQ));
363				} // for (num, <, ++)
364				Profiler.EndSection();
365
366				Profiler.BeginSection("Test rest", "Check PriorityQueue");
367				for (int num = 0; num < numOfLoops; num++)
368				{
369					PriorityQueue testPQ = new PriorityQueue();
370					for (int addNum = 0; addNum < numOfAdds; addNum++)
371					{
372						testPQ.Push(new PriorityQueue.PriorityQueueTests.
373							TestPriorityQueuePointData(
374							new Point(10, 10), num + addNum));
375						if (addNum > 10)
376							bigNum3 += (int)testPQ.Pop().Priority;
377					} // for (addNum, <, ++)
378				} // for (num, <, ++)
379				//long profileEnd = Win32Function.GetPerformanceCounter();
380
381				Profiler.Add("Check PriorityQueuePoint");
382				for (int num = 0; num < numOfLoops; num++)
383				{
384					PriorityQueuePoint testPQP = new PriorityQueuePoint();
385					for (int addNum = 0; addNum < numOfAdds; addNum++)
386					{
387						testPQP.Push(new PriorityQueuePointData(
388							new Point(10, 10), num + addNum));
389						if (addNum > 10)
390							bigNum4 += (int)testPQP.Pop().Priority;
391					} // for (addNum, <, ++)
392				} // for (num, <, ++)
393
394				Profiler.Add("Check PriorityQueue generics");
395				for (int num = 0; num < numOfLoops; num++)
396				{
397					Delta.Utilities.Collections.PriorityQueue<PriorityQueuePointData> testPQG =
398						new Delta.Utilities.Collections.PriorityQueue<PriorityQueuePointData>();
399					for (int addNum = 0; addNum < numOfAdds; addNum++)
400					{
401						testPQG.Push(new PriorityQueuePointData(
402							new Point(10, 10), num + addNum));
403						if (addNum > 10)
404							bigNum5 += (int)testPQG.Pop().Priority;
405					} // for (addNum, <, ++)
406				} // for (num, <, ++)
407
408				Profiler.EndSection();
409
410				Console.WriteLine(Profiler.GetProfilerText());
411			} // for (int)
412
413			Console.WriteLine("bigNum for BinaryPriorityQueue=" + bigNum1);// +
414			Console.WriteLine("bigNum for BinaryPriorityQueue generic=" + bigNum2);
415			Console.WriteLine("bigNum for PriorityQueue=" + bigNum3);// +
416			Console.WriteLine("bigNum for PriorityQueuePoint=" + bigNum4);// +
417			Console.WriteLine("bigNum for PriorityQueue with generics=" + bigNum5);
418			 */
419		}
420		#endregion
421
422		#region TestPriorityQueue
423		/// <summary>
424		/// Test PriorityQueue
425		/// </summary>
426		[Test]
427		public void TestPriorityQueue()
428		{
429			PriorityQueue<TestPriorityQueuePointData> pq =
430				new PriorityQueue<TestPriorityQueuePointData>();
431
432			pq.Push(new TestPriorityQueuePointData(new Point(0, 0), 1));
433			pq.Push(new TestPriorityQueuePointData(new Point(5, 3), 3));
434			pq.Push(new TestPriorityQueuePointData(new Point(40, 10), 30));
435			pq.Push(new TestPriorityQueuePointData(new Point(5, 5), 5));
436			Assert.Equal(1, pq.Pop().Priority);
437			Assert.Equal(3, pq.Pop().Priority);
438			Assert.Equal(5, pq.Pop().Priority);
439			Assert.Equal(30, pq.Pop().Priority);
440		}
441		#endregion
442	}
443}