/Utilities/Collections/PriorityQueue.cs
C# | 443 lines | 209 code | 32 blank | 202 comment | 10 complexity | e67c46f54882b91d06eb78a901526ace MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Collections
- {
- /// <summary>
- /// Priority queue with generics.
- /// INFO: This is up to 3 times faster than the non generic version
- /// Delta.Utilities.Collections.PriorityQueue.cs, Benchmark results:
- /// 1000 times adding 500 elements and popping them:
- /// BinaryPriorityQueue: 403,76ms (32,6%)
- /// PriorityQueue: 429,24ms (34,6%)
- /// PriorityQueuePoint: 197,88ms (16%)
- /// PriorityQueue with generics (this class): 208,5ms (16,8%)
- /// <para />
- /// This class is way more flexible than fixed class for just one type like
- /// the PriorityQueuePoint class, optimized for a Point structure. Generics
- /// are good enough to avoid these special case classes.
- /// </summary>
- public class PriorityQueue<T>
- : ICloneable<PriorityQueue<T>>, IEnumerable<T>
- where T : IPriorityQueueData
- {
- #region Count (Public)
- /// <summary>
- /// Count, will NOT return numOfElements, because its 1-based.
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public int Count
- {
- get
- {
- return numOfElements - 1;
- } // get
- }
- #endregion
-
- #region IsSynchronized (Public)
- /// <summary>
- /// Is synchronized
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public bool IsSynchronized
- {
- get
- {
- return data.IsSynchronized;
- } // get
- }
- #endregion
-
- #region SyncRoot (Public)
- /// <summary>
- /// Sync root
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public object SyncRoot
- {
- get
- {
- return this;
- } // get
- }
- #endregion
-
- #region Private
-
- #region numOfElements (Private)
- /// <summary>
- /// Number of elements
- /// </summary>
- private int numOfElements;
- #endregion
-
- #region data (Private)
- /// <summary>
- /// Data
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- private T[] data;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create priority queue
- /// </summary>
- public PriorityQueue(T[] setInitialData)
- {
- // must start with 1, because of 2*i and 2*i+1 logic!
- numOfElements = 1;
- data = setInitialData;
- }
-
- /// <summary>
- /// Priority queue
- /// </summary>
- public PriorityQueue()
- {
- numOfElements = 1;
- data = new T[128];
- }
- #endregion
-
- #region ICloneable<PriorityQueue<T>> Members
- /// <summary>
- /// Clone
- /// </summary>
- /// <returns>Clone</returns>
- public PriorityQueue<T> Clone()
- {
- T[] clonedData = (T[])data.Clone();
- return new PriorityQueue<T>(clonedData);
- }
- #endregion
-
- #region IEnumerable Members
- /// <summary>
- /// System. collections. i enumerable. get enumerator
- /// </summary>
- /// <returns>System. collections. i enumerator</returns>
- IEnumerator
- IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
-
- #region IEnumerable<T> Members
- /// <summary>
- /// IEnumerator
- /// </summary>
- /// <returns>IEnumerator</returns>
- public IEnumerator<T> GetEnumerator()
- {
- for (int num = 1; num < numOfElements; num++)
- {
- yield return data[num];
- }
- }
- #endregion
-
- #region Push (Public)
- /// <summary>
- /// Push a new item to the priority queue.
- /// Could realloc memory if bigger size is required.
- /// </summary>
- /// <param name="newData">New Data</param>
- public void Push(T newData)
- {
- // Realloc if we reached the data size
- if (numOfElements == data.Length)
- {
- // Just double the size, realloc happens less often if getting bigger
- data = (T[])data.Resize(data.Length * 2);
- }
-
- // Insert item, search like quicksort (i/2) for item
- // with higher priority and set it right before it!
- int i = numOfElements++;
- while (i > 1 &&
- data[i / 2].Priority > newData.Priority)
- {
- data[i] = data[i / 2];
- i /= 2;
- }
-
- data[i] = newData;
- }
- #endregion
-
- #region Pop (Public)
- /// <summary>
- /// Remove top entry (returns nothing, if you want to use top entry,
- /// just use pq.data[1])
- /// </summary>
- public T Pop()
- {
- int i = 1, j;
-
- if (numOfElements <= 1)
- {
- return default(T); // null;
- //throw new IndexOutOfRangeException(
- // "There are no more elements in the PriorityQueue to Pop()");
- }
-
- T ret = data[1];
- T tmp = data[--numOfElements];
- while (i <= numOfElements / 2)
- {
- j = 2 * i;
- if (j < numOfElements &&
- data[j].Priority > data[j + 1].Priority)
- {
- j++;
- }
- if (data[j].Priority >=
- tmp.Priority)
- {
- break;
- }
- data[i] = data[j];
- i = j;
- }
- data[i] = tmp;
- return ret;
- }
- #endregion
-
- #region Peek (Public)
- /// <summary>
- /// Get the smallest object without removing it.
- /// </summary>
- /// <returns>The smalles object without removing it</returns>
- public T Peek()
- {
- if (numOfElements <= 1)
- {
- return default(T);
- }
-
- return data[1];
- }
- #endregion
-
- #region CopyTo (Public)
- /// <summary>
- /// Copy to
- /// </summary>
- /// <param name="array">Array</param>
- /// <param name="index">Index</param>
- public void CopyTo(Array array, int index)
- {
- // Just copy from index to end everything to array
- for (int num = 0;
- num < numOfElements &&
- num + index < array.Length;
- num++)
- {
- array.SetValue(data[num], num + index);
- }
- }
- #endregion
- }
-
- /// <summary>
- /// PriorityQueue tests, must be decleared outside of a generic class.
- /// </summary>
- public class PriorityQueueTests
- {
- #region Helpers
-
- #region TestPriorityQueuePointData
- /// <summary>
- /// Test PriorityQueue helper class for Points
- /// </summary>
- public class TestPriorityQueuePointData : IPriorityQueueData
- {
- #region pos (Public)
- /// <summary>
- /// Position
- /// </summary>
- public Point pos;
- #endregion
-
- #region Priority (Public)
- /// <summary>
- /// Priority
- /// </summary>
- public float Priority
- {
- get
- {
- return priority;
- } // get
- }
- #endregion
-
- #region Private
-
- #region priority (Private)
- /// <summary>
- /// Priority
- /// </summary>
- private readonly float priority;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Priority queue point data
- /// </summary>
- public TestPriorityQueuePointData(Point setPos, float setPriority)
- {
- pos = setPos;
- priority = setPriority;
- }
- #endregion
- }
- #endregion
-
- #endregion
-
- #region TestPriorityQueueGenericPerformance (Static)
- /// <summary>
- /// Test priority queue generic performance
- /// </summary>
- [Test]
- public static void TestPriorityQueueGenericPerformance()
- {
- /*fix up, provide a way for the non existing Profiler class
- const int numOfRepeats = 2; // To eleminate JIT compiler time
- const int numOfLoops = 1000;// 500;// 1000;
- const int numOfAdds = 500;// 250;// 500;
- int bigNum1 = 0, bigNum2 = 0, bigNum3 = 0, bigNum4 = 0, bigNum5 = 0;
-
- for (int i = 0; i < numOfRepeats; i++)
- {
- Profiler.BeginSection("Test", "Check BinaryPriorityQueue");
- for (int num = 0; num < numOfLoops; num++)
- {
- BinaryPriorityQueue testBPQ = new BinaryPriorityQueue();
- for (int addNum = 0; addNum < numOfAdds; addNum++)
- {
- //Profiler.Add("push");
- testBPQ.Push(num + addNum);
- //Profiler.Add("pop");
- if (addNum > 10)
- bigNum1 += (int)testBPQ.Pop();
- //Profiler.Add("rest");
- } // for (addNum, <, ++)
-
- //if (num == 0)
- // Console.WriteLine(ArrayHelper.Write(testBPQ));
- } // for (num, <, ++)
- //long profileMiddle = Win32Function.GetPerformanceCounter();
- Profiler.EndSection();
-
- // Strangely BinaryPriorityQueue isn't any faster with generics
- Profiler.BeginSection("generic", "Check BinaryPriorityQueue generic");
- for (int num = 0; num < numOfLoops; num++)
- {
- BinaryPriorityQueue<int> testBPQ = new BinaryPriorityQueue<int>();
- for (int addNum = 0; addNum < numOfAdds; addNum++)
- {
- //Profiler.Add("push");
- testBPQ.Push(num + addNum);
- //Profiler.Add("pop");
- if (addNum > 10)
- bigNum2 += testBPQ.Pop();
- //Profiler.Add("rest");
- } // for (addNum, <, ++)
-
- //if (num == 0)
- // Console.WriteLine(ArrayHelper.Write(testBPQ));
- } // for (num, <, ++)
- Profiler.EndSection();
-
- Profiler.BeginSection("Test rest", "Check PriorityQueue");
- for (int num = 0; num < numOfLoops; num++)
- {
- PriorityQueue testPQ = new PriorityQueue();
- for (int addNum = 0; addNum < numOfAdds; addNum++)
- {
- testPQ.Push(new PriorityQueue.PriorityQueueTests.
- TestPriorityQueuePointData(
- new Point(10, 10), num + addNum));
- if (addNum > 10)
- bigNum3 += (int)testPQ.Pop().Priority;
- } // for (addNum, <, ++)
- } // for (num, <, ++)
- //long profileEnd = Win32Function.GetPerformanceCounter();
-
- Profiler.Add("Check PriorityQueuePoint");
- for (int num = 0; num < numOfLoops; num++)
- {
- PriorityQueuePoint testPQP = new PriorityQueuePoint();
- for (int addNum = 0; addNum < numOfAdds; addNum++)
- {
- testPQP.Push(new PriorityQueuePointData(
- new Point(10, 10), num + addNum));
- if (addNum > 10)
- bigNum4 += (int)testPQP.Pop().Priority;
- } // for (addNum, <, ++)
- } // for (num, <, ++)
-
- Profiler.Add("Check PriorityQueue generics");
- for (int num = 0; num < numOfLoops; num++)
- {
- Delta.Utilities.Collections.PriorityQueue<PriorityQueuePointData> testPQG =
- new Delta.Utilities.Collections.PriorityQueue<PriorityQueuePointData>();
- for (int addNum = 0; addNum < numOfAdds; addNum++)
- {
- testPQG.Push(new PriorityQueuePointData(
- new Point(10, 10), num + addNum));
- if (addNum > 10)
- bigNum5 += (int)testPQG.Pop().Priority;
- } // for (addNum, <, ++)
- } // for (num, <, ++)
-
- Profiler.EndSection();
-
- Console.WriteLine(Profiler.GetProfilerText());
- } // for (int)
-
- Console.WriteLine("bigNum for BinaryPriorityQueue=" + bigNum1);// +
- Console.WriteLine("bigNum for BinaryPriorityQueue generic=" + bigNum2);
- Console.WriteLine("bigNum for PriorityQueue=" + bigNum3);// +
- Console.WriteLine("bigNum for PriorityQueuePoint=" + bigNum4);// +
- Console.WriteLine("bigNum for PriorityQueue with generics=" + bigNum5);
- */
- }
- #endregion
-
- #region TestPriorityQueue
- /// <summary>
- /// Test PriorityQueue
- /// </summary>
- [Test]
- public void TestPriorityQueue()
- {
- PriorityQueue<TestPriorityQueuePointData> pq =
- new PriorityQueue<TestPriorityQueuePointData>();
-
- pq.Push(new TestPriorityQueuePointData(new Point(0, 0), 1));
- pq.Push(new TestPriorityQueuePointData(new Point(5, 3), 3));
- pq.Push(new TestPriorityQueuePointData(new Point(40, 10), 30));
- pq.Push(new TestPriorityQueuePointData(new Point(5, 5), 5));
- Assert.Equal(1, pq.Pop().Priority);
- Assert.Equal(3, pq.Pop().Priority);
- Assert.Equal(5, pq.Pop().Priority);
- Assert.Equal(30, pq.Pop().Priority);
- }
- #endregion
- }
- }