Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SimplePriorityQueue.Try...() overloads that expose priority as out parameter #55

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
113 changes: 112 additions & 1 deletion Priority Queue Tests/SimplePriorityQueueTests.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Priority_Queue;
Expand Down Expand Up @@ -404,6 +404,16 @@ public void TestTryFirstEmptyQueue()
Assert.IsNull(first);
}

[Test]
public void TestTryFirstEmptyQueueWithPriorityOut()
{
Node first;
float firstPriority;
Assert.IsFalse(Queue.TryFirst(out first, out firstPriority));
Assert.IsNull(first);
Assert.AreEqual(0, firstPriority);
}

[Test]
public void TestTryFirstWithItems()
{
Expand All @@ -417,6 +427,21 @@ public void TestTryFirstWithItems()
Assert.AreEqual(1, Queue.Count);
}

[Test]
public void TestTryFirstWithItemsWithPriorityOut()
{
Node node = new Node(1);
Node first;
float firstPriority;

Enqueue(node);

Assert.IsTrue(Queue.TryFirst(out first, out firstPriority));
Assert.AreEqual(node, first);
Assert.AreEqual(1, Queue.Count);
Assert.AreEqual(node.Priority, firstPriority);
}

[Test]
public void TestTryDequeueEmptyQueue()
{
Expand All @@ -425,6 +450,16 @@ public void TestTryDequeueEmptyQueue()
Assert.IsNull(first);
}

[Test]
public void TestTryDequeueEmptyQueueWithPriorityOut()
{
Node first;
float firstPriority;
Assert.IsFalse(Queue.TryDequeue(out first, out firstPriority));
Assert.IsNull(first);
Assert.AreEqual(0, firstPriority);
}

[Test]
public void TestTryDequeueWithItems()
{
Expand All @@ -438,6 +473,21 @@ public void TestTryDequeueWithItems()
Assert.AreEqual(0, Queue.Count);
}

[Test]
public void TestTryDequeueWithItemsWithPriorityOut()
{
Node node = new Node(1);
Node first;
float firstPriority;

Enqueue(node);

Assert.IsTrue(Queue.TryDequeue(out first, out firstPriority));
Assert.AreEqual(node, first);
Assert.AreEqual(0, Queue.Count);
Assert.AreEqual(node.Priority, firstPriority);
}

[Test]
public void TestTryRemoveEmptyQueue()
{
Expand All @@ -446,6 +496,16 @@ public void TestTryRemoveEmptyQueue()
Assert.IsFalse(Queue.TryRemove(node));
}

[Test]
public void TestTryRemoveEmptyQueueWithPriorityOut()
{
Node node = new Node(1);
float priority;

Assert.IsFalse(Queue.TryRemove(node, out priority));
Assert.AreEqual(0, priority);
}

[Test]
public void TestTryRemoveItemInQueue()
{
Expand Down Expand Up @@ -473,6 +533,41 @@ public void TestTryRemoveItemInQueue()
Assert.IsFalse(Queue.TryRemove(node1));
}

[Test]
public void TestTryRemoveItemInQueueWithPriorityOut()
{
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);

Enqueue(node1);
Enqueue(node2);
Enqueue(node3);

float priority;

Assert.IsTrue(Queue.Contains(node2));
Assert.IsTrue(Queue.TryRemove(node2, out priority));
Assert.AreEqual(node2.Priority, priority);
Assert.IsFalse(Queue.Contains(node2));
Assert.IsFalse(Queue.TryRemove(node2, out priority));
Assert.AreEqual(0, priority);

Assert.IsTrue(Queue.Contains(node3));
Assert.IsTrue(Queue.TryRemove(node3, out priority));
Assert.AreEqual(node3.Priority, priority);
Assert.IsFalse(Queue.Contains(node3));
Assert.IsFalse(Queue.TryRemove(node3, out priority));
Assert.AreEqual(0, priority);

Assert.IsTrue(Queue.Contains(node1));
Assert.IsTrue(Queue.TryRemove(node1, out priority));
Assert.AreEqual(node1.Priority, priority);
Assert.IsFalse(Queue.Contains(node1));
Assert.IsFalse(Queue.TryRemove(node1, out priority));
Assert.AreEqual(0, priority);
}

[Test]
public void TestTryRemoveItemNotInQueue()
{
Expand All @@ -486,6 +581,22 @@ public void TestTryRemoveItemNotInQueue()
Assert.IsFalse(Queue.TryRemove(node3));
}

[Test]
public void TestTryRemoveItemNotInQueueWithPriorityOut()
{
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);

Enqueue(node1);
Enqueue(node2);

float priority;

Assert.IsFalse(Queue.TryRemove(node3, out priority));
Assert.AreEqual(0, priority);
}

[Test]
public void TestTryUpdatePriorityEmptyQueue()
{
Expand Down
105 changes: 103 additions & 2 deletions Priority Queue/SimplePriorityQueue.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;

Expand Down Expand Up @@ -372,10 +372,12 @@ public TPriority GetPriority(TItem item)
}

#region Try* methods for multithreading
/// <summary>
/// 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)
/// </summary>
public bool TryFirst(out TItem first)
{
if (_queue.Count > 0)
Expand All @@ -394,6 +396,32 @@ public bool TryFirst(out TItem first)
return false;
}

/// <summary>
/// Get the head of the queue, without removing it (use TryDequeue() for that) as well as its priority.
/// Useful for multi-threading, where the queue may become empty between calls to Contains(), First and GetPriority()
/// Returns true if successful, false otherwise
/// O(1)
/// </summary>
public bool TryFirst(out TItem first, out TPriority firstPriority)
{
if (_queue.Count > 0)
{
lock (_queue)
{
if (_queue.Count > 0)
{
first = _queue.First.Data;
firstPriority = _queue.First.Priority;
return true;
}
}
}

first = default(TItem);
firstPriority = default(TPriority);
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()
Expand Down Expand Up @@ -421,7 +449,36 @@ public bool TryDequeue(out TItem first)
}

/// <summary>
/// Attempts to remove an item from the queue. The item does not need to be the head of the queue.
/// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion),
/// and sets it to first along with its priority.
/// Useful for multi-threading, where the queue may become empty between calls to Contains(), GetPriority() and Dequeue()
/// Returns true if successful; false if queue was empty
/// O(log n)
/// </summary>
public bool TryDequeue(out TItem first, out TPriority firstPriority)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a duplicate method. Create method, that dequeues simple node (private bool TryDequeue(out SimpleNode node)) and use it in both TryDequeue overrides

{
if (_queue.Count > 0)
{
lock (_queue)
{
if (_queue.Count > 0)
{
SimpleNode node = _queue.Dequeue();
first = node.Data;
firstPriority = node.Priority;
RemoveFromNodeCache(node);
return true;
}
}
}

first = default(TItem);
firstPriority = default(TPriority);
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.
Expand Down Expand Up @@ -460,6 +517,50 @@ public bool TryRemove(TItem item)
}
}

/// <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(), GetPriority() and Remove()
/// Returns true if the item was successfully removed, false if it wasn't in the queue.
/// Assigns priority to priority of removed item if successfully removed.
/// If multiple copies of the item are enqueued, only the first one is removed.
/// O(log n)
/// </summary>
public bool TryRemove(TItem item, out TPriority priority)
{
lock (_queue)
{
SimpleNode removeMe;
IList<SimpleNode> nodes;
if (item == null)
{
if (_nullNodesCache.Count == 0)
{
priority = default(TPriority);
return false;
}
removeMe = _nullNodesCache[0];
nodes = _nullNodesCache;
}
else
{
if (!_itemToNodesCache.TryGetValue(item, out nodes))
{
priority = default(TPriority);
return false;
}
removeMe = nodes[0];
if (nodes.Count == 1)
{
_itemToNodesCache.Remove(item);
}
}
_queue.Remove(removeMe);
nodes.Remove(removeMe);
priority = removeMe.Priority;
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()
Expand Down