Fully implemented the priority collection; Created a wrapper for non-intrusive priority collections that keep the priority external to the object; wrote unit tests for both

git-svn-id: file:///srv/devel/repo-conversion/nusu@4 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2007-03-05 18:22:31 +00:00
parent d339e00bce
commit d813756eed
6 changed files with 524 additions and 385 deletions

View File

@ -65,6 +65,10 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Source\Collections\UnintrusivePriorityQueue.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>UnintrusivePriorityQueue</Name>
</Compile>
<Compile Include="Source\Collections\Parentable.cs"> <Compile Include="Source\Collections\Parentable.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline> <XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>Parentable</Name> <Name>Parentable</Name>
@ -77,6 +81,11 @@
<XNAUseContentPipeline>false</XNAUseContentPipeline> <XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>PriorityQueue</Name> <Name>PriorityQueue</Name>
</Compile> </Compile>
<Compile Include="Source\Collections\PriorityQueue.Test.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>PriorityQueue.Test</Name>
<DependentUpon>PriorityQueue.cs</DependentUpon>
</Compile>
<Compile Include="Source\Collections\RingMemoryStream.cs"> <Compile Include="Source\Collections\RingMemoryStream.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline> <XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>RingMemoryStream</Name> <Name>RingMemoryStream</Name>
@ -87,6 +96,11 @@
<Name>RingMemoryStream.Test</Name> <Name>RingMemoryStream.Test</Name>
<DependentUpon>RingMemoryStream.cs</DependentUpon> <DependentUpon>RingMemoryStream.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Collections\UnintrusivePriorityQueue.Test.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>UnintrusivePriorityQueue.Test</Name>
<DependentUpon>UnintrusivePriorityQueue.cs</DependentUpon>
</Compile>
<Compile Include="Source\Serialization\BinarySerializer.Test.cs"> <Compile Include="Source\Serialization\BinarySerializer.Test.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline> <XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>BinarySerializer.Test</Name> <Name>BinarySerializer.Test</Name>

View File

@ -14,15 +14,10 @@ namespace Nuclex.Support.Collections {
/// </remarks> /// </remarks>
/// <typeparam name="ParentType">Type of the parent object to assign to items</typeparam> /// <typeparam name="ParentType">Type of the parent object to assign to items</typeparam>
/// <typeparam name="ItemType">Type of the items being managed in the collection</typeparam> /// <typeparam name="ItemType">Type of the items being managed in the collection</typeparam>
public class ParentingCollection<ParentType, ItemType> : Collection<ItemType>, IDisposable public class ParentingCollection<ParentType, ItemType> : Collection<ItemType>
where ItemType : Parentable<ParentType> where ItemType : Parentable<ParentType>
where ParentType : class { where ParentType : class {
/// <summary>Called when the object is garbage-collected</summary>
~ParentingCollection() {
Dispose(false); // called from GC
}
/// <summary>Reparents all elements in the collection</summary> /// <summary>Reparents all elements in the collection</summary>
/// <param name="parent">New parent to take ownership of the items</param> /// <param name="parent">New parent to take ownership of the items</param>
protected void Reparent(ParentType parent) { protected void Reparent(ParentType parent) {
@ -32,37 +27,6 @@ namespace Nuclex.Support.Collections {
base[index].SetParent(parent); base[index].SetParent(parent);
} }
/// <summary>Called when the asset needs to release its resources</summary>
/// <param name="calledByUser">
/// Whether the mehod has been called from user code. If this argument
/// is false, the object is being disposed by the garbage collector and
/// it mustn't access other objects (including the attempt to Dispose() them)
/// as these might have already been destroyed by the GC.
/// </param>
protected virtual void Dispose(bool calledByUser) {
// Only destroy the other resources when we're not being called from
// the garbage collector, otherwise we'd risk accessing objects that
// have already been disposed
if(calledByUser) {
// Have the items do their cleanup work
Reparent(null);
// Dispose of all the items in the collection
foreach(ItemType item in this) {
IDisposable disposable = item as IDisposable;
if(disposable != null)
disposable.Dispose();
}
// Remove all items from the collection
base.ClearItems();
}
}
/// <summary>Clears all elements from the collection</summary> /// <summary>Clears all elements from the collection</summary>
protected override void ClearItems() { protected override void ClearItems() {
for(int index = 0; index < Count; ++index) for(int index = 0; index < Count; ++index)
@ -94,9 +58,32 @@ namespace Nuclex.Support.Collections {
item.SetParent(this.parent); item.SetParent(this.parent);
} }
/// <summary>Release all resources owned by the instance explicitely</summary> /// <summary>Disposes the collection and optionally all items contained therein</summary>
public void Dispose() { /// <param name="disposeItems">Whether to try calling Dispose() on all items</param>
Dispose(true); // Called by user /// <remarks>
/// This method is intended to support collections that need to dispose their
/// items. The ParentingCollection will first detach all items from the parent
/// object and them call Dispose() on any item that implements IDisposable.
/// </remarks>
protected void InternalDispose(bool disposeItems) {
if(disposeItems) {
// Have the items do their cleanup work
Reparent(null);
// Dispose of all the items in the collection that implement IDisposable
foreach(ItemType item in this) {
IDisposable disposable = item as IDisposable;
if(disposable != null)
disposable.Dispose();
}
}
// Remove all items from the collection
base.ClearItems();
} }
/// <summary>Parent this collection currently belongs to</summary> /// <summary>Parent this collection currently belongs to</summary>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the priority queue class</summary>
[TestFixture]
public class PriorityQueueTest {
private class FloatComparer : IComparer<float> {
public int Compare(float left, float right) {
return Math.Sign(left - right);
}
public static readonly FloatComparer Default = new FloatComparer();
}
/// <summary>Tests to ensure the count property is properly updated</summary>
[Test]
public void TestCount() {
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
Assert.AreEqual(0, testQueue.Count);
testQueue.Enqueue(12.34f);
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(56.78f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Dequeue();
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(9.0f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Clear();
Assert.AreEqual(0, testQueue.Count);
}
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
[Test]
public void TestOrdering() {
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
testQueue.Enqueue(1.0f);
testQueue.Enqueue(9.0f);
testQueue.Enqueue(2.0f);
testQueue.Enqueue(8.0f);
testQueue.Enqueue(3.0f);
testQueue.Enqueue(7.0f);
testQueue.Enqueue(4.0f);
testQueue.Enqueue(6.0f);
testQueue.Enqueue(5.0f);
Assert.AreEqual(9.0f, testQueue.Dequeue());
Assert.AreEqual(8.0f, testQueue.Dequeue());
Assert.AreEqual(7.0f, testQueue.Dequeue());
Assert.AreEqual(6.0f, testQueue.Dequeue());
Assert.AreEqual(5.0f, testQueue.Dequeue());
Assert.AreEqual(4.0f, testQueue.Dequeue());
Assert.AreEqual(3.0f, testQueue.Dequeue());
Assert.AreEqual(2.0f, testQueue.Dequeue());
Assert.AreEqual(1.0f, testQueue.Dequeue());
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View File

@ -3,391 +3,224 @@ using System.Collections.Generic;
using System.Collections; using System.Collections;
namespace Nuclex.Support.Collections { namespace Nuclex.Support.Collections {
/*
public class PriorityQueue : ICollection {
private struct HeapEntry { /// <summary>Queue that dequeues items in order of their priority</summary>
public class PriorityQueue<ItemType> : ICollection, IEnumerable<ItemType> {
public HeapEntry(object item, int priority) { #region class Enumerator
this.item = item;
this.priority = priority; /// <summary>Enumerates all items contained in a priority queue</summary>
} private class Enumerator : IEnumerator<ItemType> {
public object Item {
get { return item; } /// <summary>Initializes a new priority queue enumerator</summary>
} /// <param name="priorityQueue">Priority queue to be enumerated</param>
public int Priority { public Enumerator(PriorityQueue<ItemType> priorityQueue) {
get { return priority; } this.priorityQueue = priorityQueue;
Reset();
} }
private object item; /// <summary>Resets the enumerator to its initial state</summary>
private int priority; public void Reset() {
index = -1;
version = priorityQueue.version;
}
/// <summary>The current item being enumerated</summary>
ItemType IEnumerator<ItemType>.Current {
get {
checkVersion();
return priorityQueue.heap[index];
}
}
/// <summary>Moves to the next item in the priority queue</summary>
/// <returns>True if a next item was found, false if the end has been reached</returns>
public bool MoveNext() {
checkVersion();
if(index + 1 == priorityQueue.count)
return false;
++index;
return true;
}
/// <summary>Releases all resources used by the enumerator</summary>
public void Dispose() { }
/// <summary>Ensures that the priority queue has not changed</summary>
private void checkVersion() {
if(version != priorityQueue.version)
throw new InvalidOperationException("Priority queue has been modified");
}
/// <summary>The current item being enumerated</summary>
object IEnumerator.Current {
get {
checkVersion();
return priorityQueue.heap[index];
}
}
/// <summary>Index of the current item in the priority queue</summary>
private int index;
/// <summary>The priority queue whose items this instance enumerates</summary>
private PriorityQueue<ItemType> priorityQueue;
/// <summary>Expected version of the priority queue</summary>
private int version;
} }
private int count; #endregion // class Enumerator
private int capacity;
private int version;
private HeapEntry[] heap;
public PriorityQueue() { /// <summary>Initializes a new priority queue</summary>
/// <param name="comparer">Comparer to use for ordering the items</param>
public PriorityQueue(IComparer<ItemType> comparer) {
this.comparer = comparer;
capacity = 15; // 15 is equal to 4 complete levels capacity = 15; // 15 is equal to 4 complete levels
heap = new HeapEntry[capacity]; heap = new ItemType[capacity];
} }
public object Dequeue() { /// <summary>Takes the item with the highest priority off from the queue</summary>
/// <returns>The item with the highest priority in the list</returns>
public ItemType Dequeue() {
if(count == 0) if(count == 0)
throw new InvalidOperationException(); throw new InvalidOperationException("No items available to dequeue");
object result = heap[0].Item; ItemType result = heap[0];
count--; --count;
trickleDown(0, heap[count]); trickleDown(0, heap[count]);
version++;
++version;
return result; return result;
} }
public void Enqueue(object item, int priority) { /// <summary>Puts an item into the priority queue</summary>
/// <param name="item">Item to be queued</param>
public void Enqueue(ItemType item) {
if(count == capacity) if(count == capacity)
growHeap(); growHeap();
count++;
bubbleUp(count - 1, new HeapEntry(item, priority)); ++count;
version++; bubbleUp(count - 1, item);
++version;
} }
private void bubbleUp(int index, HeapEntry he) { /// <summary>Removes all items from the priority queue</summary>
int parent = getParent(index); public void Clear() {
// note: (index > 0) means there is a parent this.count = 0;
while((index > 0) && (heap[parent].Priority < he.Priority)) { ++version;
heap[index] = heap[parent];
index = parent;
parent = getParent(index);
}
heap[index] = he;
} }
private int getLeftChild(int index) {
return (index * 2) + 1;
}
private int getParent(int index) { /// <summary>Total number of items in the priority queue</summary>
return (index - 1) / 2;
}
private void growHeap() {
capacity = (capacity * 2) + 1;
HeapEntry[] newHeap = new HeapEntry[capacity];
System.Array.Copy(heap, 0, newHeap, 0, count);
heap = newHeap;
}
private void trickleDown(int index, HeapEntry he) {
int child = getLeftChild(index);
while(child < count) {
if(((child + 1) < count) &&
(heap[child].Priority < heap[child + 1].Priority)) {
child++;
}
heap[index] = heap[child];
index = child;
child = getLeftChild(index);
}
bubbleUp(index, he);
}
#region IEnumerable implementation
public IEnumerator GetEnumerator() {
return new PriorityQueueEnumerator(this);
}
#endregion
#region ICollection implementation
public int Count { public int Count {
get { return count; } get { return this.count; }
} }
/// <summary>Copies the contents of the priority queue into an array</summary>
/// <param name="array">Array to copy the priority queue into</param>
/// <param name="index">Starting index for the destination array</param>
public void CopyTo(Array array, int index) { public void CopyTo(Array array, int index) {
System.Array.Copy(heap, 0, array, index, count); Array.Copy(heap, 0, array, index, count);
} }
/// <summary>
/// Obtains an object that can be used to synchronize accesses to the priority queue
/// from different threads
/// </summary>
public object SyncRoot { public object SyncRoot {
get { return this; } get { return this; }
} }
/// <summary>Whether operations performed on this priority queue are thread safe</summary>
public bool IsSynchronized { public bool IsSynchronized {
get { return false; } get { return false; }
} }
#endregion
#region Priority Queue enumerator /// <summary>Returns a typesafe enumerator for the priority queue</summary>
private class PriorityQueueEnumerator : IEnumerator { /// <returns>A new enumerator for the priority queue</returns>
private int index; public IEnumerator<ItemType> GetEnumerator() {
private PriorityQueue pq; return new Enumerator(this);
private int version;
public PriorityQueueEnumerator(PriorityQueue pq) {
this.pq = pq;
Reset();
}
private void checkVersion() {
if(version != pq.version)
throw new InvalidOperationException();
}
#region IEnumerator Members
public void Reset() {
index = -1;
version = pq.version;
}
public object Current {
get {
checkVersion();
return pq.heap[index].Item;
}
}
public bool MoveNext() {
checkVersion();
if(index + 1 == pq.count)
return false;
index++;
return true;
}
#endregion
} }
#endregion
/// <summary>Moves an item upwards in the heap tree</summary>
/// <param name="index">Index of the item to be moved</param>
/// <param name="item">Item to be moved</param>
private void bubbleUp(int index, ItemType item) {
int parent = getParent(index);
// note: (index > 0) means there is a parent
while((index > 0) && (this.comparer.Compare(heap[parent], item) < 0)) {
heap[index] = heap[parent];
index = parent;
parent = getParent(index);
}
heap[index] = item;
}
/// <summary>Moved the item downwards in the heap tree</summary>
/// <param name="index">Index of the item to be moved</param>
/// <param name="item">Item to be moved</param>
private void trickleDown(int index, ItemType item) {
int child = getLeftChild(index);
while(child < count) {
if(((child + 1) < count) && (this.comparer.Compare(heap[child], heap[child + 1]) < 0))
++child;
heap[index] = heap[child];
index = child;
child = getLeftChild(index);
}
bubbleUp(index, item);
}
/// <summary>Obtains the left child item in the heap tree</summary>
/// <param name="index">Index of the item whose left child to return</param>
/// <returns>The left child item of the provided parent item</returns>
private int getLeftChild(int index) {
return (index * 2) + 1;
}
/// <summary>Calculates the parent entry of the item on the heap</summary>
/// <param name="index">Index of the item whose parent to calculate</param>
/// <returns>The index of the parent to the specified item</returns>
private int getParent(int index) {
return (index - 1) / 2;
}
/// <summary>Increases the size of the priority collection's heap</summary>
private void growHeap() {
capacity = (capacity * 2) + 1;
ItemType[] newHeap = new ItemType[capacity];
Array.Copy(heap, 0, newHeap, 0, count);
heap = newHeap;
}
/// <summary>Returns an enumerator for the priority queue</summary>
/// <returns>A new enumerator for the priority queue</returns>
IEnumerator IEnumerable.GetEnumerator() {
return new Enumerator(this);
}
/// <summary>Comparer used to order the items in the priority queue</summary>
private IComparer<ItemType> comparer;
/// <summary>Total number of items in the priority queue</summary>
private int count;
/// <summary>Available space in the priority queue</summary>
private int capacity;
/// <summary>Incremented whenever the priority queue is modified</summary>
private int version;
/// <summary>Tree containing the items in the priority queue</summary>
private ItemType[] heap;
} }
*/
/*
/// <summary>Queue that dequeues items in order of their priority</summary>
public class PriorityQueue<ItemType> : ICollection, IEnumerable<ItemType> {
public static PriorityQueue Syncronized(PriorityQueue<ItemType> P) {
return new PriorityQueue<ItemType>(ArrayList.Synchronized(P.InnerList), P.Comparer, false);
}
public static PriorityQueue<ItemType> ReadOnly(PriorityQueue<ItemType> P) {
return new PriorityQueue(ArrayList.ReadOnly(P.InnerList), P.Comparer, false);
}
public PriorityQueue()
: this(Comparer.Default) { }
public PriorityQueue(int C)
: this(Comparer.Default, C) { }
public PriorityQueue(IComparer c) {
Comparer = c;
}
public PriorityQueue(IComparer c, int Capacity) {
Comparer = c;
InnerList.Capacity = Capacity;
}
protected PriorityQueue(ArrayList Core, IComparer Comp, bool Copy) {
if(Copy)
InnerList = Core.Clone() as ArrayList;
else
InnerList = Core;
Comparer = Comp;
}
protected void SwitchElements(int i, int j) {
object h = InnerList[i];
InnerList[i] = InnerList[j];
InnerList[j] = h;
}
protected virtual int OnCompare(int i, int j) {
return Comparer.Compare(InnerList[i], InnerList[j]);
}
#region public methods
/// <summary>
/// Push an object onto the PQ
/// </summary>
/// <param name="O">The new object</param>
/// <returns>
/// The index in the list where the object is _now_. This will change when
/// objects are taken from or put onto the PQ.
/// </returns>
public int Queue(object O) {
int p = InnerList.Count, p2;
InnerList.Add(O); // E[p] = O
do {
if(p == 0)
break;
p2 = (p - 1) / 2;
if(OnCompare(p, p2) < 0) {
SwitchElements(p, p2);
p = p2;
} else
break;
} while(true);
return p;
}
/// <summary>
/// Get the smallest object and remove it.
/// </summary>
/// <returns>The smallest object</returns>
public object Dequeue() {
object result = InnerList[0];
int p = 0, p1, p2, pn;
InnerList[0] = InnerList[InnerList.Count - 1];
InnerList.RemoveAt(InnerList.Count - 1);
do {
pn = p;
p1 = 2 * p + 1;
p2 = 2 * p + 2;
if(InnerList.Count > p1 && OnCompare(p, p1) > 0) // links kleiner
p = p1;
if(InnerList.Count > p2 && OnCompare(p, p2) > 0) // rechts noch kleiner
p = p2;
if(p == pn)
break;
SwitchElements(p, pn);
} while(true);
return result;
}
/// <summary>
/// Notify the PQ that the object at position i has changed
/// and the PQ needs to restore order.
/// Since you dont have access to any indexes (except by using the
/// explicit IList.this) you should not call this function without knowing exactly
/// what you do.
/// </summary>
/// <param name="i">The index of the changed object.</param>
public void Update(int i) {
int p = i, pn;
int p1, p2;
do // aufsteigen
{
if(p == 0)
break;
p2 = (p - 1) / 2;
if(OnCompare(p, p2) < 0) {
SwitchElements(p, p2);
p = p2;
} else
break;
} while(true);
if(p < i)
return;
do // absteigen
{
pn = p;
p1 = 2 * p + 1;
p2 = 2 * p + 2;
if(InnerList.Count > p1 && OnCompare(p, p1) > 0) // links kleiner
p = p1;
if(InnerList.Count > p2 && OnCompare(p, p2) > 0) // rechts noch kleiner
p = p2;
if(p == pn)
break;
SwitchElements(p, pn);
} while(true);
}
/// <summary>
/// Get the smallest object without removing it.
/// </summary>
/// <returns>The smallest object</returns>
public object Peek() {
if(InnerList.Count > 0)
return InnerList[0];
return null;
}
public bool Contains(object value) {
return InnerList.Contains(value);
}
public void Clear() {
InnerList.Clear();
}
public int Count {
get {
return InnerList.Count;
}
}
IEnumerator IEnumerable.GetEnumerator() {
return InnerList.GetEnumerator();
}
public void CopyTo(Array array, int index) {
InnerList.CopyTo(array, index);
}
public object Clone() {
return new PriorityQueue(InnerList, Comparer, true);
}
public bool IsSynchronized {
get {
return InnerList.IsSynchronized;
}
}
public object SyncRoot {
get {
return this;
}
}
#endregion
#region explicit implementation
bool IList.IsReadOnly {
get {
return false;
}
}
object IList.this[int index] {
get {
return InnerList[index];
}
set {
InnerList[index] = value;
Update(index);
}
}
int IList.Add(object o) {
return Queue(o);
}
void IList.RemoveAt(int index) {
throw new NotSupportedException();
}
void IList.Insert(int index, object value) {
throw new NotSupportedException();
}
void IList.Remove(object value) {
throw new NotSupportedException();
}
int IList.IndexOf(object value) {
throw new NotSupportedException();
}
bool IList.IsFixedSize {
get {
return false;
}
}
#endregion
protected ArrayList InnerList = new ArrayList();
protected IComparer Comparer;
}
*/
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the priority queue class</summary>
[TestFixture]
public class UnintrusivePriorityQueueTest {
/// <summary>Tests to ensure the count property is properly updated</summary>
[Test]
public void TestCount() {
UnintrusivePriorityQueue<string, float> testQueue =
new UnintrusivePriorityQueue<string, float>();
Assert.AreEqual(0, testQueue.Count);
testQueue.Enqueue("a", 12.34f);
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue("b", 56.78f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Dequeue();
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue("c", 9.0f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Clear();
Assert.AreEqual(0, testQueue.Count);
}
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
[Test]
public void TestOrdering() {
UnintrusivePriorityQueue<string, float> testQueue =
new UnintrusivePriorityQueue<string, float>();
testQueue.Enqueue("a", 1.0f);
testQueue.Enqueue("i", 9.0f);
testQueue.Enqueue("b", 2.0f);
testQueue.Enqueue("h", 8.0f);
testQueue.Enqueue("c", 3.0f);
testQueue.Enqueue("g", 7.0f);
testQueue.Enqueue("d", 4.0f);
testQueue.Enqueue("f", 6.0f);
testQueue.Enqueue("e", 5.0f);
Assert.AreEqual("i", testQueue.Dequeue());
Assert.AreEqual("h", testQueue.Dequeue());
Assert.AreEqual("g", testQueue.Dequeue());
Assert.AreEqual("f", testQueue.Dequeue());
Assert.AreEqual("e", testQueue.Dequeue());
Assert.AreEqual("d", testQueue.Dequeue());
Assert.AreEqual("c", testQueue.Dequeue());
Assert.AreEqual("b", testQueue.Dequeue());
Assert.AreEqual("a", testQueue.Dequeue());
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View File

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Collections;
namespace Nuclex.Support.Collections {
/// <summary>Queue that dequeues items in order of their priority</summary>
public class UnintrusivePriorityQueue<ItemType, PriorityType>
: ICollection, IEnumerable<ItemType> where PriorityType : IComparable<PriorityType> {
#region struct Entry
/// <summary>An entry in the priority queue</summary>
private struct Entry {
/// <summary>Initializes a new priority queue entry</summary>
/// <param name="item">Item to be stored in the entry</param>
/// <param name="priority">Priority of the item in the entry</param>
public Entry(ItemType item, PriorityType priority) {
this.Item = item;
this.Priority = priority;
}
/// <summary>Item contained in this priority queue entry</summary>
public ItemType Item;
/// <summary>Priority assigned to this entry</summary>
public PriorityType Priority;
}
#endregion // struct Entry
#region class EntryComparer
/// <summary>Compares two priority queue entries based on their priority</summary>
private class EntryComparer : IComparer<Entry> {
/// <summary>Compares the left entry to the right entry</summary>
/// <param name="left">Entry on the left side</param>
/// <param name="right">Entry on the right side</param>
/// <returns>The relationship of the two entries</returns>
public int Compare(Entry left, Entry right) {
return left.Priority.CompareTo(right.Priority);
}
/// <summary>Default instance for this comparer</summary>
public static readonly EntryComparer Instance = new EntryComparer();
}
#endregion // class EntryComparer
#region class UnwrappingEnumerator
/// <summary>Enumerates all items contained in a priority queue</summary>
private class UnwrappingEnumerator : IEnumerator<ItemType> {
/// <summary>Initializes a new priority queue enumerator</summary>
/// <param name="entryEnumerator">Enumerator of entries to unwrap</param>
public UnwrappingEnumerator(IEnumerator<Entry> entryEnumerator) {
this.entryEnumerator = entryEnumerator;
}
/// <summary>Resets the enumerator to its initial state</summary>
public void Reset() {
this.entryEnumerator.Reset();
}
/// <summary>The current item being enumerated</summary>
ItemType IEnumerator<ItemType>.Current {
get { return this.entryEnumerator.Current.Item; }
}
/// <summary>Releases all resources used by the enumerator</summary>
public void Dispose() {
this.entryEnumerator.Dispose();
}
/// <summary>Moves to the next item in the priority queue</summary>
/// <returns>True if a next item was found, false if the end has been reached</returns>
public bool MoveNext() {
return this.entryEnumerator.MoveNext();
}
/// <summary>The current item being enumerated</summary>
object IEnumerator.Current {
get { return this.entryEnumerator.Current.Item; }
}
/// <summary>Enumerator for entries to be unwrapped by this enumerator</summary>
private IEnumerator<Entry> entryEnumerator;
}
#endregion // class Enumerator
/// <summary>Initializes a new non-intrusive priority queue</summary>
public UnintrusivePriorityQueue() {
this.intrusiveQueue = new PriorityQueue<Entry>(EntryComparer.Instance);
}
/// <summary>Takes the item with the highest priority off from the queue</summary>
/// <returns>The item with the highest priority in the list</returns>
public ItemType Dequeue() {
return this.intrusiveQueue.Dequeue().Item;
}
/// <summary>Puts an item into the priority queue</summary>
/// <param name="item">Item to be queued</param>
/// <param name="priority">Priority of the item to be queued</param>
public void Enqueue(ItemType item, PriorityType priority) {
this.intrusiveQueue.Enqueue(new Entry(item, priority));
}
/// <summary>Removes all items from the priority queue</summary>
public void Clear() {
this.intrusiveQueue.Clear();
}
/// <summary>Total number of items in the priority queue</summary>
public int Count {
get { return this.intrusiveQueue.Count; }
}
/// <summary>Copies the contents of the priority queue into an array</summary>
/// <param name="array">Array to copy the priority queue into</param>
/// <param name="index">Starting index for the destination array</param>
public void CopyTo(Array array, int index) {
Entry[] entries = new Entry[this.intrusiveQueue.Count];
this.intrusiveQueue.CopyTo(array, 0);
for(int entryIndex = 0; entryIndex < this.intrusiveQueue.Count; ++entryIndex)
array.SetValue(entries[entryIndex], entryIndex + index);
}
/// <summary>
/// Obtains an object that can be used to synchronize accesses to the priority queue
/// from different threads
/// </summary>
public object SyncRoot {
get { return this.intrusiveQueue.SyncRoot; }
}
/// <summary>Whether operations performed on this priority queue are thread safe</summary>
public bool IsSynchronized {
get { return this.intrusiveQueue.IsSynchronized; }
}
/// <summary>Returns a typesafe enumerator for the priority queue</summary>
/// <returns>A new enumerator for the priority queue</returns>
public IEnumerator<ItemType> GetEnumerator() {
return new UnwrappingEnumerator(this.intrusiveQueue.GetEnumerator());
}
/// <summary>Returns an enumerator for the priority queue</summary>
/// <returns>A new enumerator for the priority queue</returns>
IEnumerator IEnumerable.GetEnumerator() {
return new UnwrappingEnumerator(this.intrusiveQueue.GetEnumerator());
}
/// <summary>Intrusive priority queue being wrapped by this class</summary>
private PriorityQueue<Entry> intrusiveQueue;
}
} // namespace Nuclex.Support.Collections