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:
parent
d339e00bce
commit
d813756eed
|
@ -65,6 +65,10 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Source\Collections\UnintrusivePriorityQueue.cs">
|
||||
<XNAUseContentPipeline>false</XNAUseContentPipeline>
|
||||
<Name>UnintrusivePriorityQueue</Name>
|
||||
</Compile>
|
||||
<Compile Include="Source\Collections\Parentable.cs">
|
||||
<XNAUseContentPipeline>false</XNAUseContentPipeline>
|
||||
<Name>Parentable</Name>
|
||||
|
@ -77,6 +81,11 @@
|
|||
<XNAUseContentPipeline>false</XNAUseContentPipeline>
|
||||
<Name>PriorityQueue</Name>
|
||||
</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">
|
||||
<XNAUseContentPipeline>false</XNAUseContentPipeline>
|
||||
<Name>RingMemoryStream</Name>
|
||||
|
@ -87,6 +96,11 @@
|
|||
<Name>RingMemoryStream.Test</Name>
|
||||
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
||||
</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">
|
||||
<XNAUseContentPipeline>false</XNAUseContentPipeline>
|
||||
<Name>BinarySerializer.Test</Name>
|
||||
|
|
|
@ -14,15 +14,10 @@ namespace Nuclex.Support.Collections {
|
|||
/// </remarks>
|
||||
/// <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>
|
||||
public class ParentingCollection<ParentType, ItemType> : Collection<ItemType>, IDisposable
|
||||
public class ParentingCollection<ParentType, ItemType> : Collection<ItemType>
|
||||
where ItemType : Parentable<ParentType>
|
||||
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>
|
||||
/// <param name="parent">New parent to take ownership of the items</param>
|
||||
protected void Reparent(ParentType parent) {
|
||||
|
@ -32,37 +27,6 @@ namespace Nuclex.Support.Collections {
|
|||
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>
|
||||
protected override void ClearItems() {
|
||||
for(int index = 0; index < Count; ++index)
|
||||
|
@ -94,9 +58,32 @@ namespace Nuclex.Support.Collections {
|
|||
item.SetParent(this.parent);
|
||||
}
|
||||
|
||||
/// <summary>Release all resources owned by the instance explicitely</summary>
|
||||
public void Dispose() {
|
||||
Dispose(true); // Called by user
|
||||
/// <summary>Disposes the collection and optionally all items contained therein</summary>
|
||||
/// <param name="disposeItems">Whether to try calling Dispose() on all items</param>
|
||||
/// <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>
|
||||
|
|
72
Source/Collections/PriorityQueue.Test.cs
Normal file
72
Source/Collections/PriorityQueue.Test.cs
Normal 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
|
|
@ -3,391 +3,224 @@ using System.Collections.Generic;
|
|||
using System.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) {
|
||||
this.item = item;
|
||||
this.priority = priority;
|
||||
}
|
||||
public object Item {
|
||||
get { return item; }
|
||||
}
|
||||
public int Priority {
|
||||
get { return priority; }
|
||||
#region class Enumerator
|
||||
|
||||
/// <summary>Enumerates all items contained in a priority queue</summary>
|
||||
private class Enumerator : IEnumerator<ItemType> {
|
||||
|
||||
/// <summary>Initializes a new priority queue enumerator</summary>
|
||||
/// <param name="priorityQueue">Priority queue to be enumerated</param>
|
||||
public Enumerator(PriorityQueue<ItemType> priorityQueue) {
|
||||
this.priorityQueue = priorityQueue;
|
||||
Reset();
|
||||
}
|
||||
|
||||
private object item;
|
||||
private int priority;
|
||||
/// <summary>Resets the enumerator to its initial state</summary>
|
||||
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;
|
||||
private int capacity;
|
||||
private int version;
|
||||
private HeapEntry[] heap;
|
||||
#endregion // class Enumerator
|
||||
|
||||
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
|
||||
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)
|
||||
throw new InvalidOperationException();
|
||||
throw new InvalidOperationException("No items available to dequeue");
|
||||
|
||||
object result = heap[0].Item;
|
||||
count--;
|
||||
ItemType result = heap[0];
|
||||
--count;
|
||||
trickleDown(0, heap[count]);
|
||||
version++;
|
||||
|
||||
++version;
|
||||
|
||||
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)
|
||||
growHeap();
|
||||
count++;
|
||||
bubbleUp(count - 1, new HeapEntry(item, priority));
|
||||
version++;
|
||||
|
||||
++count;
|
||||
bubbleUp(count - 1, item);
|
||||
++version;
|
||||
}
|
||||
|
||||
private void bubbleUp(int index, HeapEntry he) {
|
||||
int parent = getParent(index);
|
||||
// note: (index > 0) means there is a parent
|
||||
while((index > 0) && (heap[parent].Priority < he.Priority)) {
|
||||
heap[index] = heap[parent];
|
||||
index = parent;
|
||||
parent = getParent(index);
|
||||
}
|
||||
heap[index] = he;
|
||||
/// <summary>Removes all items from the priority queue</summary>
|
||||
public void Clear() {
|
||||
this.count = 0;
|
||||
++version;
|
||||
}
|
||||
|
||||
private int getLeftChild(int index) {
|
||||
return (index * 2) + 1;
|
||||
}
|
||||
|
||||
private int getParent(int index) {
|
||||
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
|
||||
/// <summary>Total number of items in the priority queue</summary>
|
||||
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) {
|
||||
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 {
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
/// <summary>Whether operations performed on this priority queue are thread safe</summary>
|
||||
public bool IsSynchronized {
|
||||
get { return false; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Priority Queue enumerator
|
||||
private class PriorityQueueEnumerator : IEnumerator {
|
||||
private int index;
|
||||
private PriorityQueue pq;
|
||||
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
|
||||
/// <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 Enumerator(this);
|
||||
}
|
||||
#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
|
||||
|
|
64
Source/Collections/UnintrusivePriorityQueue.Test.cs
Normal file
64
Source/Collections/UnintrusivePriorityQueue.Test.cs
Normal 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
|
169
Source/Collections/UnintrusivePriorityQueue.cs
Normal file
169
Source/Collections/UnintrusivePriorityQueue.cs
Normal 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
|
Loading…
Reference in New Issue
Block a user