| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 | 
							- using System;
 
- using System.Collections;
 
- using System.Collections.Generic;
 
- using System.Linq;
 
- using System.Text;
 
- using System.Threading.Tasks;
 
- namespace Priority_Queue
 
- {
 
-     /// <summary>
 
-     /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
 
-     /// A copy of StablePriorityQueue which also has generic priority-type
 
-     /// </summary>
 
-     /// <typeparam name="TItem">The values in the queue.  Must extend the GenericPriorityQueue class</typeparam>
 
-     /// <typeparam name="TPriority">The priority-type.  Must extend IComparable<TPriority></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;
 
-         /// <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)
 
-         {
 
- #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;
 
-         }
 
-         /// <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.  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.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 by first-in-first-out.
 
-         /// If the queue is full, the result is undefined.
 
-         /// If the node is already enqueued, the result is undefined.
 
-         /// 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 (Contains(node))
 
-             {
 
-                 throw new InvalidOperationException("Node is already enqueued: " + node);
 
-             }
 
- #endif
 
-             node.Priority = priority;
 
-             _numNodes++;
 
-             _nodes[_numNodes] = node;
 
-             node.QueueIndex = _numNodes;
 
-             node.InsertionIndex = _numNodesEverEnqueued++;
 
-             CascadeUp(_nodes[_numNodes]);
 
-         }
 
- #if NET_VERSION_4_5
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
- #endif
 
-         private void Swap(TItem node1, TItem node2)
 
-         {
 
-             //Swap the nodes
 
-             _nodes[node1.QueueIndex] = node2;
 
-             _nodes[node2.QueueIndex] = node1;
 
-             //Swap their indicies
 
-             int temp = node1.QueueIndex;
 
-             node1.QueueIndex = node2.QueueIndex;
 
-             node2.QueueIndex = temp;
 
-         }
 
-         //Performance appears to be slightly better when this is NOT inlined o_O
 
-         private void CascadeUp(TItem node)
 
-         {
 
-             //aka Heapify-up
 
-             int parent = node.QueueIndex / 2;
 
-             while (parent >= 1)
 
-             {
 
-                 TItem parentNode = _nodes[parent];
 
-                 if (HasHigherPriority(parentNode, node))
 
-                     break;
 
-                 //Node has lower priority value, so move it up the heap
 
-                 Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()
 
-                 parent = node.QueueIndex / 2;
 
-             }
 
-         }
 
- #if NET_VERSION_4_5
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
- #endif
 
-         private void CascadeDown(TItem node)
 
-         {
 
-             //aka Heapify-down
 
-             TItem newParent;
 
-             int finalQueueIndex = node.QueueIndex;
 
-             while (true)
 
-             {
 
-                 newParent = node;
 
-                 int childLeftIndex = 2 * finalQueueIndex;
 
-                 //Check if the left-child is higher-priority than the current node
 
-                 if (childLeftIndex > _numNodes)
 
-                 {
 
-                     //This could be placed outside the loop, but then we'd have to check newParent != node twice
 
-                     node.QueueIndex = finalQueueIndex;
 
-                     _nodes[finalQueueIndex] = node;
 
-                     break;
 
-                 }
 
-                 TItem childLeft = _nodes[childLeftIndex];
 
-                 if (HasHigherPriority(childLeft, newParent))
 
-                 {
 
-                     newParent = childLeft;
 
-                 }
 
-                 //Check if the right-child is higher-priority than either the current node or the left child
 
-                 int childRightIndex = childLeftIndex + 1;
 
-                 if (childRightIndex <= _numNodes)
 
-                 {
 
-                     TItem childRight = _nodes[childRightIndex];
 
-                     if (HasHigherPriority(childRight, newParent))
 
-                     {
 
-                         newParent = childRight;
 
-                     }
 
-                 }
 
-                 //If either of the children has higher (smaller) priority, swap and continue cascading
 
-                 if (newParent != node)
 
-                 {
 
-                     //Move new parent to its new index.  node will be moved once, at the end
 
-                     //Doing it this way is one less assignment operation than calling Swap()
 
-                     _nodes[finalQueueIndex] = newParent;
 
-                     int temp = newParent.QueueIndex;
 
-                     newParent.QueueIndex = finalQueueIndex;
 
-                     finalQueueIndex = temp;
 
-                 }
 
-                 else
 
-                 {
 
-                     //See note above
 
-                     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 = higher.Priority.CompareTo(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>
 
-         public bool TryDequeue(out TItem item)
 
-         {
 
-             if (_numNodes <= 0)
 
-             {
 
-                 item = default(TItem);
 
-                 return false;
 
-             }
 
- #if DEBUG
 
-             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];
 
-             Remove(returnMe);
 
-             item = returnMe;
 
-             return true;
 
-         }
 
-         /// <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);
 
-             for (int i = 1; i <= highestIndexToCopy; i++)
 
-             {
 
-                 newArray[i] = _nodes[i];
 
-             }
 
-             _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 (!Contains(node))
 
-             {
 
-                 throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
 
-             }
 
- #endif
 
-             node.Priority = priority;
 
-             OnNodeUpdated(node);
 
-         }
 
-         private void OnNodeUpdated(TItem node)
 
-         {
 
-             //Bubble the updated node up or down as appropriate
 
-             int parentIndex = node.QueueIndex / 2;
 
-             TItem parentNode = _nodes[parentIndex];
 
-             if (parentIndex > 0 && HasHigherPriority(node, parentNode))
 
-             {
 
-                 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>
 
-         public void Remove(TItem node)
 
-         {
 
- #if DEBUG
 
-             if (node == null)
 
-             {
 
-                 throw new ArgumentNullException("node");
 
-             }
 
-             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];
 
-             Swap(node, formerLastNode);
 
-             _nodes[_numNodes] = null;
 
-             _numNodes--;
 
-             //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
 
-             OnNodeUpdated(formerLastNode);
 
-         }
 
-         public IEnumerator<TItem> GetEnumerator()
 
-         {
 
-             for (int i = 1; i <= _numNodes; i++)
 
-                 yield return _nodes[i];
 
-         }
 
-         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;
 
-         }
 
-     }
 
- }
 
 
  |