aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/SideChains/Priority Queue
diff options
context:
space:
mode:
authorAvi Levkovich <avi@twine-s.com>2020-03-25 17:43:49 +0200
committerAvi Levkovich <avi@twine-s.com>2020-03-25 17:43:49 +0200
commitd29da53d6f71f45749c0ede5b4cd7281ed3a270e (patch)
treefd83afc7771c0f4f19c581e1cf407bcf7c14818b /Software/Visual_Studio/SideChains/Priority Queue
parent0208e9f1800c044ec3bd002b7aa7fd00621c81be (diff)
downloadTango-d29da53d6f71f45749c0ede5b4cd7281ed3a270e.tar.gz
Tango-d29da53d6f71f45749c0ede5b4cd7281ed3a270e.zip
merge
Diffstat (limited to 'Software/Visual_Studio/SideChains/Priority Queue')
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs593
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs25
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs602
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs29
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs31
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs55
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj89
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec42
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs38
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs588
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs587
-rw-r--r--Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs10
12 files changed, 2689 insertions, 0 deletions
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs
new file mode 100644
index 000000000..927a20d41
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueue.cs
@@ -0,0 +1,593 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// An implementation of a min-Priority Queue using a heap. Has O(1) .Contains()!
+ /// See https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/wiki/Getting-Started for more information
+ /// </summary>
+ /// <typeparam name="T">The values in the queue. Must extend the FastPriorityQueueNode class</typeparam>
+ public sealed class FastPriorityQueue<T> : IFixedSizePriorityQueue<T, float>
+ where T : FastPriorityQueueNode
+ {
+ private int _numNodes;
+ private T[] _nodes;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public FastPriorityQueue(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+#endif
+
+ _numNodes = 0;
+ _nodes = new T[maxNodes + 1];
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public bool Contains(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if(node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?");
+ }
+#endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken arbitrarily.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Enqueue(T node, float priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+#endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ CascadeUp(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeUp(T node)
+ {
+ //aka Heapify-up
+ int parent;
+ if(node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherOrEqualPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherOrEqualPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(T node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ T childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherPriority(T higher, T lower)
+ {
+ return (higher.Priority < lower.Priority);
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherOrEqualPriority(node, node) (ie. both arguments the same node) will return true
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherOrEqualPriority(T higher, T lower)
+ {
+ return (higher.Priority <= lower.Priority);
+ }
+
+ /// <summary>
+ /// Removes the head of the queue and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public T Dequeue()
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+#endif
+
+ T returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+#endif
+
+ T[] newArray = new T[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public T First
+ {
+ get
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+#endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void UpdatePriority(T node, float priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(T node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// If the node is currently in the queue or belongs to another queue, the result is undefined
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(T node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<T> e = new ArraySegment<T>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs
new file mode 100644
index 000000000..54b0573ec
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/FastPriorityQueueNode.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Priority_Queue
+{
+ public class FastPriorityQueueNode
+ {
+ /// <summary>
+ /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor).
+ /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead
+ /// </summary>
+ public float Priority { get; protected internal set; }
+
+ /// <summary>
+ /// Represents the current position in the queue
+ /// </summary>
+ public int QueueIndex { get; internal set; }
+
+#if DEBUG
+ /// <summary>
+ /// The queue this node is tied to. Used only for debug builds.
+ /// </summary>
+ public object Queue { get; internal set; }
+#endif
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs
new file mode 100644
index 000000000..7687ba213
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueue.cs
@@ -0,0 +1,602 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A copy of StablePriorityQueue which also has generic priority-type
+ /// </summary>
+ /// <typeparam name="TItem">The values in the queue. Must extend the GenericPriorityQueueNode class</typeparam>
+ /// <typeparam name="TPriority">The priority-type. Must extend IComparable&lt;TPriority&gt;</typeparam>
+ public sealed class GenericPriorityQueue<TItem, TPriority> : IFixedSizePriorityQueue<TItem, TPriority>
+ where TItem : GenericPriorityQueueNode<TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ private int _numNodes;
+ private TItem[] _nodes;
+ private long _numNodesEverEnqueued;
+ private readonly Comparison<TPriority> _comparer;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public GenericPriorityQueue(int maxNodes) : this(maxNodes, Comparer<TPriority>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ /// <param name="comparer">The comparer used to compare TPriority values.</param>
+ public GenericPriorityQueue(int maxNodes, IComparer<TPriority> comparer) : this(maxNodes, comparer.Compare) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ /// <param name="comparer">The comparison function to use to compare TPriority values</param>
+ public GenericPriorityQueue(int maxNodes, Comparison<TPriority> comparer)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+#endif
+
+ _numNodes = 0;
+ _nodes = new TItem[maxNodes + 1];
+ _numNodesEverEnqueued = 0;
+ _comparer = comparer;
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public bool Contains(TItem node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually?");
+ }
+#endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Enqueue(TItem node, TPriority priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+#endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ node.InsertionIndex = _numNodesEverEnqueued++;
+ CascadeUp(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeUp(TItem node)
+ {
+ //aka Heapify-up
+ int parent;
+ if (node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ TItem parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ TItem parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(TItem node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ TItem childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ TItem childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private bool HasHigherPriority(TItem higher, TItem lower)
+ {
+ var cmp = _comparer(higher.Priority, lower.Priority);
+ return (cmp < 0 || (cmp == 0 && higher.InsertionIndex < lower.InsertionIndex));
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public TItem Dequeue()
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+#endif
+
+ TItem returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ TItem formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+#if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+#endif
+
+ TItem[] newArray = new TItem[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public TItem First
+ {
+ get
+ {
+#if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+#endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void UpdatePriority(TItem node, TPriority priority)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(TItem node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(TItem node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ TItem formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(TItem node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+
+ public IEnumerator<TItem> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<TItem> e = new ArraySegment<TItem>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs
new file mode 100644
index 000000000..5a53ed24e
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/GenericPriorityQueueNode.cs
@@ -0,0 +1,29 @@
+namespace Priority_Queue
+{
+ public class GenericPriorityQueueNode<TPriority>
+ {
+ /// <summary>
+ /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor).
+ /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead
+ /// </summary>
+ public TPriority Priority { get; protected internal set; }
+
+ /// <summary>
+ /// Represents the current position in the queue
+ /// </summary>
+ public int QueueIndex { get; internal set; }
+
+ /// <summary>
+ /// Represents the order the node was inserted in
+ /// </summary>
+ public long InsertionIndex { get; internal set; }
+
+
+#if DEBUG
+ /// <summary>
+ /// The queue this node is tied to. Used only for debug builds.
+ /// </summary>
+ public object Queue { get; internal set; }
+#endif
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs
new file mode 100644
index 000000000..e95f7c29b
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/IFixedSizePriorityQueue.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A helper-interface only needed to make writing unit tests a bit easier (hence the 'internal' access modifier)
+ /// </summary>
+ internal interface IFixedSizePriorityQueue<TItem, in TPriority> : IPriorityQueue<TItem, TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// </summary>
+ void Resize(int maxNodes);
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior.
+ /// </summary>
+ int MaxSize { get; }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+ void ResetNode(TItem node);
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs
new file mode 100644
index 000000000..a5053e955
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/IPriorityQueue.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later.
+ /// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can
+ /// (theoretically?) optimize method calls from concrete-types slightly better.
+ /// </summary>
+ public interface IPriorityQueue<TItem, in TPriority> : IEnumerable<TItem>
+ where TPriority : IComparable<TPriority>
+ {
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// See implementation for how duplicates are handled.
+ /// </summary>
+ void Enqueue(TItem node, TPriority priority);
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// </summary>
+ TItem Dequeue();
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// </summary>
+ void Clear();
+
+ /// <summary>
+ /// Returns whether the given node is in the queue.
+ /// </summary>
+ bool Contains(TItem node);
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// </summary>
+ void Remove(TItem node);
+
+ /// <summary>
+ /// Call this method to change the priority of a node.
+ /// </summary>
+ void UpdatePriority(TItem node, TPriority priority);
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// </summary>
+ TItem First { get; }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// </summary>
+ int Count { get; }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj
new file mode 100644
index 000000000..8b02157f1
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.csproj
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{1531C1EA-BD53-41D1-A34B-CFCDF79D2651}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Priority_Queue</RootNamespace>
+ <AssemblyName>Priority Queue</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">NET_VERSION_4_5</CustomConstants>
+ <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <DefineConstants>TRACE;NET_VERSION_4_5</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <OutputPath>bin\Release\net45\</OutputPath>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <DocumentationFile>bin\Release\net45\Priority Queue.xml</DocumentationFile>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release 2.0|AnyCPU'">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <OutputPath>bin\Release\net20\</OutputPath>
+ <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+ <DocumentationFile>bin\Release\net20\Priority Queue.xml</DocumentationFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="GenericPriorityQueue.cs" />
+ <Compile Include="GenericPriorityQueueNode.cs" />
+ <Compile Include="IFixedSizePriorityQueue.cs" />
+ <Compile Include="StablePriorityQueue.cs" />
+ <Compile Include="FastPriorityQueue.cs" />
+ <Compile Include="StablePriorityQueueNode.cs" />
+ <Compile Include="IPriorityQueue.cs" />
+ <Compile Include="FastPriorityQueueNode.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SimplePriorityQueue.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec
new file mode 100644
index 000000000..94ab70f4d
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Priority Queue.nuspec
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<package >
+ <metadata>
+ <id>OptimizedPriorityQueue</id>
+ <version>4.2.0</version>
+ <title>Highly Optimized Priority Queue</title>
+ <authors>BlueRaja</authors>
+ <owners>BlueRaja</owners>
+ <licenseUrl>https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/blob/master/LICENSE.txt</licenseUrl>
+ <projectUrl>https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>A highly optimized Priority Queue for path-finding and related applications</description>
+ <releaseNotes>Speed improvements; added ResetNode(); included IEqualityComparer in SimplePriorityQueue to avoid boxing</releaseNotes>
+ <copyright>Copyright 2018</copyright>
+ <tags>C# priority-queue pathfinding optimized</tags>
+ </metadata>
+ <files>
+ <file src="bin\Release\net20\Priority Queue.dll" target="lib\net20\Priority Queue.dll" />
+ <file src="bin\Release\net20\Priority Queue.pdb" target="lib\net20\Priority Queue.pdb" />
+ <file src="bin\Release\net20\Priority Queue.xml" target="lib\net20\Priority Queue.xml" />
+
+ <file src="bin\Release\net45\Priority Queue.dll" target="lib\net45\Priority Queue.dll" />
+ <file src="bin\Release\net45\Priority Queue.pdb" target="lib\net45\Priority Queue.pdb" />
+ <file src="bin\Release\net45\Priority Queue.xml" target="lib\net45\Priority Queue.xml" />
+
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.dll" target="lib\netstandard1.0\Priority Queue.dll" />
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.pdb" target="lib\netstandard1.0\Priority Queue.pdb" />
+ <file src="..\Priority Queue UWP\bin\Release\netcore45\Priority Queue.xml" target="lib\netstandard1.0\Priority Queue.xml" />
+
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.dll" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.dll" />
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.pdb" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.pdb" />
+ <file src="..\Priority Queue PCL\bin\Release\Priority Queue.xml" target="lib\portable-net40+sl5+win8+wpa81+wp8\Priority Queue.xml" />
+
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.dll" target="lib\net35-unity full v3.5\Priority Queue.dll" />
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.pdb" target="lib\net35-unity full v3.5\Priority Queue.pdb" />
+ <file src="..\Priority Queue Unity Full\bin\Release\Priority Queue.xml" target="lib\net35-unity full v3.5\Priority Queue.xml" />
+
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.dll" target="lib\net35-unity subset v3.5\Priority Queue.dll" />
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.pdb" target="lib\net35-unity subset v3.5\Priority Queue.pdb" />
+ <file src="..\Priority Queue Unity Subset\bin\Release\Priority Queue.xml" target="lib\net35-unity subset v3.5\Priority Queue.xml" />
+ </files>
+</package> \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs b/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..7143261f2
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/Properties/AssemblyInfo.cs
@@ -0,0 +1,38 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Highly Optimized Priority Queue")]
+[assembly: AssemblyDescription("A highly optimized Priority Queue for path-finding and related applications")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Highly Optimized Priority Queue")]
+[assembly: AssemblyCopyright("Copyright © BlueRaja 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3eee6b54-af8a-494b-9121-3d46ed09a58b")]
+
+[assembly: InternalsVisibleToAttribute("Priority Queue Tests")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("4.2.0")]
+[assembly: AssemblyFileVersion("4.2.0")] \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs
new file mode 100644
index 000000000..0d35ff1ac
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/SimplePriorityQueue.cs
@@ -0,0 +1,588 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
+ /// FastPriorityQueue
+ /// Methods tagged as O(1) or O(log n) are assuming there are no duplicates. Duplicates may increase the algorithmic complexity.
+ /// </summary>
+ /// <typeparam name="TItem">The type to enqueue</typeparam>
+ /// <typeparam name="TPriority">The priority-type to use for nodes. Must extend IComparable&lt;TPriority&gt;</typeparam>
+ public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority>
+ where TPriority : IComparable<TPriority>
+ {
+ private class SimpleNode : GenericPriorityQueueNode<TPriority>
+ {
+ public TItem Data { get; private set; }
+
+ public SimpleNode(TItem data)
+ {
+ Data = data;
+ }
+ }
+
+ private const int INITIAL_QUEUE_SIZE = 10;
+ private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue;
+ private readonly Dictionary<TItem, IList<SimpleNode>> _itemToNodesCache;
+ private readonly IList<SimpleNode> _nullNodesCache;
+
+ #region Constructors
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ public SimplePriorityQueue() : this(Comparer<TPriority>.Default, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparer used to compare TPriority values. Defaults to Comparer&lt;TPriority&gt;.default</param>
+ public SimplePriorityQueue(IComparer<TPriority> priorityComparer) : this(priorityComparer.Compare, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparison function to use to compare TPriority values</param>
+ public SimplePriorityQueue(Comparison<TPriority> priorityComparer) : this(priorityComparer, EqualityComparer<TItem>.Default) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(IEqualityComparer<TItem> itemEquality) : this(Comparer<TPriority>.Default, itemEquality) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparer used to compare TPriority values. Defaults to Comparer&lt;TPriority&gt;.default</param>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(IComparer<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality) : this(priorityComparer.Compare, itemEquality) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="priorityComparer">The comparison function to use to compare TPriority values</param>
+ /// <param name="itemEquality">The equality comparison function to use to compare TItem values</param>
+ public SimplePriorityQueue(Comparison<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality)
+ {
+ _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE, priorityComparer);
+ _itemToNodesCache = new Dictionary<TItem, IList<SimpleNode>>(itemEquality);
+ _nullNodesCache = new List<SimpleNode>();
+ }
+ #endregion
+
+ /// <summary>
+ /// Given an item of type T, returns the existing SimpleNode in the queue
+ /// </summary>
+ private SimpleNode GetExistingNode(TItem item)
+ {
+ if (item == null)
+ {
+ return _nullNodesCache.Count > 0 ? _nullNodesCache[0] : null;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ return null;
+ }
+ return nodes[0];
+ }
+
+ /// <summary>
+ /// Adds an item to the Node-cache to allow for many methods to be O(1) or O(log n)
+ /// </summary>
+ private void AddToNodeCache(SimpleNode node)
+ {
+ if (node.Data == null)
+ {
+ _nullNodesCache.Add(node);
+ return;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[node.Data] = nodes;
+ }
+ nodes.Add(node);
+ }
+
+ /// <summary>
+ /// Removes an item to the Node-cache to allow for many methods to be O(1) or O(log n) (assuming no duplicates)
+ /// </summary>
+ private void RemoveFromNodeCache(SimpleNode node)
+ {
+ if (node.Data == null)
+ {
+ _nullNodesCache.Remove(node);
+ return;
+ }
+
+ IList<SimpleNode> nodes;
+ if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
+ {
+ return;
+ }
+ nodes.Remove(node);
+ if (nodes.Count == 0)
+ {
+ _itemToNodesCache.Remove(node.Data);
+ }
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ lock(_queue)
+ {
+ return _queue.Count;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// Throws an exception when the queue is empty.
+ /// O(1)
+ /// </summary>
+ public TItem First
+ {
+ get
+ {
+ lock(_queue)
+ {
+ if(_queue.Count <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+
+ return _queue.First.Data;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n)
+ /// </summary>
+ public void Clear()
+ {
+ lock(_queue)
+ {
+ _queue.Clear();
+ _itemToNodesCache.Clear();
+ _nullNodesCache.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Returns whether the given item is in the queue.
+ /// O(1)
+ /// </summary>
+ public bool Contains(TItem item)
+ {
+ lock(_queue)
+ {
+ return item == null ? _nullNodesCache.Count > 0 : _itemToNodesCache.ContainsKey(item);
+ }
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, throws an exception
+ /// O(log n)
+ /// </summary>
+ public TItem Dequeue()
+ {
+ lock(_queue)
+ {
+ if(_queue.Count <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ SimpleNode node =_queue.Dequeue();
+ RemoveFromNodeCache(node);
+ return node.Data;
+ }
+ }
+
+ /// <summary>
+ /// Enqueue the item with the given priority, without calling lock(_queue) or AddToNodeCache(node)
+ /// </summary>
+ /// <param name="item"></param>
+ /// <param name="priority"></param>
+ /// <returns></returns>
+ private SimpleNode EnqueueNoLockOrCache(TItem item, TPriority priority)
+ {
+ SimpleNode node = new SimpleNode(item);
+ if (_queue.Count == _queue.MaxSize)
+ {
+ _queue.Resize(_queue.MaxSize * 2 + 1);
+ }
+ _queue.Enqueue(node, priority);
+ return node;
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'.
+ /// Duplicates and null-values are allowed.
+ /// O(log n)
+ /// </summary>
+ public void Enqueue(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ nodes = _nullNodesCache;
+ }
+ else if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[item] = nodes;
+ }
+ SimpleNode node = EnqueueNoLockOrCache(item, priority);
+ nodes.Add(node);
+ }
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue if it doesn't already exist. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'. Null values are allowed.
+ /// Returns true if the node was successfully enqueued; false if it already exists.
+ /// O(log n)
+ /// </summary>
+ public bool EnqueueWithoutDuplicates(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count > 0)
+ {
+ return false;
+ }
+ nodes = _nullNodesCache;
+ }
+ else if (_itemToNodesCache.ContainsKey(item))
+ {
+ return false;
+ }
+ else
+ {
+ nodes = new List<SimpleNode>();
+ _itemToNodesCache[item] = nodes;
+ }
+ SimpleNode node = EnqueueNoLockOrCache(item, priority);
+ nodes.Add(node);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Removes an item from the queue. The item does not need to be the head of the queue.
+ /// If the item is not in the queue, an exception is thrown. If unsure, check Contains() first.
+ /// If multiple copies of the item are enqueued, only the first one is removed.
+ /// O(log n)
+ /// </summary>
+ public void Remove(TItem item)
+ {
+ lock(_queue)
+ {
+ SimpleNode removeMe;
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count == 0)
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item);
+ }
+ removeMe = _nullNodesCache[0];
+ nodes = _nullNodesCache;
+ }
+ else
+ {
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item);
+ }
+ removeMe = nodes[0];
+ if (nodes.Count == 1)
+ {
+ _itemToNodesCache.Remove(item);
+ }
+ }
+ _queue.Remove(removeMe);
+ nodes.Remove(removeMe);
+ }
+ }
+
+ /// <summary>
+ /// Call this method to change the priority of an item.
+ /// Calling this method on a item not in the queue will throw an exception.
+ /// If the item is enqueued multiple times, only the first one will be updated.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
+ /// O(log n)
+ /// </summary>
+ public void UpdatePriority(TItem item, TPriority priority)
+ {
+ lock (_queue)
+ {
+ SimpleNode updateMe = GetExistingNode(item);
+ if (updateMe == null)
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item);
+ }
+ _queue.UpdatePriority(updateMe, priority);
+ }
+ }
+
+ /// <summary>
+ /// Returns the priority of the given item.
+ /// Calling this method on a item not in the queue will throw an exception.
+ /// If the item is enqueued multiple times, only the priority of the first will be returned.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished).
+ /// O(1)
+ /// </summary>
+ public TPriority GetPriority(TItem item)
+ {
+ lock (_queue)
+ {
+ SimpleNode findMe = GetExistingNode(item);
+ if(findMe == null)
+ {
+ throw new InvalidOperationException("Cannot call GetPriority() on a node which is not enqueued: " + item);
+ }
+ return findMe.Priority;
+ }
+ }
+
+ #region Try* methods for multithreading
+ /// Get the head of the queue, without removing it (use TryDequeue() for that).
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and First
+ /// Returns true if successful, false otherwise
+ /// O(1)
+ public bool TryFirst(out TItem first)
+ {
+ if (_queue.Count > 0)
+ {
+ lock (_queue)
+ {
+ if (_queue.Count > 0)
+ {
+ first = _queue.First.Data;
+ return true;
+ }
+ }
+ }
+
+ first = default(TItem);
+ return false;
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and sets it to first.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Dequeue()
+ /// Returns true if successful; false if queue was empty
+ /// O(log n)
+ /// </summary>
+ public bool TryDequeue(out TItem first)
+ {
+ if (_queue.Count > 0)
+ {
+ lock (_queue)
+ {
+ if (_queue.Count > 0)
+ {
+ SimpleNode node = _queue.Dequeue();
+ first = node.Data;
+ RemoveFromNodeCache(node);
+ return true;
+ }
+ }
+ }
+
+ first = default(TItem);
+ return false;
+ }
+
+ /// <summary>
+ /// Attempts to remove an item from the queue. The item does not need to be the head of the queue.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Remove()
+ /// Returns true if the item was successfully removed, false if it wasn't in the queue.
+ /// If multiple copies of the item are enqueued, only the first one is removed.
+ /// O(log n)
+ /// </summary>
+ public bool TryRemove(TItem item)
+ {
+ lock(_queue)
+ {
+ SimpleNode removeMe;
+ IList<SimpleNode> nodes;
+ if (item == null)
+ {
+ if (_nullNodesCache.Count == 0)
+ {
+ return false;
+ }
+ removeMe = _nullNodesCache[0];
+ nodes = _nullNodesCache;
+ }
+ else
+ {
+ if (!_itemToNodesCache.TryGetValue(item, out nodes))
+ {
+ return false;
+ }
+ removeMe = nodes[0];
+ if (nodes.Count == 1)
+ {
+ _itemToNodesCache.Remove(item);
+ }
+ }
+ _queue.Remove(removeMe);
+ nodes.Remove(removeMe);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Call this method to change the priority of an item.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and UpdatePriority()
+ /// If the item is enqueued multiple times, only the first one will be updated.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
+ /// Returns true if the item priority was updated, false otherwise.
+ /// O(log n)
+ /// </summary>
+ public bool TryUpdatePriority(TItem item, TPriority priority)
+ {
+ lock(_queue)
+ {
+ SimpleNode updateMe = GetExistingNode(item);
+ if(updateMe == null)
+ {
+ return false;
+ }
+ _queue.UpdatePriority(updateMe, priority);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Attempt to get the priority of the given item.
+ /// Useful for multi-threading, where the queue may become empty between calls to Contains() and GetPriority()
+ /// If the item is enqueued multiple times, only the priority of the first will be returned.
+ /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
+ /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished).
+ /// Returns true if the item was found in the queue, false otherwise
+ /// O(1)
+ /// </summary>
+ public bool TryGetPriority(TItem item, out TPriority priority)
+ {
+ lock(_queue)
+ {
+ SimpleNode findMe = GetExistingNode(item);
+ if(findMe == null)
+ {
+ priority = default(TPriority);
+ return false;
+ }
+ priority = findMe.Priority;
+ return true;
+ }
+ }
+ #endregion
+
+ public IEnumerator<TItem> GetEnumerator()
+ {
+ List<TItem> queueData = new List<TItem>();
+ lock (_queue)
+ {
+ //Copy to a separate list because we don't want to 'yield return' inside a lock
+ foreach(var node in _queue)
+ {
+ queueData.Add(node.Data);
+ }
+ }
+
+ return queueData.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public bool IsValidQueue()
+ {
+ lock(_queue)
+ {
+ // Check all items in cache are in the queue
+ foreach (IList<SimpleNode> nodes in _itemToNodesCache.Values)
+ {
+ foreach (SimpleNode node in nodes)
+ {
+ if (!_queue.Contains(node))
+ {
+ return false;
+ }
+ }
+ }
+
+ // Check all items in queue are in cache
+ foreach (SimpleNode node in _queue)
+ {
+ if (GetExistingNode(node.Data) == null)
+ {
+ return false;
+ }
+ }
+
+ // Check queue structure itself
+ return _queue.IsValidQueue();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
+ /// FastPriorityQueue
+ /// This class is kept here for backwards compatibility. It's recommended you use SimplePriorityQueue&lt;TItem, TPriority&gt;
+ /// </summary>
+ /// <typeparam name="TItem">The type to enqueue</typeparam>
+ public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float>
+ {
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ public SimplePriorityQueue() { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="comparer">The comparer used to compare priority values. Defaults to Comparer&lt;float&gt;.default</param>
+ public SimplePriorityQueue(IComparer<float> comparer) : base(comparer) { }
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="comparer">The comparison function to use to compare priority values</param>
+ public SimplePriorityQueue(Comparison<float> comparer) : base(comparer) { }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs
new file mode 100644
index 000000000..442b2dbcc
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueue.cs
@@ -0,0 +1,587 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Priority_Queue
+{
+ /// <summary>
+ /// A copy of FastPriorityQueue which is also stable - that is, when two nodes are enqueued with the same priority, they
+ /// are always dequeued in the same order.
+ /// See https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp/wiki/Getting-Started for more information
+ /// </summary>
+ /// <typeparam name="T">The values in the queue. Must extend the StablePriorityQueueNode class</typeparam>
+ public sealed class StablePriorityQueue<T> : IFixedSizePriorityQueue<T, float>
+ where T : StablePriorityQueueNode
+ {
+ private int _numNodes;
+ private T[] _nodes;
+ private long _numNodesEverEnqueued;
+
+ /// <summary>
+ /// Instantiate a new Priority Queue
+ /// </summary>
+ /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
+ public StablePriorityQueue(int maxNodes)
+ {
+ #if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("New queue size cannot be smaller than 1");
+ }
+ #endif
+
+ _numNodes = 0;
+ _nodes = new T[maxNodes + 1];
+ _numNodesEverEnqueued = 0;
+ }
+
+ /// <summary>
+ /// Returns the number of nodes in the queue.
+ /// O(1)
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _numNodes;
+ }
+ }
+
+ /// <summary>
+ /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
+ /// attempting to enqueue another item will cause undefined behavior. O(1)
+ /// </summary>
+ public int MaxSize
+ {
+ get
+ {
+ return _nodes.Length - 1;
+ }
+ }
+
+ /// <summary>
+ /// Removes every node from the queue.
+ /// O(n) (So, don't do this often!)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void Clear()
+ {
+ Array.Clear(_nodes, 1, _numNodes);
+ _numNodes = 0;
+ }
+
+ /// <summary>
+ /// Returns (in O(1)!) whether the given node is in the queue.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(1)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public bool Contains(T node)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Contains was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
+ {
+ throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually?");
+ }
+ #endif
+
+ return (_nodes[node.QueueIndex] == node);
+ }
+
+ /// <summary>
+ /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
+ /// If the queue is full, the result is undefined.
+ /// If the node is already enqueued, the result is undefined.
+ /// If node is or has been previously added to another queue, the result is undefined unless oldQueue.ResetNode(node) has been called
+ /// O(log n)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void Enqueue(T node, float priority)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if(_numNodes >= _nodes.Length - 1)
+ {
+ throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Enqueue was called on a node from another queue. Please call originalQueue.ResetNode() first");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("Node is already enqueued: " + node);
+ }
+ node.Queue = this;
+ #endif
+
+ node.Priority = priority;
+ _numNodes++;
+ _nodes[_numNodes] = node;
+ node.QueueIndex = _numNodes;
+ node.InsertionIndex = _numNodesEverEnqueued++;
+ CascadeUp(node);
+ }
+
+ //Performance appears to be slightly better when this is NOT inlined o_O
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ private void CascadeUp(T node)
+ {
+ //aka Heapify-up
+ int parent;
+ if(node.QueueIndex > 1)
+ {
+ parent = node.QueueIndex >> 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ return;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ else
+ {
+ return;
+ }
+ while(parent > 1)
+ {
+ parent >>= 1;
+ T parentNode = _nodes[parent];
+ if(HasHigherPriority(parentNode, node))
+ break;
+
+ //Node has lower priority value, so move parent down the heap to make room
+ _nodes[node.QueueIndex] = parentNode;
+ parentNode.QueueIndex = node.QueueIndex;
+
+ node.QueueIndex = parent;
+ }
+ _nodes[node.QueueIndex] = node;
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CascadeDown(T node)
+ {
+ //aka Heapify-down
+ int finalQueueIndex = node.QueueIndex;
+ int childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ return;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ int childRightIndex = childLeftIndex + 1;
+ T childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ return;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ return;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ return;
+ }
+ }
+
+ while(true)
+ {
+ childLeftIndex = 2 * finalQueueIndex;
+
+ // If leaf node, we're done
+ if(childLeftIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+
+ // Check if the left-child is higher-priority than the current node
+ childRightIndex = childLeftIndex + 1;
+ childLeft = _nodes[childLeftIndex];
+ if(HasHigherPriority(childLeft, node))
+ {
+ // Check if there is a right child. If not, swap and finish.
+ if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = childLeftIndex;
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ _nodes[childLeftIndex] = node;
+ break;
+ }
+ // Check if the left-child is higher-priority than the right-child
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childLeft, childRight))
+ {
+ // left is highest, move it up and continue
+ childLeft.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childLeft;
+ finalQueueIndex = childLeftIndex;
+ }
+ else
+ {
+ // right is even higher, move it up and continue
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ }
+ // Not swapping with left-child, does right-child exist?
+ else if(childRightIndex > _numNodes)
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ else
+ {
+ // Check if the right-child is higher-priority than the current node
+ T childRight = _nodes[childRightIndex];
+ if(HasHigherPriority(childRight, node))
+ {
+ childRight.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = childRight;
+ finalQueueIndex = childRightIndex;
+ }
+ // Neither child is higher-priority than current, so finish and stop.
+ else
+ {
+ node.QueueIndex = finalQueueIndex;
+ _nodes[finalQueueIndex] = node;
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
+ /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ private bool HasHigherPriority(T higher, T lower)
+ {
+ return (higher.Priority < lower.Priority ||
+ (higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex));
+ }
+
+ /// <summary>
+ /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
+ /// If queue is empty, result is undefined
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public T Dequeue()
+ {
+ #if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
+ }
+
+ if(!IsValidQueue())
+ {
+ throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
+ "Or add the same node to two different queues?)");
+ }
+ #endif
+
+ T returnMe = _nodes[1];
+ //If the node is already the last node, we can remove it immediately
+ if(_numNodes == 1)
+ {
+ _nodes[1] = null;
+ _numNodes = 0;
+ return returnMe;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[1] = formerLastNode;
+ formerLastNode.QueueIndex = 1;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) down
+ CascadeDown(formerLastNode);
+ return returnMe;
+ }
+
+ /// <summary>
+ /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
+ /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
+ /// O(n)
+ /// </summary>
+ public void Resize(int maxNodes)
+ {
+ #if DEBUG
+ if (maxNodes <= 0)
+ {
+ throw new InvalidOperationException("Queue size cannot be smaller than 1");
+ }
+
+ if (maxNodes < _numNodes)
+ {
+ throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
+ }
+ #endif
+
+ T[] newArray = new T[maxNodes + 1];
+ int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
+ Array.Copy(_nodes, newArray, highestIndexToCopy + 1);
+ _nodes = newArray;
+ }
+
+ /// <summary>
+ /// Returns the head of the queue, without removing it (use Dequeue() for that).
+ /// If the queue is empty, behavior is undefined.
+ /// O(1)
+ /// </summary>
+ public T First
+ {
+ get
+ {
+ #if DEBUG
+ if(_numNodes <= 0)
+ {
+ throw new InvalidOperationException("Cannot call .First on an empty queue");
+ }
+ #endif
+
+ return _nodes[1];
+ }
+ }
+
+ /// <summary>
+ /// This method must be called on a node every time its priority changes while it is in the queue.
+ /// <b>Forgetting to call this method will result in a corrupted queue!</b>
+ /// Calling this method on a node not in the queue results in undefined behavior
+ /// O(log n)
+ /// </summary>
+ #if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public void UpdatePriority(T node, float priority)
+ {
+ #if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.UpdatePriority was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
+ }
+ #endif
+
+ node.Priority = priority;
+ OnNodeUpdated(node);
+ }
+
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void OnNodeUpdated(T node)
+ {
+ //Bubble the updated node up or down as appropriate
+ int parentIndex = node.QueueIndex >> 1;
+
+ if(parentIndex > 0 && HasHigherPriority(node, _nodes[parentIndex]))
+ {
+ CascadeUp(node);
+ }
+ else
+ {
+ //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
+ CascadeDown(node);
+ }
+ }
+
+ /// <summary>
+ /// Removes a node from the queue. The node does not need to be the head of the queue.
+ /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
+ /// O(log n)
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void Remove(T node)
+ {
+#if DEBUG
+ if(node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.Remove was called on a node from another queue");
+ }
+ if (!Contains(node))
+ {
+ throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
+ }
+#endif
+
+ //If the node is already the last node, we can remove it immediately
+ if(node.QueueIndex == _numNodes)
+ {
+ _nodes[_numNodes] = null;
+ _numNodes--;
+ return;
+ }
+
+ //Swap the node with the last node
+ T formerLastNode = _nodes[_numNodes];
+ _nodes[node.QueueIndex] = formerLastNode;
+ formerLastNode.QueueIndex = node.QueueIndex;
+ _nodes[_numNodes] = null;
+ _numNodes--;
+
+ //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
+ OnNodeUpdated(formerLastNode);
+ }
+
+ /// <summary>
+ /// By default, nodes that have been previously added to one queue cannot be added to another queue.
+ /// If you need to do this, please call originalQueue.ResetNode(node) before attempting to add it in the new queue
+ /// </summary>
+#if NET_VERSION_4_5
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public void ResetNode(T node)
+ {
+#if DEBUG
+ if (node == null)
+ {
+ throw new ArgumentNullException("node");
+ }
+ if (node.Queue != null && !Equals(node.Queue))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node from another queue");
+ }
+ if (Contains(node))
+ {
+ throw new InvalidOperationException("node.ResetNode was called on a node that is still in the queue");
+ }
+
+ node.Queue = null;
+#endif
+
+ node.QueueIndex = 0;
+ }
+
+
+ public IEnumerator<T> GetEnumerator()
+ {
+#if NET_VERSION_4_5 // ArraySegment does not implement IEnumerable before 4.5
+ IEnumerable<T> e = new ArraySegment<T>(_nodes, 1, _numNodes);
+ return e.GetEnumerator();
+#else
+ for(int i = 1; i <= _numNodes; i++)
+ yield return _nodes[i];
+#endif
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// <b>Should not be called in production code.</b>
+ /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
+ /// </summary>
+ public bool IsValidQueue()
+ {
+ for(int i = 1; i < _nodes.Length; i++)
+ {
+ if(_nodes[i] != null)
+ {
+ int childLeftIndex = 2 * i;
+ if(childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
+ return false;
+
+ int childRightIndex = childLeftIndex + 1;
+ if(childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs
new file mode 100644
index 000000000..c794aa0d0
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/Priority Queue/StablePriorityQueueNode.cs
@@ -0,0 +1,10 @@
+namespace Priority_Queue
+{
+ public class StablePriorityQueueNode : FastPriorityQueueNode
+ {
+ /// <summary>
+ /// Represents the order the node was inserted in
+ /// </summary>
+ public long InsertionIndex { get; internal set; }
+ }
+}