/*
Copyright 2007-2013 The NGenerics Team
(https://github.com/ngenerics/ngenerics/wiki/Team)
This program is licensed under the GNU Lesser General Public License (LGPL). You should
have received a copy of the license along with the source code. If not, an online copy
of the license can be found at http://www.gnu.org/copyleft/lesser.html.
Community contributions :
- TKey Peek(out TPriority) contributed by Karl Shulze (http://www.karlschulze.com/).
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using NGenerics.Comparers;
using NGenerics.DataStructures.Trees;
using NGenerics.Util;
namespace NGenerics.DataStructures.Queues
{
///
/// An implementation of a Priority Queue (can be or ).
///
/// The type of the priority in the .
/// The type of the elements in the .
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
[SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
//[Serializable]
public class PriorityQueue : ICollection, IQueue
{
#region Globals
private readonly RedBlackTreeList tree;
private TPriority defaultPriority;
private readonly PriorityQueueType queueType;
#endregion
#region Construction
/// Type of the queue.
///
///
///
///
public PriorityQueue(PriorityQueueType queueType) :
this(queueType, Comparer.Default) {
}
///
public PriorityQueue(PriorityQueueType queueType, IComparer comparer) {
if ((queueType != PriorityQueueType.Minimum) && (queueType != PriorityQueueType.Maximum)) {
throw new ArgumentOutOfRangeException("queueType");
}
this.queueType = queueType;
tree = new RedBlackTreeList(comparer);
}
///
/// Initializes a new instance of the class.
///
/// Type of the queue.
/// The comparison.
///
public PriorityQueue(PriorityQueueType queueType, Comparison comparison) :
this(queueType, new ComparisonComparer(comparison)) {
}
#endregion
#region IQueue Members
///
///
///
///
///
public void Enqueue(TValue item)
{
Add(item);
}
///
/// Enqueues the specified item.
///
/// The item.
/// The priority.
///
///
///
///
public void Enqueue(TValue item, TPriority priority)
{
Add(item, priority);
}
///
/// Dequeues the item at the front of the queue.
///
/// The item at the front of the queue.
///
///
///
///
public TValue Dequeue()
{
TPriority priority;
return Dequeue(out priority);
}
///
/// Peeks at the item in the front of the queue, without removing it.
///
/// The item at the front of the queue.
///
///
///
///
public TValue Peek()
{
var association = GetNextItem();
// Always dequeue in FIFO manner
return association.Value.First.Value;
}
///
/// Peeks at the item in the front of the queue, without removing it.
///
/// The priority of the item.
/// The item at the front of the queue.
///
///
///
///
public TValue Peek(out TPriority priority)
{
var association = GetNextItem();
var item = association.Value.First.Value;
priority = association.Key;
return item;
}
#endregion
#region ICollection Members
///
///
///
///
///
public bool IsReadOnly
{
get
{
return false;
}
}
#endregion
#region IEnumerable Members
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
///
///
///
///
public IEnumerator GetEnumerator()
{
return tree.GetValueEnumerator();
}
#endregion
#region Public Members
///
///
///
///
///
public int Count { get; private set; }
///
///
///
///
///
public bool Contains(TValue item)
{
return tree.ContainsValue(item);
}
///
///
///
///
///
public void CopyTo(TValue[] array, int arrayIndex)
{
#region Validation
Guard.ArgumentNotNull(array, "array");
if ((array.Length - arrayIndex) < Count)
{
throw new ArgumentException(Constants.NotEnoughSpaceInTheTargetArray, "array");
}
#endregion
foreach (var association in tree)
{
var items = association.Value;
foreach (var item in items)
{
array.SetValue(item, arrayIndex++);
}
}
}
///
///
///
///
///
public void Add(TValue item)
{
Add(item, defaultPriority);
}
///
/// Adds an item to the .
///
/// The object to add to the .
/// The priority of the item.
/// The is read-only.
///
///
///
///
public void Add(TValue item, TPriority priority)
{
AddItem(item, priority);
}
///
public bool Remove(TValue item)
{
TPriority priority;
return Remove(item, out priority);
}
///
/// Returns an enumerator that iterates through the keys in the collection.
///
/// A that can be used to iterate through the keys in the collection.
///
///
///
///
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
public IEnumerator> GetKeyEnumerator()
{
return tree.GetKeyEnumerator();
}
///
///
///
///
///
public void Clear()
{
ClearItems();
}
///
/// Dequeues the item from the head of the queue.
///
/// The priority of the item to dequeue.
/// The item at the head of the queue.
/// The is empty.
///
///
///
///
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#")]
public TValue Dequeue(out TPriority priority)
{
return DequeueItem(out priority);
}
///
/// Dequeues the item at the front of the queue.
///
/// The item at the front of the queue.
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the or methods.
///
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#")]
protected virtual TValue DequeueItem(out TPriority priority)
{
var association = GetNextItem();
var item = association.Value.First.Value;
association.Value.RemoveFirst();
var key = association.Key;
if (association.Value.Count == 0)
{
tree.Remove(association.Key);
}
Count--;
priority = key;
return item;
}
///
/// Gets or sets the default priority.
///
/// The default priority.
public TPriority DefaultPriority
{
get
{
return defaultPriority;
}
set
{
defaultPriority = value;
}
}
///
/// Removes the first occurrence of the specified item from the property queue.
///
/// The item to remove.
/// The priority associated with the item.
/// true if the item exists in the and has been removed; otherwise false.
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
public bool Remove(TValue item, out TPriority priority)
{
return RemoveItem(item, out priority);
}
///
/// Removes the item.
///
/// The item to remove
/// The priority of the item that was removed.
/// An indication of whether the item was found, and removed.
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the method.
///
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
protected virtual bool RemoveItem(TValue item, out TPriority priority)
{
var removed = tree.Remove(item, out priority);
if (removed)
{
Count--;
}
return removed;
}
///
/// Removes the items with the specified priority.
///
/// The priority.
/// true if the priority exists in the and has been removed; otherwise false.
public bool RemovePriorityGroup(TPriority priority)
{
return RemoveItems(priority);
}
///
/// Removes the items from the collection with the specified priority.
///
/// The priority to search for.
/// An indication of whether items were found having the specified priority.
protected virtual bool RemoveItems(TPriority priority)
{
LinkedList items;
if (tree.TryGetValue(priority, out items))
{
tree.Remove(priority);
Count -= items.Count;
return true;
}
return false;
}
///
/// Removes the items with the specified priority.
///
/// The priority.
/// The items with the specified priority.
public IList GetPriorityGroup(TPriority priority)
{
LinkedList items;
return tree.TryGetValue(priority, out items) ? new List(items) : new List();
}
///
/// Adds the specified items to the priority queue with the specified priority.
///
/// The items.
/// The priority.
/// is a null reference (Nothing in Visual Basic).
public void AddPriorityGroup(IList items, TPriority priority)
{
#region Validation
Guard.ArgumentNotNull(items, "items");
#endregion
AddPriorityGroupItem(items, priority);
}
///
/// Adds the specified items to the priority queue with the specified priority.
///
/// The items.
/// The priority.
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the method.
///
protected virtual void AddPriorityGroupItem(IList items, TPriority priority)
{
LinkedList currentValues;
if (tree.TryGetValue(priority, out currentValues))
{
for (var i = 0; i < items.Count; i++)
{
currentValues.AddLast(items[i]);
}
}
else
{
currentValues = new LinkedList(items);
tree.Add(priority, currentValues);
}
}
#endregion
#region Protected Members
///
/// Adds the item to the queue.
///
/// The item to add.
/// The priority of the item.
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the method.
///
protected virtual void AddItem(TValue item, TPriority priority)
{
LinkedList list;
if (tree.TryGetValue(priority, out list))
{
list.AddLast(item);
}
else
{
list = new LinkedList();
list.AddLast(item);
tree.Add(priority, list);
}
Count++;
}
///
/// Clears all the objects in this instance.
///
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the method.
///
protected virtual void ClearItems()
{
tree.Clear();
Count = 0;
}
#endregion
#region Private Members
///
/// Checks if the list is not empty, and if it is, throw an exception.
///
private void CheckTreeNotEmpty()
{
if (tree.Count == 0)
{
throw new InvalidOperationException("The Priority Queue is empty.");
}
}
///
/// Gets the next item.
///
private KeyValuePair> GetNextItem()
{
#region Validation
CheckTreeNotEmpty();
#endregion
return queueType == PriorityQueueType.Maximum ? tree.Maximum : tree.Minimum;
}
#endregion
}
}