PageRenderTime 40ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

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