Skip to content

Commit 1a7b74b

Browse files
committed
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.
1 parent 9439ee2 commit 1a7b74b

File tree

3 files changed

+203
-112
lines changed

3 files changed

+203
-112
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Algorithms.Sorters.Utils;
2+
using NUnit.Framework;
3+
using System.Collections.Generic;
4+
5+
namespace Algorithms.Tests.Sorters.Utils
6+
{
7+
[TestFixture]
8+
public class GallopingStrategyTests
9+
{
10+
private readonly IComparer<int> comparer = Comparer<int>.Default;
11+
12+
[Test]
13+
public void GallopLeft_KeyPresent_ReturnsCorrectIndex()
14+
{
15+
var array = new[] { 1, 2, 3, 4, 5 };
16+
var index = GallopingStrategy<int>.GallopLeft(array, 3, 0, array.Length, comparer);
17+
Assert.That(index, Is.EqualTo(2));
18+
}
19+
20+
[Test]
21+
public void GallopLeft_KeyNotPresent_ReturnsCorrectIndex()
22+
{
23+
var array = new[] { 1, 2, 4, 5 };
24+
var index = GallopingStrategy<int>.GallopLeft(array, 3, 0, array.Length, comparer);
25+
Assert.That(index, Is.EqualTo(2));
26+
}
27+
28+
[Test]
29+
public void GallopLeft_KeyLessThanAll_ReturnsZero()
30+
{
31+
var array = new[] { 2, 3, 4, 5 };
32+
var index = GallopingStrategy<int>.GallopLeft(array, 1, 0, array.Length, comparer);
33+
Assert.That(index, Is.EqualTo(0));
34+
}
35+
36+
[Test]
37+
public void GallopLeft_KeyGreaterThanAll_ReturnsLength()
38+
{
39+
var array = new[] { 1, 2, 3, 4 };
40+
var index = GallopingStrategy<int>.GallopLeft(array, 5, 0, array.Length, comparer);
41+
Assert.That(index, Is.EqualTo(array.Length));
42+
}
43+
44+
[Test]
45+
public void GallopRight_KeyPresent_ReturnsCorrectIndex()
46+
{
47+
var array = new[] { 1, 2, 3, 4, 5 };
48+
var index = GallopingStrategy<int>.GallopRight(array, 3, 0, array.Length, comparer);
49+
Assert.That(index, Is.EqualTo(3));
50+
}
51+
52+
[Test]
53+
public void GallopRight_KeyNotPresent_ReturnsCorrectIndex()
54+
{
55+
var array = new[] { 1, 2, 4, 5 };
56+
var index = GallopingStrategy<int>.GallopRight(array, 3, 0, array.Length, comparer);
57+
Assert.That(index, Is.EqualTo(2));
58+
}
59+
60+
[Test]
61+
public void GallopRight_KeyLessThanAll_ReturnsZero()
62+
{
63+
var array = new[] { 2, 3, 4, 5 };
64+
var index = GallopingStrategy<int>.GallopRight(array, 1, 0, array.Length, comparer);
65+
Assert.That(index, Is.EqualTo(0));
66+
}
67+
68+
[Test]
69+
public void GallopRight_KeyGreaterThanAll_ReturnsLength()
70+
{
71+
var array = new[] { 1, 2, 3, 4 };
72+
var index = GallopingStrategy<int>.GallopRight(array, 5, 0, array.Length, comparer);
73+
Assert.That(index, Is.EqualTo(array.Length));
74+
}
75+
76+
[Test]
77+
public void GallopLeft_EmptyArray_ReturnsZero()
78+
{
79+
var array = new int[] { };
80+
var index = GallopingStrategy<int>.GallopLeft(array, 1, 0, array.Length, comparer);
81+
Assert.That(index, Is.EqualTo(0));
82+
}
83+
84+
[Test]
85+
public void GallopRight_EmptyArray_ReturnsZero()
86+
{
87+
var array = new int[] { };
88+
var index = GallopingStrategy<int>.GallopRight(array, 1, 0, array.Length, comparer);
89+
Assert.That(index, Is.EqualTo(0));
90+
}
91+
}
92+
}

Algorithms/Sorters/Comparison/TimSorter.cs

Lines changed: 5 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using Algorithms.Sorters.Utils;
34

45
namespace Algorithms.Sorters.Comparison;
56

@@ -163,15 +164,6 @@ private static void ReverseRange(T[] array, int start, int end)
163164
}
164165
}
165166

166-
/// <summary>
167-
/// Left shift a value, preventing a roll over to negative numbers.
168-
/// </summary>
169-
/// <param name="shiftable">int value to left shift.</param>
170-
/// <returns>Left shifted value, bound to 2,147,483,647.</returns>
171-
private static int BoundLeftShift(int shiftable) => (shiftable << 1) < 0
172-
? (shiftable << 1) + 1
173-
: int.MaxValue;
174-
175167
/// <summary>
176168
/// Check the chunks before getting in to a merge to make sure there's something to actually do.
177169
/// </summary>
@@ -270,105 +262,6 @@ private int CountRunAndMakeAscending(T[] array, int start)
270262
return runHi - start;
271263
}
272264

273-
/// <summary>
274-
/// Find the position in the array that a key should fit to the left of where it currently sits.
275-
/// </summary>
276-
/// <param name="array">Array to search.</param>
277-
/// <param name="key">Key to place in the array.</param>
278-
/// <param name="i">Base index for the key.</param>
279-
/// <param name="len">Length of the chunk to run through.</param>
280-
/// <param name="hint">Initial starting position to start from.</param>
281-
/// <returns>Offset for the key's location.</returns>
282-
private int GallopLeft(T[] array, T key, int i, int len, int hint)
283-
{
284-
var (offset, lastOfs) = comparer.Compare(key, array[i + hint]) > 0
285-
? RightRun(array, key, i, len, hint, 0)
286-
: LeftRun(array, key, i, hint, 1);
287-
288-
return FinalOffset(array, key, i, offset, lastOfs, 1);
289-
}
290-
291-
/// <summary>
292-
/// Find the position in the array that a key should fit to the right of where it currently sits.
293-
/// </summary>
294-
/// <param name="array">Array to search.</param>
295-
/// <param name="key">Key to place in the array.</param>
296-
/// <param name="i">Base index for the key.</param>
297-
/// <param name="len">Length of the chunk to run through.</param>
298-
/// <param name="hint">Initial starting position to start from.</param>
299-
/// <returns>Offset for the key's location.</returns>
300-
private int GallopRight(T[] array, T key, int i, int len, int hint)
301-
{
302-
var (offset, lastOfs) = comparer.Compare(key, array[i + hint]) < 0
303-
? LeftRun(array, key, i, hint, 0)
304-
: RightRun(array, key, i, len, hint, -1);
305-
306-
return FinalOffset(array, key, i, offset, lastOfs, 0);
307-
}
308-
309-
private (int offset, int lastOfs) LeftRun(T[] array, T key, int i, int hint, int lt)
310-
{
311-
var maxOfs = hint + 1;
312-
var (offset, tmp) = (1, 0);
313-
314-
while (offset < maxOfs && comparer.Compare(key, array[i + hint - offset]) < lt)
315-
{
316-
tmp = offset;
317-
offset = BoundLeftShift(offset);
318-
}
319-
320-
if (offset > maxOfs)
321-
{
322-
offset = maxOfs;
323-
}
324-
325-
var lastOfs = hint - offset;
326-
offset = hint - tmp;
327-
328-
return (offset, lastOfs);
329-
}
330-
331-
private (int offset, int lastOfs) RightRun(T[] array, T key, int i, int len, int hint, int gt)
332-
{
333-
var (offset, lastOfs) = (1, 0);
334-
var maxOfs = len - hint;
335-
while (offset < maxOfs && comparer.Compare(key, array[i + hint + offset]) > gt)
336-
{
337-
lastOfs = offset;
338-
offset = BoundLeftShift(offset);
339-
}
340-
341-
if (offset > maxOfs)
342-
{
343-
offset = maxOfs;
344-
}
345-
346-
offset += hint;
347-
lastOfs += hint;
348-
349-
return (offset, lastOfs);
350-
}
351-
352-
private int FinalOffset(T[] array, T key, int i, int offset, int lastOfs, int lt)
353-
{
354-
lastOfs++;
355-
while (lastOfs < offset)
356-
{
357-
var m = lastOfs + (int)((uint)(offset - lastOfs) >> 1);
358-
359-
if (comparer.Compare(key, array[i + m]) < lt)
360-
{
361-
offset = m;
362-
}
363-
else
364-
{
365-
lastOfs = m + 1;
366-
}
367-
}
368-
369-
return offset;
370-
}
371-
372265
/// <summary>
373266
/// Sorts the specified portion of the specified array using a binary
374267
/// insertion sort. It requires O(n log n) compares, but O(n^2) data movement.
@@ -470,7 +363,7 @@ private void MergeAt(T[] array, int index)
470363

471364
stackSize--;
472365

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

475368
baseA += k;
476369
lenA -= k;
@@ -480,7 +373,7 @@ private void MergeAt(T[] array, int index)
480373
return;
481374
}
482375

483-
lenB = GallopLeft(array, array[baseA + lenA - 1], baseB, lenB, lenB - 1);
376+
lenB = GallopingStrategy<T>.GallopLeft(array, array[baseA + lenA - 1], baseB, lenB, comparer);
484377

485378
if (lenB <= 0)
486379
{
@@ -595,7 +488,7 @@ private bool StableMerge(TimChunk<T> left, TimChunk<T> right, ref int dest, int
595488

596489
private bool GallopMerge(TimChunk<T> left, TimChunk<T> right, ref int dest)
597490
{
598-
left.Wins = GallopRight(left.Array, right.Array[right.Index], left.Index, left.Remaining, 0);
491+
left.Wins = GallopingStrategy<T>.GallopRight(left.Array, right.Array[right.Index], left.Index, left.Remaining, comparer);
599492
if (left.Wins != 0)
600493
{
601494
Array.Copy(left.Array, left.Index, right.Array, dest, left.Wins);
@@ -614,7 +507,7 @@ private bool GallopMerge(TimChunk<T> left, TimChunk<T> right, ref int dest)
614507
return true;
615508
}
616509

617-
right.Wins = GallopLeft(right.Array, left.Array[left.Index], right.Index, right.Remaining, 0);
510+
right.Wins = GallopingStrategy<T>.GallopLeft(right.Array, left.Array[left.Index], right.Index, right.Remaining, comparer);
618511
if (right.Wins != 0)
619512
{
620513
Array.Copy(right.Array, right.Index, right.Array, dest, right.Wins);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Algorithms.Sorters.Utils
8+
{
9+
public static class GallopingStrategy<T>
10+
{
11+
public static int GallopLeft(T[] array, T key, int baseIndex, int length, IComparer<T> comparer)
12+
{
13+
if (array.Length == 0)
14+
{
15+
return 0;
16+
}
17+
18+
var (offset, lastOfs) = comparer.Compare(key, array[baseIndex]) > 0
19+
? RightRun(array, key, baseIndex, length, 0, comparer)
20+
: LeftRun(array, key, baseIndex, 0, comparer);
21+
22+
return FinalOffset(array, key, baseIndex, offset, lastOfs, 1, comparer);
23+
}
24+
25+
public static int GallopRight(T[] array, T key, int baseIndex, int length, IComparer<T> comparer)
26+
{
27+
if (array.Length == 0)
28+
{
29+
return 0;
30+
}
31+
32+
var (offset, lastOfs) = comparer.Compare(key, array[baseIndex]) < 0
33+
? LeftRun(array, key, baseIndex, length, comparer)
34+
: RightRun(array, key, baseIndex, length, 0, comparer);
35+
36+
return FinalOffset(array, key, baseIndex, offset, lastOfs, 0, comparer);
37+
}
38+
39+
private static (int offset, int lastOfs) LeftRun(T[] array, T key, int baseIndex, int hint, IComparer<T> comparer)
40+
{
41+
var maxOfs = hint + 1;
42+
var (offset, tmp) = (1, 0);
43+
44+
while (offset < maxOfs && comparer.Compare(key, array[baseIndex + hint - offset]) < 0)
45+
{
46+
tmp = offset;
47+
offset = BoundLeftShift(offset);
48+
}
49+
50+
if (offset > maxOfs)
51+
{
52+
offset = maxOfs;
53+
}
54+
55+
var lastOfs = hint - offset;
56+
offset = hint - tmp;
57+
58+
return (offset, lastOfs);
59+
}
60+
61+
private static (int offset, int lastOfs) RightRun(T[] array, T key, int baseIndex, int len, int hint, IComparer<T> comparer)
62+
{
63+
var (offset, lastOfs) = (1, 0);
64+
var maxOfs = len - hint;
65+
while (offset < maxOfs && comparer.Compare(key, array[baseIndex + hint + offset]) > 0)
66+
{
67+
lastOfs = offset;
68+
offset = BoundLeftShift(offset);
69+
}
70+
71+
if (offset > maxOfs)
72+
{
73+
offset = maxOfs;
74+
}
75+
76+
offset += hint;
77+
lastOfs += hint;
78+
79+
return (offset, lastOfs);
80+
}
81+
82+
private static int FinalOffset(T[] array, T key, int baseIndex, int offset, int lastOfs, int lt, IComparer<T> comparer)
83+
{
84+
lastOfs++;
85+
while (lastOfs < offset)
86+
{
87+
var m = lastOfs + (int)((uint)(offset - lastOfs) >> 1);
88+
89+
if (comparer.Compare(key, array[baseIndex + m]) < lt)
90+
{
91+
offset = m;
92+
}
93+
else
94+
{
95+
lastOfs = m + 1;
96+
}
97+
}
98+
99+
return offset;
100+
}
101+
102+
private static int BoundLeftShift(int shiftable) => (shiftable << 1) < 0
103+
? (shiftable << 1) + 1
104+
: int.MaxValue;
105+
}
106+
}

0 commit comments

Comments
 (0)