Skip to content

Commit

Permalink
Extract galloping methods into static GallopingStrategy class
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
Kalkwst committed Sep 22, 2024
1 parent 9439ee2 commit e5d593a
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 3 deletions.
92 changes: 92 additions & 0 deletions Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs
Original file line number Diff line number Diff line change
@@ -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<int> comparer = Comparer<int>.Default;

Check warning on line 10 in Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Algorithms.Tests/Sorters/Utils/GallopingStrategyTests.cs#L10

Make 'comparer' 'readonly'.

[Test]
public void GallopLeft_KeyPresent_ReturnsCorrectIndex()
{
var array = new[] { 1, 2, 3, 4, 5 };
var index = GallopingStrategy<int>.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<int>.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<int>.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<int>.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<int>.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<int>.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<int>.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<int>.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<int>.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<int>.GallopRight(array, 1, 0, array.Length, comparer);
Assert.That(index, Is.EqualTo(0));
}
}
}
7 changes: 4 additions & 3 deletions Algorithms/Sorters/Comparison/TimSorter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Algorithms.Sorters.Utils;

namespace Algorithms.Sorters.Comparison;

Expand Down Expand Up @@ -470,7 +471,7 @@ private void MergeAt(T[] array, int index)

stackSize--;

var k = GallopRight(array, array[baseB], baseA, lenA, 0);
var k = GallopingStrategy<T>.GallopRight(array, array[baseB], baseA, lenA, comparer);

baseA += k;
lenA -= k;
Expand All @@ -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<T>.GallopLeft(array, array[baseA + lenA - 1], baseB, lenB, comparer);

if (lenB <= 0)
{
Expand Down Expand Up @@ -614,7 +615,7 @@ private bool GallopMerge(TimChunk<T> left, TimChunk<T> right, ref int dest)
return true;
}

right.Wins = GallopLeft(right.Array, left.Array[left.Index], right.Index, right.Remaining, 0);
right.Wins = GallopingStrategy<T>.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);
Expand Down
106 changes: 106 additions & 0 deletions Algorithms/Sorters/Utils/GallopingStrategy.cs
Original file line number Diff line number Diff line change
@@ -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<T>
{
public static int GallopLeft(T[] array, T key, int baseIndex, int length, IComparer<T> 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<T> 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<T> 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<T> 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<T> 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;
}
}

0 comments on commit e5d593a

Please sign in to comment.