#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2013 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Collections;
namespace Nuclex.Support.Collections {
/// Queue that dequeues items in order of their priority
public class PriorityQueue : ICollection, IEnumerable {
#region class Enumerator
/// Enumerates all items contained in a priority queue
private class Enumerator : IEnumerator {
/// Initializes a new priority queue enumerator
/// Priority queue to be enumerated
public Enumerator(PriorityQueue priorityQueue) {
this.priorityQueue = priorityQueue;
Reset();
}
/// Resets the enumerator to its initial state
public void Reset() {
this.index = -1;
#if DEBUG
this.expectedVersion = this.priorityQueue.version;
#endif
}
/// The current item being enumerated
TItem IEnumerator.Current {
get {
#if DEBUG
checkVersion();
#endif
return this.priorityQueue.heap[index];
}
}
/// Moves to the next item in the priority queue
/// True if a next item was found, false if the end has been reached
public bool MoveNext() {
#if DEBUG
checkVersion();
#endif
if(this.index + 1 == this.priorityQueue.count)
return false;
++this.index;
return true;
}
/// Releases all resources used by the enumerator
public void Dispose() { }
#if DEBUG
/// Ensures that the priority queue has not changed
private void checkVersion() {
if(this.expectedVersion != this.priorityQueue.version)
throw new InvalidOperationException("Priority queue has been modified");
}
#endif
/// The current item being enumerated
object IEnumerator.Current {
get {
#if DEBUG
checkVersion();
#endif
return this.priorityQueue.heap[index];
}
}
/// Index of the current item in the priority queue
private int index;
/// The priority queue whose items this instance enumerates
private PriorityQueue priorityQueue;
#if DEBUG
/// Expected version of the priority queue
private int expectedVersion;
#endif
}
#endregion // class Enumerator
///
/// Initializes a new priority queue using IComparable for comparing items
///
public PriorityQueue() : this(Comparer.Default) { }
/// Initializes a new priority queue
/// Comparer to use for ordering the items
public PriorityQueue(IComparer comparer) {
this.comparer = comparer;
this.capacity = 15; // 15 is equal to 4 complete levels
this.heap = new TItem[this.capacity];
}
/// Returns the topmost item in the queue without dequeueing it
/// The topmost item in the queue
public TItem Peek() {
if(this.count == 0) {
throw new InvalidOperationException("No items queued");
}
return this.heap[0];
}
/// Takes the item with the highest priority off from the queue
/// The item with the highest priority in the list
/// When the queue is empty
public TItem Dequeue() {
if(this.count == 0) {
throw new InvalidOperationException("No items available to dequeue");
}
TItem result = this.heap[0];
--this.count;
trickleDown(0, this.heap[this.count]);
#if DEBUG
++this.version;
#endif
return result;
}
/// Puts an item into the priority queue
/// Item to be queued
public void Enqueue(TItem item) {
if(this.count == capacity)
growHeap();
++this.count;
bubbleUp(this.count - 1, item);
#if DEBUG
++this.version;
#endif
}
/// Removes all items from the priority queue
public void Clear() {
this.count = 0;
#if DEBUG
++this.version;
#endif
}
/// Total number of items in the priority queue
public int Count {
get { return this.count; }
}
/// Copies the contents of the priority queue into an array
/// Array to copy the priority queue into
/// Starting index for the destination array
public void CopyTo(Array array, int index) {
Array.Copy(this.heap, 0, array, index, this.count);
}
///
/// Obtains an object that can be used to synchronize accesses to the priority queue
/// from different threads
///
public object SyncRoot {
get { return this; }
}
/// Whether operations performed on this priority queue are thread safe
public bool IsSynchronized {
get { return false; }
}
/// Returns a typesafe enumerator for the priority queue
/// A new enumerator for the priority queue
public IEnumerator GetEnumerator() {
return new Enumerator(this);
}
/// Moves an item upwards in the heap tree
/// Index of the item to be moved
/// Item to be moved
private void bubbleUp(int index, TItem item) {
int parent = getParent(index);
// Note: (index > 0) means there is a parent
while((index > 0) && (this.comparer.Compare(this.heap[parent], item) < 0)) {
this.heap[index] = this.heap[parent];
index = parent;
parent = getParent(index);
}
this.heap[index] = item;
}
/// Move the item downwards in the heap tree
/// Index of the item to be moved
/// Item to be moved
private void trickleDown(int index, TItem item) {
int child = getLeftChild(index);
while(child < this.count) {
bool needsToBeMoved =
((child + 1) < this.count) &&
(this.comparer.Compare(heap[child], this.heap[child + 1]) < 0);
if(needsToBeMoved)
++child;
this.heap[index] = this.heap[child];
index = child;
child = getLeftChild(index);
}
bubbleUp(index, item);
}
/// Obtains the left child item in the heap tree
/// Index of the item whose left child to return
/// The left child item of the provided parent item
private int getLeftChild(int index) {
return (index * 2) + 1;
}
/// Calculates the parent entry of the item on the heap
/// Index of the item whose parent to calculate
/// The index of the parent to the specified item
private int getParent(int index) {
return (index - 1) / 2;
}
/// Increases the size of the priority collection's heap
private void growHeap() {
this.capacity = (capacity * 2) + 1;
TItem[] newHeap = new TItem[this.capacity];
Array.Copy(this.heap, 0, newHeap, 0, this.count);
this.heap = newHeap;
}
/// Returns an enumerator for the priority queue
/// A new enumerator for the priority queue
IEnumerator IEnumerable.GetEnumerator() {
return new Enumerator(this);
}
/// Comparer used to order the items in the priority queue
private IComparer comparer;
/// Total number of items in the priority queue
private int count;
/// Available space in the priority queue
private int capacity;
/// Tree containing the items in the priority queue
private TItem[] heap;
#if DEBUG
/// Incremented whenever the priority queue is modified
private int version;
#endif
}
} // namespace Nuclex.Support.Collections