From 45e4426df6e33fd7878c267869eacdc17e1b5b57 Mon Sep 17 00:00:00 2001 From: ikpil Date: Thu, 8 Feb 2024 00:03:06 +0900 Subject: [PATCH] Changed RcSortedQueue.Remove() function to use binary search --- CHANGELOG.md | 5 +- .../Collections/RcSortedQueue.cs | 30 ++++--- src/DotRecast.Detour/DtNodeQueue.cs | 11 ++- test/DotRecast.Core.Test/RcSortedQueueTest.cs | 83 +++++++++++++++++++ test/DotRecast.Core.Test/RcStackArrayTest.cs | 1 - test/DotRecast.Detour.Test/DtNodeQueueTest.cs | 35 ++++++++ 6 files changed, 146 insertions(+), 19 deletions(-) create mode 100644 test/DotRecast.Core.Test/RcSortedQueueTest.cs create mode 100644 test/DotRecast.Detour.Test/DtNodeQueueTest.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index adbbbfd..43b898a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] - yyyy-mm-dd ### Added - +- Added DtNodeQueue UnitTest [@ikpil](https://github.com/ikpil) +- Added RcSortedQueue UnitTest [@ikpil](https://github.com/ikpil) + ### Fixed ### Changed +- Changed RcSortedQueue.Remove() function to use binary search. - Update Microsoft.NET.Test.Sdk 17.8.0 to 17.9.0 ### Removed diff --git a/src/DotRecast.Core/Collections/RcSortedQueue.cs b/src/DotRecast.Core/Collections/RcSortedQueue.cs index d6c2efe..db6d4ab 100644 --- a/src/DotRecast.Core/Collections/RcSortedQueue.cs +++ b/src/DotRecast.Core/Collections/RcSortedQueue.cs @@ -27,12 +27,12 @@ namespace DotRecast.Core.Collections { private bool _dirty; private readonly List _items; - private readonly Comparison _comparison; + private readonly Comparer _comparer; - public RcSortedQueue(Comparison comparison) + public RcSortedQueue(Comparison comp) { _items = new List(); - _comparison = (x, y) => comparison.Invoke(x, y) * -1; // reverse + _comparer = Comparer.Create((x, y) => comp.Invoke(x, y) * -1); } public int Count() @@ -40,16 +40,22 @@ namespace DotRecast.Core.Collections return _items.Count; } + public bool IsEmpty() + { + return 0 == _items.Count; + } + public void Clear() { _items.Clear(); + _dirty = false; } private void Balance() { if (_dirty) { - _items.Sort(_comparison); // reverse + _items.Sort(_comparer); // reverse _dirty = false; } } @@ -57,13 +63,13 @@ namespace DotRecast.Core.Collections public T Peek() { Balance(); - return _items[_items.Count - 1]; + return _items[^1]; } public T Dequeue() { var node = Peek(); - _items.Remove(node); + _items.RemoveAt(_items.Count - 1); return node; } @@ -73,19 +79,17 @@ namespace DotRecast.Core.Collections _dirty = true; } - public void Remove(T item) + public bool Remove(T item) { - int idx = _items.FindLastIndex(x => item.Equals(x)); + Balance(); + int idx = _items.BinarySearch(item, _comparer); if (0 > idx) - return; + return false; _items.RemoveAt(idx); + return true; } - public bool IsEmpty() - { - return 0 == _items.Count; - } public List ToList() { diff --git a/src/DotRecast.Detour/DtNodeQueue.cs b/src/DotRecast.Detour/DtNodeQueue.cs index bafe465..15f0f3f 100644 --- a/src/DotRecast.Detour/DtNodeQueue.cs +++ b/src/DotRecast.Detour/DtNodeQueue.cs @@ -24,7 +24,12 @@ namespace DotRecast.Detour { public class DtNodeQueue { - private readonly RcSortedQueue m_heap = new RcSortedQueue((n1, n2) => n1.total.CompareTo(n2.total)); + private readonly RcSortedQueue m_heap; + + public DtNodeQueue() + { + m_heap = new RcSortedQueue((n1, n2) => n1.total.CompareTo(n2.total)); + } public int Count() { @@ -43,9 +48,7 @@ namespace DotRecast.Detour public DtNode Pop() { - var node = Peek(); - m_heap.Remove(node); - return node; + return m_heap.Dequeue(); } public void Push(DtNode node) diff --git a/test/DotRecast.Core.Test/RcSortedQueueTest.cs b/test/DotRecast.Core.Test/RcSortedQueueTest.cs new file mode 100644 index 0000000..e08943b --- /dev/null +++ b/test/DotRecast.Core.Test/RcSortedQueueTest.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using DotRecast.Core.Collections; +using NUnit.Framework; + +namespace DotRecast.Core.Test; + +public class RcSortedQueueTest +{ + [Test] + public void TestEnqueueAndDequeue() + { + var sortedQueue = new RcSortedQueue((a, b) => a.CompareTo(b)); + + var r = new RcRand(); + var expectedList = new List(); + for (int i = 0; i < 999; ++i) + { + expectedList.Add(r.NextInt32() % 300); // allow duplication + } + + // ready + foreach (var expected in expectedList) + { + sortedQueue.Enqueue(expected); + } + + expectedList.Sort(); + + // check count + Assert.That(sortedQueue.Count(), Is.EqualTo(expectedList.Count)); + Assert.That(sortedQueue.IsEmpty(), Is.False); + + Assert.That(sortedQueue.ToList(), Is.EqualTo(expectedList)); + + // check Peek and Dequeue + for (int i = 0; i < expectedList.Count; ++i) + { + Assert.That(sortedQueue.Peek(), Is.EqualTo(expectedList[i])); + Assert.That(sortedQueue.Count(), Is.EqualTo(expectedList.Count - i)); + + Assert.That(sortedQueue.Dequeue(), Is.EqualTo(expectedList[i])); + Assert.That(sortedQueue.Count(), Is.EqualTo(expectedList.Count - i - 1)); + } + + // check count + Assert.That(sortedQueue.Count(), Is.EqualTo(0)); + Assert.That(sortedQueue.IsEmpty(), Is.True); + } + + [Test] + public void TestRemove() + { + var sortedQueue = new RcSortedQueue((a, b) => a.CompareTo(b)); + + var r = new RcRand(); + var expectedList = new List(); + for (int i = 0; i < 999; ++i) + { + expectedList.Add(r.NextInt32() % 300); // allow duplication + } + + // ready + foreach (var expected in expectedList) + { + sortedQueue.Enqueue(expected); + } + + expectedList.Shuffle(); + + // check + Assert.That(sortedQueue.Count(), Is.EqualTo(expectedList.Count)); + + foreach (var expected in expectedList) + { + Assert.That(sortedQueue.Remove(expected), Is.True); + } + + Assert.That(sortedQueue.IsEmpty(), Is.True); + } +} \ No newline at end of file diff --git a/test/DotRecast.Core.Test/RcStackArrayTest.cs b/test/DotRecast.Core.Test/RcStackArrayTest.cs index 52d0648..2678839 100644 --- a/test/DotRecast.Core.Test/RcStackArrayTest.cs +++ b/test/DotRecast.Core.Test/RcStackArrayTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using DotRecast.Core.Collections; -using NuGet.Frameworks; using NUnit.Framework; namespace DotRecast.Core.Test; diff --git a/test/DotRecast.Detour.Test/DtNodeQueueTest.cs b/test/DotRecast.Detour.Test/DtNodeQueueTest.cs new file mode 100644 index 0000000..bf542c7 --- /dev/null +++ b/test/DotRecast.Detour.Test/DtNodeQueueTest.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; +using DotRecast.Core.Collections; +using NUnit.Framework; + +namespace DotRecast.Detour.Test; + +public class DtNodeQueueTest +{ + [Test] + public void TestDtNodeQueue() + { + var queue = new DtNodeQueue(); + + // check count + Assert.That(queue.Count(), Is.EqualTo(0)); + + const int count = 1000; + var nodes = new List(); + for (int i = 0; i < count; ++i) + { + var node = new DtNode(i); + node.total = i; + nodes.Add(node); + } + nodes.Shuffle(); + + foreach (var node in nodes) + { + queue.Push(node); + } + + Assert.That(queue.Count(), Is.EqualTo(count)); + } +} \ No newline at end of file