1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using Algorithms . Sorters . Utils ;
3
4
4
5
namespace Algorithms . Sorters . Comparison ;
5
6
@@ -27,6 +28,10 @@ public class TimSorter<T> : IComparisonSorter<T>
27
28
{
28
29
private readonly int minMerge ;
29
30
private readonly int initMinGallop ;
31
+
32
+ // Pool of reusable TimChunk objects for memory efficiency.
33
+ private readonly TimChunk < T > [ ] chunkPool = new TimChunk < T > [ 2 ] ;
34
+
30
35
private readonly int [ ] runBase ;
31
36
private readonly int [ ] runLengths ;
32
37
@@ -50,15 +55,18 @@ private class TimChunk<Tc>
50
55
public int Wins { get ; set ; }
51
56
}
52
57
53
- public TimSorter ( int minMerge = 32 , int minGallop = 7 )
58
+ public TimSorter ( TimSorterSettings settings , IComparer < T > comparer )
54
59
{
55
60
initMinGallop = minGallop ;
56
- this . minMerge = minMerge ;
57
61
runBase = new int [ 85 ] ;
58
62
runLengths = new int [ 85 ] ;
59
63
60
64
stackSize = 0 ;
61
- this . minGallop = minGallop ;
65
+
66
+ minGallop = settings . MinGallop ;
67
+ minMerge = settings . MinMerge ;
68
+
69
+ this . comparer = comparer ?? Comparer < T > . Default ;
62
70
}
63
71
64
72
/// <summary>
@@ -158,15 +166,6 @@ private static void ReverseRange(T[] array, int start, int end)
158
166
}
159
167
}
160
168
161
- /// <summary>
162
- /// Left shift a value, preventing a roll over to negative numbers.
163
- /// </summary>
164
- /// <param name="shiftable">int value to left shift.</param>
165
- /// <returns>Left shifted value, bound to 2,147,483,647.</returns>
166
- private static int BoundLeftShift ( int shiftable ) => ( shiftable << 1 ) < 0
167
- ? ( shiftable << 1 ) + 1
168
- : int . MaxValue ;
169
-
170
169
/// <summary>
171
170
/// Check the chunks before getting in to a merge to make sure there's something to actually do.
172
171
/// </summary>
@@ -265,105 +264,6 @@ private int CountRunAndMakeAscending(T[] array, int start)
265
264
return runHi - start ;
266
265
}
267
266
268
- /// <summary>
269
- /// Find the position in the array that a key should fit to the left of where it currently sits.
270
- /// </summary>
271
- /// <param name="array">Array to search.</param>
272
- /// <param name="key">Key to place in the array.</param>
273
- /// <param name="i">Base index for the key.</param>
274
- /// <param name="len">Length of the chunk to run through.</param>
275
- /// <param name="hint">Initial starting position to start from.</param>
276
- /// <returns>Offset for the key's location.</returns>
277
- private int GallopLeft ( T [ ] array , T key , int i , int len , int hint )
278
- {
279
- var ( offset , lastOfs ) = comparer . Compare ( key , array [ i + hint ] ) > 0
280
- ? RightRun ( array , key , i , len , hint , 0 )
281
- : LeftRun ( array , key , i , hint , 1 ) ;
282
-
283
- return FinalOffset ( array , key , i , offset , lastOfs , 1 ) ;
284
- }
285
-
286
- /// <summary>
287
- /// Find the position in the array that a key should fit to the right of where it currently sits.
288
- /// </summary>
289
- /// <param name="array">Array to search.</param>
290
- /// <param name="key">Key to place in the array.</param>
291
- /// <param name="i">Base index for the key.</param>
292
- /// <param name="len">Length of the chunk to run through.</param>
293
- /// <param name="hint">Initial starting position to start from.</param>
294
- /// <returns>Offset for the key's location.</returns>
295
- private int GallopRight ( T [ ] array , T key , int i , int len , int hint )
296
- {
297
- var ( offset , lastOfs ) = comparer . Compare ( key , array [ i + hint ] ) < 0
298
- ? LeftRun ( array , key , i , hint , 0 )
299
- : RightRun ( array , key , i , len , hint , - 1 ) ;
300
-
301
- return FinalOffset ( array , key , i , offset , lastOfs , 0 ) ;
302
- }
303
-
304
- private ( int offset , int lastOfs ) LeftRun ( T [ ] array , T key , int i , int hint , int lt )
305
- {
306
- var maxOfs = hint + 1 ;
307
- var ( offset , tmp ) = ( 1 , 0 ) ;
308
-
309
- while ( offset < maxOfs && comparer . Compare ( key , array [ i + hint - offset ] ) < lt )
310
- {
311
- tmp = offset ;
312
- offset = BoundLeftShift ( offset ) ;
313
- }
314
-
315
- if ( offset > maxOfs )
316
- {
317
- offset = maxOfs ;
318
- }
319
-
320
- var lastOfs = hint - offset ;
321
- offset = hint - tmp ;
322
-
323
- return ( offset , lastOfs ) ;
324
- }
325
-
326
- private ( int offset , int lastOfs ) RightRun ( T [ ] array , T key , int i , int len , int hint , int gt )
327
- {
328
- var ( offset , lastOfs ) = ( 1 , 0 ) ;
329
- var maxOfs = len - hint ;
330
- while ( offset < maxOfs && comparer . Compare ( key , array [ i + hint + offset ] ) > gt )
331
- {
332
- lastOfs = offset ;
333
- offset = BoundLeftShift ( offset ) ;
334
- }
335
-
336
- if ( offset > maxOfs )
337
- {
338
- offset = maxOfs ;
339
- }
340
-
341
- offset += hint ;
342
- lastOfs += hint ;
343
-
344
- return ( offset , lastOfs ) ;
345
- }
346
-
347
- private int FinalOffset ( T [ ] array , T key , int i , int offset , int lastOfs , int lt )
348
- {
349
- lastOfs ++ ;
350
- while ( lastOfs < offset )
351
- {
352
- var m = lastOfs + ( int ) ( ( uint ) ( offset - lastOfs ) >> 1 ) ;
353
-
354
- if ( comparer . Compare ( key , array [ i + m ] ) < lt )
355
- {
356
- offset = m ;
357
- }
358
- else
359
- {
360
- lastOfs = m + 1 ;
361
- }
362
- }
363
-
364
- return offset ;
365
- }
366
-
367
267
/// <summary>
368
268
/// Sorts the specified portion of the specified array using a binary
369
269
/// insertion sort. It requires O(n log n) compares, but O(n^2) data movement.
@@ -465,7 +365,7 @@ private void MergeAt(T[] array, int index)
465
365
466
366
stackSize -- ;
467
367
468
- var k = GallopRight ( array , array [ baseB ] , baseA , lenA , 0 ) ;
368
+ var k = GallopingStrategy < T > . GallopRight ( array , array [ baseB ] , baseA , lenA , comparer ) ;
469
369
470
370
baseA += k ;
471
371
lenA -= k ;
@@ -475,7 +375,7 @@ private void MergeAt(T[] array, int index)
475
375
return ;
476
376
}
477
377
478
- lenB = GallopLeft ( array , array [ baseA + lenA - 1 ] , baseB , lenB , lenB - 1 ) ;
378
+ lenB = GallopingStrategy < T > . GallopLeft ( array , array [ baseA + lenA - 1 ] , baseB , lenB , comparer ) ;
479
379
480
380
if ( lenB <= 0 )
481
381
{
@@ -590,7 +490,7 @@ private bool StableMerge(TimChunk<T> left, TimChunk<T> right, ref int dest, int
590
490
591
491
private bool GallopMerge ( TimChunk < T > left , TimChunk < T > right , ref int dest )
592
492
{
593
- left . Wins = GallopRight ( left . Array , right . Array [ right . Index ] , left . Index , left . Remaining , 0 ) ;
493
+ left . Wins = GallopingStrategy < T > . GallopRight ( left . Array , right . Array [ right . Index ] , left . Index , left . Remaining , comparer ) ;
594
494
if ( left . Wins != 0 )
595
495
{
596
496
Array . Copy ( left . Array , left . Index , right . Array , dest , left . Wins ) ;
@@ -609,7 +509,7 @@ private bool GallopMerge(TimChunk<T> left, TimChunk<T> right, ref int dest)
609
509
return true ;
610
510
}
611
511
612
- right . Wins = GallopLeft ( right . Array , left . Array [ left . Index ] , right . Index , right . Remaining , 0 ) ;
512
+ right . Wins = GallopingStrategy < T > . GallopLeft ( right . Array , left . Array [ left . Index ] , right . Index , right . Remaining , comparer ) ;
613
513
if ( right . Wins != 0 )
614
514
{
615
515
Array . Copy ( right . Array , right . Index , right . Array , dest , right . Wins ) ;
@@ -631,3 +531,16 @@ private bool GallopMerge(TimChunk<T> left, TimChunk<T> right, ref int dest)
631
531
return false ;
632
532
}
633
533
}
534
+
535
+ public class TimSorterSettings
536
+ {
537
+ public int MinMerge { get ; }
538
+
539
+ public int MinGallop { get ; }
540
+
541
+ public TimSorterSettings ( int minMerge = 32 , int minGallop = 7 )
542
+ {
543
+ MinMerge = minMerge ;
544
+ MinGallop = minGallop ;
545
+ }
546
+ }
0 commit comments