/* 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 } }