/Aurora/Framework/Utilities/MinHeap.cs

https://bitbucket.org/VirtualReality/software-testing · C# · 443 lines · 349 code · 68 blank · 26 comment · 73 complexity · 04d1aca88a19d291747ca87ba3e0c484 MD5 · raw file

  1. /*
  2. * Copyright (c) Contributors, http://aurora-sim.org/, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the Aurora-Sim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Runtime.InteropServices;
  31. using System.Threading;
  32. namespace Aurora.Framework.Utilities
  33. {
  34. public interface IHandle
  35. {
  36. }
  37. [Serializable, ComVisible(false)]
  38. public class MinHeap<T> : ICollection<T>, ICollection
  39. {
  40. public const int DEFAULT_CAPACITY = 4;
  41. private readonly Comparison<T> comparison;
  42. private HeapItem[] items;
  43. private int size;
  44. private object sync_root;
  45. private int version;
  46. public MinHeap() : this(DEFAULT_CAPACITY, Comparer<T>.Default)
  47. {
  48. }
  49. public MinHeap(int capacity) : this(capacity, Comparer<T>.Default)
  50. {
  51. }
  52. public MinHeap(IComparer<T> comparer) : this(DEFAULT_CAPACITY, comparer)
  53. {
  54. }
  55. public MinHeap(int capacity, IComparer<T> comparer) :
  56. this(capacity, comparer.Compare)
  57. {
  58. }
  59. public MinHeap(Comparison<T> comparison) : this(DEFAULT_CAPACITY, comparison)
  60. {
  61. }
  62. public MinHeap(int capacity, Comparison<T> comparison)
  63. {
  64. this.items = new HeapItem[capacity];
  65. this.comparison = comparison;
  66. this.size = this.version = 0;
  67. }
  68. public T this[IHandle key]
  69. {
  70. get
  71. {
  72. Handle handle = ValidateThisHandle(key);
  73. return this.items[handle.index].value;
  74. }
  75. set
  76. {
  77. Handle handle = ValidateThisHandle(key);
  78. this.items[handle.index].value = value;
  79. if (!BubbleUp(handle.index))
  80. BubbleDown(handle.index);
  81. }
  82. }
  83. #region ICollection Members
  84. public bool IsSynchronized
  85. {
  86. get { return false; }
  87. }
  88. public object SyncRoot
  89. {
  90. get
  91. {
  92. if (this.sync_root == null)
  93. Interlocked.CompareExchange<object>(ref this.sync_root, new object(), null);
  94. return this.sync_root;
  95. }
  96. }
  97. public void CopyTo(Array array, int index)
  98. {
  99. if (array == null)
  100. throw new ArgumentNullException("array");
  101. if (array.Rank != 1)
  102. throw new ArgumentException("Multidimensional array not supported");
  103. if (array.GetLowerBound(0) != 0)
  104. throw new ArgumentException("Non-zero lower bound array not supported");
  105. int length = array.Length;
  106. if ((index < 0) || (index > length))
  107. throw new ArgumentOutOfRangeException("index");
  108. if ((length - index) < this.size)
  109. throw new ArgumentException("Not enough space available in array starting at index");
  110. try
  111. {
  112. for (int i = 0; i < this.size; ++i)
  113. array.SetValue(this.items[i].value, index + i);
  114. }
  115. catch (ArrayTypeMismatchException)
  116. {
  117. throw new ArgumentException("Invalid array type");
  118. }
  119. }
  120. #endregion
  121. #region ICollection<T> Members
  122. public int Count
  123. {
  124. get { return this.size; }
  125. }
  126. public bool IsReadOnly
  127. {
  128. get { return false; }
  129. }
  130. public void Add(T value)
  131. {
  132. Add(value, null);
  133. }
  134. public void Clear()
  135. {
  136. for (int index = 0; index < this.size; ++index)
  137. this.items[index].Clear();
  138. this.size = 0;
  139. ++this.version;
  140. }
  141. public bool Contains(T value)
  142. {
  143. return GetIndex(value) != -1;
  144. }
  145. public bool Remove(T value)
  146. {
  147. int index = GetIndex(value);
  148. if (index != -1)
  149. {
  150. RemoveAt(index);
  151. return true;
  152. }
  153. return false;
  154. }
  155. public void CopyTo(T[] array, int index)
  156. {
  157. if (array == null)
  158. throw new ArgumentNullException("array");
  159. if (array.Rank != 1)
  160. throw new ArgumentException("Multidimensional array not supported");
  161. if (array.GetLowerBound(0) != 0)
  162. throw new ArgumentException("Non-zero lower bound array not supported");
  163. int length = array.Length;
  164. if ((index < 0) || (index > length))
  165. throw new ArgumentOutOfRangeException("index");
  166. if ((length - index) < this.size)
  167. throw new ArgumentException("Not enough space available in array starting at index");
  168. for (int i = 0; i < this.size; ++i)
  169. array[index + i] = this.items[i].value;
  170. }
  171. public IEnumerator<T> GetEnumerator()
  172. {
  173. int version2 = this.version;
  174. for (int index = 0; index < this.size; ++index)
  175. {
  176. if (version2 != this.version)
  177. throw new InvalidOperationException("Heap was modified while enumerating");
  178. yield return this.items[index].value;
  179. }
  180. }
  181. IEnumerator IEnumerable.GetEnumerator()
  182. {
  183. return GetEnumerator();
  184. }
  185. #endregion
  186. private Handle ValidateHandle(IHandle ihandle)
  187. {
  188. if (ihandle == null)
  189. throw new ArgumentNullException("handle");
  190. Handle handle = ihandle as Handle;
  191. if (handle == null)
  192. throw new InvalidOperationException("handle is not valid");
  193. return handle;
  194. }
  195. private Handle ValidateThisHandle(IHandle ihandle)
  196. {
  197. Handle handle = ValidateHandle(ihandle);
  198. if (!ReferenceEquals(handle.heap, this))
  199. throw new InvalidOperationException("handle is not valid for this heap");
  200. if (handle.index < 0)
  201. throw new InvalidOperationException("handle is not associated to a value");
  202. return handle;
  203. }
  204. private void Set(HeapItem item, int index)
  205. {
  206. this.items[index] = item;
  207. if (item.handle != null)
  208. item.handle.index = index;
  209. }
  210. private bool BubbleUp(int index)
  211. {
  212. HeapItem item = this.items[index];
  213. int current, parent;
  214. for (current = index, parent = (current - 1)/2;
  215. (current > 0) && (this.comparison(this.items[parent].value, item.value)) > 0;
  216. current = parent, parent = (current - 1)/2)
  217. {
  218. Set(this.items[parent], current);
  219. }
  220. if (current != index)
  221. {
  222. Set(item, current);
  223. ++this.version;
  224. return true;
  225. }
  226. return false;
  227. }
  228. private void BubbleDown(int index)
  229. {
  230. HeapItem item = this.items[index];
  231. int current, child;
  232. for (current = index, child = (2*current) + 1;
  233. current < this.size/2;
  234. current = child, child = (2*current) + 1)
  235. {
  236. if ((child < this.size - 1) && this.comparison(this.items[child].value, this.items[child + 1].value) > 0)
  237. ++child;
  238. if (this.comparison(this.items[child].value, item.value) >= 0)
  239. break;
  240. Set(this.items[child], current);
  241. }
  242. if (current != index)
  243. {
  244. Set(item, current);
  245. ++this.version;
  246. }
  247. }
  248. public bool TryGetValue(IHandle key, out T value)
  249. {
  250. Handle handle = ValidateHandle(key);
  251. if (handle.index > -1)
  252. {
  253. value = this.items[handle.index].value;
  254. return true;
  255. }
  256. value = default(T);
  257. return false;
  258. }
  259. public bool ContainsHandle(IHandle ihandle)
  260. {
  261. Handle handle = ValidateHandle(ihandle);
  262. return ReferenceEquals(handle.heap, this) && handle.index > -1;
  263. }
  264. public void Add(T value, ref IHandle handle)
  265. {
  266. if (handle == null)
  267. handle = new Handle();
  268. Add(value, handle);
  269. }
  270. public void Add(T value, IHandle ihandle)
  271. {
  272. if (this.size == this.items.Length)
  273. {
  274. int capacity = (int) ((this.items.Length*200L)/100L);
  275. if (capacity < (this.items.Length + DEFAULT_CAPACITY))
  276. capacity = this.items.Length + DEFAULT_CAPACITY;
  277. Array.Resize(ref this.items, capacity);
  278. }
  279. Handle handle = null;
  280. if (ihandle != null)
  281. {
  282. handle = ValidateHandle(ihandle);
  283. handle.heap = this;
  284. }
  285. HeapItem item = new HeapItem(value, handle);
  286. Set(item, this.size);
  287. BubbleUp(this.size++);
  288. }
  289. public T Min()
  290. {
  291. if (this.size == 0)
  292. throw new InvalidOperationException("Heap is empty");
  293. return this.items[0].value;
  294. }
  295. public void TrimExcess()
  296. {
  297. int length = (int) (this.items.Length*0.9);
  298. if (this.size < length)
  299. Array.Resize(ref this.items, Math.Min(this.size, DEFAULT_CAPACITY));
  300. }
  301. private void RemoveAt(int index)
  302. {
  303. if (this.size == 0)
  304. throw new InvalidOperationException("Heap is empty");
  305. if (index >= this.size)
  306. throw new ArgumentOutOfRangeException("index");
  307. this.items[index].Clear();
  308. if (--this.size > 0 && index != this.size)
  309. {
  310. Set(this.items[this.size], index);
  311. if (!BubbleUp(index))
  312. BubbleDown(index);
  313. }
  314. }
  315. public T RemoveMin()
  316. {
  317. if (this.size == 0)
  318. throw new InvalidOperationException("Heap is empty");
  319. HeapItem item = this.items[0];
  320. RemoveAt(0);
  321. return item.value;
  322. }
  323. public T Remove(IHandle ihandle)
  324. {
  325. Handle handle = ValidateThisHandle(ihandle);
  326. HeapItem item = this.items[handle.index];
  327. RemoveAt(handle.index);
  328. return item.value;
  329. }
  330. private int GetIndex(T value)
  331. {
  332. EqualityComparer<T> comparer = EqualityComparer<T>.Default;
  333. int index;
  334. for (index = 0; index < this.size; ++index)
  335. {
  336. if (comparer.Equals(this.items[index].value, value))
  337. return index;
  338. }
  339. return -1;
  340. }
  341. #region Nested type: Handle
  342. private class Handle : IHandle
  343. {
  344. internal MinHeap<T> heap;
  345. internal int index = -1;
  346. internal void Clear()
  347. {
  348. this.index = -1;
  349. this.heap = null;
  350. }
  351. }
  352. #endregion
  353. #region Nested type: HeapItem
  354. private struct HeapItem
  355. {
  356. internal Handle handle;
  357. internal T value;
  358. internal HeapItem(T value, Handle handle)
  359. {
  360. this.value = value;
  361. this.handle = handle;
  362. }
  363. internal void Clear()
  364. {
  365. this.value = default(T);
  366. if (this.handle != null)
  367. {
  368. this.handle.Clear();
  369. this.handle = null;
  370. }
  371. }
  372. }
  373. #endregion
  374. }
  375. }