From 87427096d22a0bb0d933539d3bb28a277430e8f7 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 10 May 2018 13:51:43 -0400 Subject: [PATCH] feat: Initial commit of PriorityQueue collection --- CHANGELOG.md | 3 +- .../Collections/PriorityQueueTest.cs | 140 +++++++++++++++++ ICD.Common.Utils/Collections/PriorityQueue.cs | 148 ++++++++++++++++++ .../ICD.Common.Utils_SimplSharp.csproj | 1 + 4 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs create mode 100644 ICD.Common.Utils/Collections/PriorityQueue.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b6061b6..179a748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - - Adding IcdOrderedDictionary collection + - Added IcdOrderedDictionary collection + - Added PriorityQueue collection ## [3.2.0] - 2018-05-09 ### Added diff --git a/ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs b/ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs new file mode 100644 index 0000000..5163e0d --- /dev/null +++ b/ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using ICD.Common.Utils.Collections; +using NUnit.Framework; + +namespace ICD.Common.Utils.Tests.Collections +{ + [TestFixture] + public sealed class PriorityQueueTest + { + #region Properties + + [Test] + public void CountTest() + { + PriorityQueue queue = new PriorityQueue(); + + Assert.AreEqual(0, queue.Count); + + queue.Enqueue(1); + queue.Enqueue(2); + queue.Enqueue(3); + + Assert.AreEqual(3, queue.Count); + + queue.Clear(); + + Assert.AreEqual(0, queue.Count); + } + + [Test] + public void IsSynchronizedTest() + { + PriorityQueue queue = new PriorityQueue(); + + Assert.IsFalse(queue.IsSynchronized); + } + + [Test] + public void SyncRootTest() + { + PriorityQueue queue = new PriorityQueue(); + + Assert.AreEqual(queue, queue.SyncRoot); + } + + #endregion + + #region Methods + + [Test] + public void ClearTest() + { + PriorityQueue queue = new PriorityQueue(); + + queue.Enqueue(1); + queue.Enqueue(2); + queue.Enqueue(3); + + Assert.AreEqual(3, queue.Count); + + queue.Clear(); + + Assert.AreEqual(0, queue.Count); + } + + [Test] + public void EnqueueTest() + { + PriorityQueue queue = new PriorityQueue(); + + queue.Enqueue(1); + queue.Enqueue(2); + queue.Enqueue(3); + + Assert.AreEqual(3, queue.Count); + } + + [Test] + public void EnqueuePriorityTest() + { + PriorityQueue queue = new PriorityQueue(); + + queue.Enqueue(1, 3); + queue.Enqueue(2, 2); + queue.Enqueue(3, 1); + + Assert.AreEqual(3, queue.Count); + + List dequeue = new List + { + queue.Dequeue(), + queue.Dequeue(), + queue.Dequeue() + }; + + Assert.AreEqual(3, dequeue[0]); + Assert.AreEqual(2, dequeue[1]); + Assert.AreEqual(1, dequeue[2]); + } + + [Test] + public void DequeueTest() + { + PriorityQueue queue = new PriorityQueue(); + + queue.Enqueue(1, 3); + queue.Enqueue(2, 2); + queue.Enqueue(3, 1); + + Assert.AreEqual(3, queue.Count); + + List dequeue = new List + { + queue.Dequeue(), + queue.Dequeue(), + queue.Dequeue() + }; + + Assert.AreEqual(3, dequeue[0]); + Assert.AreEqual(2, dequeue[1]); + Assert.AreEqual(1, dequeue[2]); + + Assert.AreEqual(0, queue.Count); + } + + [Test] + public void GetEnumeratorTest() + { + Assert.Inconclusive(); + } + + [Test] + public void CopyToTest() + { + Assert.Inconclusive(); + } + + #endregion + } +} diff --git a/ICD.Common.Utils/Collections/PriorityQueue.cs b/ICD.Common.Utils/Collections/PriorityQueue.cs new file mode 100644 index 0000000..c0abdc0 --- /dev/null +++ b/ICD.Common.Utils/Collections/PriorityQueue.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using ICD.Common.Properties; +using ICD.Common.Utils.Extensions; + +namespace ICD.Common.Utils.Collections +{ + /// + /// Provides a first-in first-out collection with enhanced insertion features. + /// + public sealed class PriorityQueue : IEnumerable, ICollection + { + private readonly IcdOrderedDictionary> m_PriorityToQueue; + private int m_Count; + + #region Properties + + /// + /// Gets the number of items in the collection. + /// + public int Count { get { return m_Count; } } + + /// + /// Is the collection thread safe? + /// + public bool IsSynchronized { get { return false; } } + + /// + /// Gets a reference for locking. + /// + public object SyncRoot { get { return this; } } + + #endregion + + /// + /// Constructor. + /// + public PriorityQueue() + { + m_PriorityToQueue = new IcdOrderedDictionary>(); + } + + #region Methods + + /// + /// Clears the collection. + /// + [PublicAPI] + public void Clear() + { + m_PriorityToQueue.Clear(); + m_Count = 0; + } + + /// + /// Adds the item to the end of the queue. + /// + /// + [PublicAPI] + public void Enqueue(T item) + { + Enqueue(item, int.MaxValue); + } + + /// + /// Adds the item to the queue with the given priority. + /// Lower values are dequeued first. + /// + /// + /// + [PublicAPI] + public void Enqueue(T item, int priority) + { + if (!m_PriorityToQueue.ContainsKey(priority)) + m_PriorityToQueue.Add(priority, new List()); + + m_PriorityToQueue[priority].Add(item); + m_Count++; + } + + /// + /// Dequeues the first item with the lowest priority value. + /// + /// + [PublicAPI] + public T Dequeue() + { + KeyValuePair> kvp; + if (!m_PriorityToQueue.TryFirst(out kvp)) + throw new InvalidOperationException("The queue is empty."); + + int priority = kvp.Key; + List queue = kvp.Value; + + T output = queue[0]; + queue.RemoveAt(0); + + if (queue.Count == 0) + m_PriorityToQueue.Remove(priority); + + m_Count--; + + return output; + } + + /// + /// Gets an enumerator for the items. + /// + /// + public IEnumerator GetEnumerator() + { + return m_PriorityToQueue.Values + .SelectMany(v => v) + .GetEnumerator(); + } + + /// + /// Copies the collection to the target array. + /// + /// + /// + public void CopyTo(Array array, int index) + { + foreach (T item in this) + { + array.SetValue(item, index); + index++; + } + } + + #endregion + + #region Private Methods + + /// + /// Gets an enumerator for the items. + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 3a062cd..87c6a3b 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -75,6 +75,7 @@ +