/Aurora/Framework/Utilities/MinHeap.cs
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 28using System; 29using System.Collections; 30using System.Collections.Generic; 31using System.Runtime.InteropServices; 32using System.Threading; 33 34namespace Aurora.Framework.Utilities 35{ 36 public interface IHandle 37 { 38 } 39 40 [Serializable, ComVisible(false)] 41 public class MinHeap<T> : ICollection<T>, ICollection 42 { 43 public const int DEFAULT_CAPACITY = 4; 44 private readonly Comparison<T> comparison; 45 46 private HeapItem[] items; 47 private int size; 48 private object sync_root; 49 private int version; 50 51 public MinHeap() : this(DEFAULT_CAPACITY, Comparer<T>.Default) 52 { 53 } 54 55 public MinHeap(int capacity) : this(capacity, Comparer<T>.Default) 56 { 57 } 58 59 public MinHeap(IComparer<T> comparer) : this(DEFAULT_CAPACITY, comparer) 60 { 61 } 62 63 public MinHeap(int capacity, IComparer<T> comparer) : 64 this(capacity, comparer.Compare) 65 { 66 } 67 68 public MinHeap(Comparison<T> comparison) : this(DEFAULT_CAPACITY, comparison) 69 { 70 } 71 72 public MinHeap(int capacity, Comparison<T> comparison) 73 { 74 this.items = new HeapItem[capacity]; 75 this.comparison = comparison; 76 this.size = this.version = 0; 77 } 78 79 public T this[IHandle key] 80 { 81 get 82 { 83 Handle handle = ValidateThisHandle(key); 84 return this.items[handle.index].value; 85 } 86 87 set 88 { 89 Handle handle = ValidateThisHandle(key); 90 this.items[handle.index].value = value; 91 if (!BubbleUp(handle.index)) 92 BubbleDown(handle.index); 93 } 94 } 95 96 #region ICollection Members 97 98 public bool IsSynchronized 99 { 100 get { return false; } 101 } 102 103 public object SyncRoot 104 { 105 get 106 { 107 if (this.sync_root == null) 108 Interlocked.CompareExchange<object>(ref this.sync_root, new object(), null); 109 return this.sync_root; 110 } 111 } 112 113 public void CopyTo(Array array, int index) 114 { 115 if (array == null) 116 throw new ArgumentNullException("array"); 117 if (array.Rank != 1) 118 throw new ArgumentException("Multidimensional array not supported"); 119 if (array.GetLowerBound(0) != 0) 120 throw new ArgumentException("Non-zero lower bound array not supported"); 121 122 int length = array.Length; 123 if ((index < 0) || (index > length)) 124 throw new ArgumentOutOfRangeException("index"); 125 if ((length - index) < this.size) 126 throw new ArgumentException("Not enough space available in array starting at index"); 127 128 try 129 { 130 for (int i = 0; i < this.size; ++i) 131 array.SetValue(this.items[i].value, index + i); 132 } 133 catch (ArrayTypeMismatchException) 134 { 135 throw new ArgumentException("Invalid array type"); 136 } 137 } 138 139 #endregion 140 141 #region ICollection<T> Members 142 143 public int Count 144 { 145 get { return this.size; } 146 } 147 148 public bool IsReadOnly 149 { 150 get { return false; } 151 } 152 153 public void Add(T value) 154 { 155 Add(value, null); 156 } 157 158 public void Clear() 159 { 160 for (int index = 0; index < this.size; ++index) 161 this.items[index].Clear(); 162 this.size = 0; 163 ++this.version; 164 } 165 166 public bool Contains(T value) 167 { 168 return GetIndex(value) != -1; 169 } 170 171 public bool Remove(T value) 172 { 173 int index = GetIndex(value); 174 if (index != -1) 175 { 176 RemoveAt(index); 177 return true; 178 } 179 return false; 180 } 181 182 public void CopyTo(T[] array, int index) 183 { 184 if (array == null) 185 throw new ArgumentNullException("array"); 186 if (array.Rank != 1) 187 throw new ArgumentException("Multidimensional array not supported"); 188 if (array.GetLowerBound(0) != 0) 189 throw new ArgumentException("Non-zero lower bound array not supported"); 190 191 int length = array.Length; 192 if ((index < 0) || (index > length)) 193 throw new ArgumentOutOfRangeException("index"); 194 if ((length - index) < this.size) 195 throw new ArgumentException("Not enough space available in array starting at index"); 196 197 for (int i = 0; i < this.size; ++i) 198 array[index + i] = this.items[i].value; 199 } 200 201 public IEnumerator<T> GetEnumerator() 202 { 203 int version2 = this.version; 204 205 for (int index = 0; index < this.size; ++index) 206 { 207 if (version2 != this.version) 208 throw new InvalidOperationException("Heap was modified while enumerating"); 209 yield return this.items[index].value; 210 } 211 } 212 213 IEnumerator IEnumerable.GetEnumerator() 214 { 215 return GetEnumerator(); 216 } 217 218 #endregion 219 220 private Handle ValidateHandle(IHandle ihandle) 221 { 222 if (ihandle == null) 223 throw new ArgumentNullException("handle"); 224 Handle handle = ihandle as Handle; 225 if (handle == null) 226 throw new InvalidOperationException("handle is not valid"); 227 return handle; 228 } 229 230 private Handle ValidateThisHandle(IHandle ihandle) 231 { 232 Handle handle = ValidateHandle(ihandle); 233 if (!ReferenceEquals(handle.heap, this)) 234 throw new InvalidOperationException("handle is not valid for this heap"); 235 if (handle.index < 0) 236 throw new InvalidOperationException("handle is not associated to a value"); 237 return handle; 238 } 239 240 private void Set(HeapItem item, int index) 241 { 242 this.items[index] = item; 243 if (item.handle != null) 244 item.handle.index = index; 245 } 246 247 private bool BubbleUp(int index) 248 { 249 HeapItem item = this.items[index]; 250 int current, parent; 251 252 for (current = index, parent = (current - 1)/2; 253 (current > 0) && (this.comparison(this.items[parent].value, item.value)) > 0; 254 current = parent, parent = (current - 1)/2) 255 { 256 Set(this.items[parent], current); 257 } 258 259 if (current != index) 260 { 261 Set(item, current); 262 ++this.version; 263 return true; 264 } 265 return false; 266 } 267 268 private void BubbleDown(int index) 269 { 270 HeapItem item = this.items[index]; 271 int current, child; 272 273 for (current = index, child = (2*current) + 1; 274 current < this.size/2; 275 current = child, child = (2*current) + 1) 276 { 277 if ((child < this.size - 1) && this.comparison(this.items[child].value, this.items[child + 1].value) > 0) 278 ++child; 279 if (this.comparison(this.items[child].value, item.value) >= 0) 280 break; 281 Set(this.items[child], current); 282 } 283 284 if (current != index) 285 { 286 Set(item, current); 287 ++this.version; 288 } 289 } 290 291 public bool TryGetValue(IHandle key, out T value) 292 { 293 Handle handle = ValidateHandle(key); 294 if (handle.index > -1) 295 { 296 value = this.items[handle.index].value; 297 return true; 298 } 299 value = default(T); 300 return false; 301 } 302 303 public bool ContainsHandle(IHandle ihandle) 304 { 305 Handle handle = ValidateHandle(ihandle); 306 return ReferenceEquals(handle.heap, this) && handle.index > -1; 307 } 308 309 public void Add(T value, ref IHandle handle) 310 { 311 if (handle == null) 312 handle = new Handle(); 313 Add(value, handle); 314 } 315 316 public void Add(T value, IHandle ihandle) 317 { 318 if (this.size == this.items.Length) 319 { 320 int capacity = (int) ((this.items.Length*200L)/100L); 321 if (capacity < (this.items.Length + DEFAULT_CAPACITY)) 322 capacity = this.items.Length + DEFAULT_CAPACITY; 323 Array.Resize(ref this.items, capacity); 324 } 325 326 Handle handle = null; 327 if (ihandle != null) 328 { 329 handle = ValidateHandle(ihandle); 330 handle.heap = this; 331 } 332 333 HeapItem item = new HeapItem(value, handle); 334 335 Set(item, this.size); 336 BubbleUp(this.size++); 337 } 338 339 public T Min() 340 { 341 if (this.size == 0) 342 throw new InvalidOperationException("Heap is empty"); 343 344 return this.items[0].value; 345 } 346 347 public void TrimExcess() 348 { 349 int length = (int) (this.items.Length*0.9); 350 if (this.size < length) 351 Array.Resize(ref this.items, Math.Min(this.size, DEFAULT_CAPACITY)); 352 } 353 354 private void RemoveAt(int index) 355 { 356 if (this.size == 0) 357 throw new InvalidOperationException("Heap is empty"); 358 if (index >= this.size) 359 throw new ArgumentOutOfRangeException("index"); 360 361 this.items[index].Clear(); 362 if (--this.size > 0 && index != this.size) 363 { 364 Set(this.items[this.size], index); 365 if (!BubbleUp(index)) 366 BubbleDown(index); 367 } 368 } 369 370 public T RemoveMin() 371 { 372 if (this.size == 0) 373 throw new InvalidOperationException("Heap is empty"); 374 375 HeapItem item = this.items[0]; 376 RemoveAt(0); 377 return item.value; 378 } 379 380 public T Remove(IHandle ihandle) 381 { 382 Handle handle = ValidateThisHandle(ihandle); 383 HeapItem item = this.items[handle.index]; 384 RemoveAt(handle.index); 385 return item.value; 386 } 387 388 private int GetIndex(T value) 389 { 390 EqualityComparer<T> comparer = EqualityComparer<T>.Default; 391 int index; 392 393 for (index = 0; index < this.size; ++index) 394 { 395 if (comparer.Equals(this.items[index].value, value)) 396 return index; 397 } 398 return -1; 399 } 400 401 #region Nested type: Handle 402 403 private class Handle : IHandle 404 { 405 internal MinHeap<T> heap; 406 internal int index = -1; 407 408 internal void Clear() 409 { 410 this.index = -1; 411 this.heap = null; 412 } 413 } 414 415 #endregion 416 417 #region Nested type: HeapItem 418 419 private struct HeapItem 420 { 421 internal Handle handle; 422 internal T value; 423 424 internal HeapItem(T value, Handle handle) 425 { 426 this.value = value; 427 this.handle = handle; 428 } 429 430 internal void Clear() 431 { 432 this.value = default(T); 433 if (this.handle != null) 434 { 435 this.handle.Clear(); 436 this.handle = null; 437 } 438 } 439 } 440 441 #endregion 442 } 443}