From e5d593af10dc3b5105f1992d5b0f4f8af8e2d067 Mon Sep 17 00:00:00 2001 From: Konstantinos Kalafatis Date: Sun, 22 Sep 2024 13:31:11 +0300 Subject: [PATCH] Extract galloping methods into static GallopingStrategy class - Moved galloping logic (GallopLeft, GallopRight, LeftRun, RightRun, FinalOffset) from TimSorter to a new GallopingStrategy static class. - Simplified the code by removing the interface and making all methods static since there's no need for instance-specific behavior. - The refactored GallopingStrategy class now encapsulates galloping functionality, improving modularity and testability. - Updated TimSorter to use GallopingStrategy for gallop operations, enhancing code clarity and separation of concerns. --- .../Sorters/Utils/GallopingStrategyTests.cs | 92 +++++++++++++++ Algorithms/Sorters/Comparison/TimSorter.cs | 7 +- Algorithms/Sorters/Utils/GallopingStrategy.cs | 106 ++++++++++++++++++ 3 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs create mode 100644 Algorithms/Sorters/Utils/GallopingStrategy.cs diff --git a/Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs b/Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs new file mode 100644 index 00000000..3106585f --- /dev/null +++ b/Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs @@ -0,0 +1,92 @@ +using Algorithms.Sorters.Utils; +using NUnit.Framework; +using System.Collections.Generic; + +namespace Algorithms.Tests.Sorters.Utils +{ + [TestFixture] + public class GallopingStrategyTests + { + private IComparer comparer = Comparer.Default; + +[Test] + public void GallopLeft_KeyPresent_ReturnsCorrectIndex() + { + var array = new[] { 1, 2, 3, 4, 5 }; + var index = GallopingStrategy.GallopLeft(array, 3, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(2)); + } + + [Test] + public void GallopLeft_KeyNotPresent_ReturnsCorrectIndex() + { + var array = new[] { 1, 2, 4, 5 }; + var index = GallopingStrategy.GallopLeft(array, 3, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(2)); + } + + [Test] + public void GallopLeft_KeyLessThanAll_ReturnsZero() + { + var array = new[] { 2, 3, 4, 5 }; + var index = GallopingStrategy.GallopLeft(array, 1, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void GallopLeft_KeyGreaterThanAll_ReturnsLength() + { + var array = new[] { 1, 2, 3, 4 }; + var index = GallopingStrategy.GallopLeft(array, 5, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(array.Length)); + } + + [Test] + public void GallopRight_KeyPresent_ReturnsCorrectIndex() + { + var array = new[] { 1, 2, 3, 4, 5 }; + var index = GallopingStrategy.GallopRight(array, 3, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(3)); + } + + [Test] + public void GallopRight_KeyNotPresent_ReturnsCorrectIndex() + { + var array = new[] { 1, 2, 4, 5 }; + var index = GallopingStrategy.GallopRight(array, 3, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(2)); + } + + [Test] + public void GallopRight_KeyLessThanAll_ReturnsZero() + { + var array = new[] { 2, 3, 4, 5 }; + var index = GallopingStrategy.GallopRight(array, 1, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void GallopRight_KeyGreaterThanAll_ReturnsLength() + { + var array = new[] { 1, 2, 3, 4 }; + var index = GallopingStrategy.GallopRight(array, 5, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(array.Length)); + } + + [Test] + public void GallopLeft_EmptyArray_ReturnsZero() + { + var array = new int[] { }; + var index = GallopingStrategy.GallopLeft(array, 1, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(0)); + } + + [Test] + public void GallopRight_EmptyArray_ReturnsZero() + { + var array = new int[] { }; + var index = GallopingStrategy.GallopRight(array, 1, 0, array.Length, comparer); + Assert.That(index, Is.EqualTo(0)); + } + } +} diff --git a/Algorithms/Sorters/Comparison/TimSorter.cs b/Algorithms/Sorters/Comparison/TimSorter.cs index e2d221fc..a73359dd 100755 --- a/Algorithms/Sorters/Comparison/TimSorter.cs +++ b/Algorithms/Sorters/Comparison/TimSorter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Algorithms.Sorters.Utils; namespace Algorithms.Sorters.Comparison; @@ -470,7 +471,7 @@ private void MergeAt(T[] array, int index) stackSize--; - var k = GallopRight(array, array[baseB], baseA, lenA, 0); + var k = GallopingStrategy.GallopRight(array, array[baseB], baseA, lenA, comparer); baseA += k; lenA -= k; @@ -480,7 +481,7 @@ private void MergeAt(T[] array, int index) return; } - lenB = GallopLeft(array, array[baseA + lenA - 1], baseB, lenB, lenB - 1); + lenB = GallopingStrategy.GallopLeft(array, array[baseA + lenA - 1], baseB, lenB, comparer); if (lenB <= 0) { @@ -614,7 +615,7 @@ private bool GallopMerge(TimChunk left, TimChunk right, ref int dest) return true; } - right.Wins = GallopLeft(right.Array, left.Array[left.Index], right.Index, right.Remaining, 0); + right.Wins = GallopingStrategy.GallopLeft(right.Array, left.Array[left.Index], right.Index, right.Remaining, comparer); if (right.Wins != 0) { Array.Copy(right.Array, right.Index, right.Array, dest, right.Wins); diff --git a/Algorithms/Sorters/Utils/GallopingStrategy.cs b/Algorithms/Sorters/Utils/GallopingStrategy.cs new file mode 100644 index 00000000..7934f7b1 --- /dev/null +++ b/Algorithms/Sorters/Utils/GallopingStrategy.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Algorithms.Sorters.Utils +{ + public static class GallopingStrategy + { + public static int GallopLeft(T[] array, T key, int baseIndex, int length, IComparer comparer) + { + if (array.Length == 0) + { + return 0; + } + + var (offset, lastOfs) = comparer.Compare(key, array[baseIndex]) > 0 + ? RightRun(array, key, baseIndex, length, 0, comparer) + : LeftRun(array, key, baseIndex, 0, comparer); + + return FinalOffset(array, key, baseIndex, offset, lastOfs, 1, comparer); + } + + public static int GallopRight(T[] array, T key, int baseIndex, int length, IComparer comparer) + { + if (array.Length == 0) + { + return 0; + } + + var (offset, lastOfs) = comparer.Compare(key, array[baseIndex]) < 0 + ? LeftRun(array, key, baseIndex, length, comparer) + : RightRun(array, key, baseIndex, length, 0, comparer); + + return FinalOffset(array, key, baseIndex, offset, lastOfs, 0, comparer); + } + + private static (int offset, int lastOfs) LeftRun(T[] array, T key, int baseIndex, int hint, IComparer comparer) + { + var maxOfs = hint + 1; + var (offset, tmp) = (1, 0); + + while (offset < maxOfs && comparer.Compare(key, array[baseIndex + hint - offset]) < 0) + { + tmp = offset; + offset = BoundLeftShift(offset); + } + + if (offset > maxOfs) + { + offset = maxOfs; + } + + var lastOfs = hint - offset; + offset = hint - tmp; + + return (offset, lastOfs); + } + + private static (int offset, int lastOfs) RightRun(T[] array, T key, int baseIndex, int len, int hint, IComparer comparer) + { + var (offset, lastOfs) = (1, 0); + var maxOfs = len - hint; + while (offset < maxOfs && comparer.Compare(key, array[baseIndex + hint + offset]) > 0) + { + lastOfs = offset; + offset = BoundLeftShift(offset); + } + + if (offset > maxOfs) + { + offset = maxOfs; + } + + offset += hint; + lastOfs += hint; + + return (offset, lastOfs); + } + + private static int FinalOffset(T[] array, T key, int baseIndex, int offset, int lastOfs, int lt, IComparer comparer) + { + lastOfs++; + while (lastOfs < offset) + { + var m = lastOfs + (int)((uint)(offset - lastOfs) >> 1); + + if (comparer.Compare(key, array[baseIndex + m]) < lt) + { + offset = m; + } + else + { + lastOfs = m + 1; + } + } + + return offset; + } + + private static int BoundLeftShift(int shiftable) => (shiftable << 1) < 0 + ? (shiftable << 1) + 1 + : int.MaxValue; + } +}