com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/AGENTS.md +1 -0
  2. package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
  3. package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
  4. package/Docs/INDEX.md +11 -1
  5. package/Docs/Images/random_generators.svg +7 -7
  6. package/Docs/RANDOM_PERFORMANCE.md +17 -14
  7. package/Docs/REFLECTION_HELPERS.md +84 -1
  8. package/Docs/REFLECTION_PERFORMANCE.md +169 -0
  9. package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
  10. package/Docs/RELATIONAL_COMPONENTS.md +6 -0
  11. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
  12. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
  13. package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
  14. package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
  15. package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
  16. package/Editor/Sprites/AnimationCopier.cs +1 -1
  17. package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
  18. package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
  19. package/Editor/Sprites/TextureResizerWizard.cs +4 -3
  20. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
  21. package/README.md +33 -18
  22. package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
  23. package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
  24. package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
  25. package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
  26. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
  27. package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
  28. package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
  29. package/Runtime/Core/CodeGen.meta +8 -0
  30. package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
  31. package/Runtime/Core/Extension/IListExtensions.cs +720 -12
  32. package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
  33. package/Runtime/Core/Helper/Objects.cs +1 -1
  34. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
  35. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
  36. package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
  37. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
  38. package/Runtime/Core/Math/Line2D.cs +2 -4
  39. package/Runtime/Core/Math/Line3D.cs +2 -4
  40. package/Runtime/Core/Random/AbstractRandom.cs +52 -5
  41. package/Runtime/Core/Random/DotNetRandom.cs +3 -3
  42. package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
  43. package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
  44. package/Runtime/Core/Random/IllusionFlow.cs +3 -3
  45. package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
  46. package/Runtime/Core/Random/PcgRandom.cs +6 -6
  47. package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
  48. package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
  49. package/Runtime/Core/Random/RomuDuo.cs +3 -3
  50. package/Runtime/Core/Random/SplitMix64.cs +3 -3
  51. package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
  52. package/Runtime/Core/Random/StormDropRandom.cs +271 -0
  53. package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
  54. package/Runtime/Core/Random/UnityRandom.cs +3 -3
  55. package/Runtime/Core/Random/WyRandom.cs +6 -4
  56. package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
  57. package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
  58. package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
  59. package/Runtime/Tags/CosmeticEffectData.cs +1 -1
  60. package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
  61. package/Tests/Editor/Helper/HelpersTests.cs +2 -2
  62. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
  63. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
  64. package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
  65. package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
  66. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
  67. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
  68. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
  69. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
  70. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
  71. package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
  72. package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
  73. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
  74. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
  75. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
  76. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
  77. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
  78. package/Tests/Editor/Tags.meta +8 -0
  79. package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
  80. package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
  81. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
  82. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
  83. package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
  84. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
  85. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
  86. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
  87. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
  88. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
  89. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
  90. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
  91. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
  92. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
  93. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
  94. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
  95. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
  96. package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
  97. package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
  98. package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
  99. package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
  100. package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
  101. package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
  102. package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
  103. package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
  104. package/package.json +1 -1
  105. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
  106. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
@@ -22,6 +22,18 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
22
22
 
23
23
  /// <summary>Insertion sort algorithm - efficient for small or nearly-sorted lists.</summary>
24
24
  Insertion = 2,
25
+
26
+ /// <summary>Meteor sort algorithm - adaptive gap-based sorting variant.</summary>
27
+ Meteor = 3,
28
+
29
+ /// <summary>Pattern-defeating quicksort - adaptive quicksort with pattern detection.</summary>
30
+ PatternDefeatingQuickSort = 4,
31
+
32
+ /// <summary>Grail sort algorithm - stable mergesort leveraging pooled buffers.</summary>
33
+ Grail = 5,
34
+
35
+ /// <summary>Power sort algorithm - adaptive mergesort that exploits natural runs.</summary>
36
+ Power = 6,
25
37
  }
26
38
 
27
39
  /// <summary>
@@ -175,13 +187,22 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
175
187
  /// <typeparam name="TComparer">The type of comparer.</typeparam>
176
188
  /// <param name="array">The list to sort.</param>
177
189
  /// <param name="comparer">The comparer to use for element comparisons.</param>
178
- /// <param name="sortAlgorithm">The sorting algorithm to use (Ghost or Insertion). Defaults to Ghost.</param>
190
+ /// <param name="sortAlgorithm">
191
+ /// The sorting algorithm to use (Ghost, Meteor, PatternDefeatingQuickSort, Grail, Power, or Insertion).
192
+ /// Defaults to Ghost.
193
+ /// </param>
179
194
  /// <remarks>
180
195
  /// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
181
196
  /// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
182
- /// <para>Performance: Ghost sort is O(n log n) average case. Insertion sort is O(n^2).</para>
197
+ /// <para>
198
+ /// Performance: Ghost, Meteor, PatternDefeatingQuickSort, Grail, and Power sorts are O(n log n) on average.
199
+ /// Insertion sort is O(n^2) worst/average case.
200
+ /// </para>
183
201
  /// <para>Allocations: No allocations.</para>
184
- /// <para>Edge cases: Empty or single element lists require no sorting. Ghost sort is currently not stable.</para>
202
+ /// <para>
203
+ /// Edge cases: Empty or single element lists require no sorting. Ghost, Meteor, and PatternDefeatingQuickSort
204
+ /// are currently not stable. Grail and Power sorts are stable.
205
+ /// </para>
185
206
  /// </remarks>
186
207
  /// <exception cref="InvalidEnumArgumentException">Thrown when sortAlgorithm is not a valid SortAlgorithm value.</exception>
187
208
  public static void Sort<T, TComparer>(
@@ -203,6 +224,26 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
203
224
  InsertionSort(array, comparer);
204
225
  return;
205
226
  }
227
+ case SortAlgorithm.Meteor:
228
+ {
229
+ MeteorSort(array, comparer);
230
+ return;
231
+ }
232
+ case SortAlgorithm.PatternDefeatingQuickSort:
233
+ {
234
+ PatternDefeatingQuickSort(array, comparer);
235
+ return;
236
+ }
237
+ case SortAlgorithm.Grail:
238
+ {
239
+ GrailSort(array, comparer);
240
+ return;
241
+ }
242
+ case SortAlgorithm.Power:
243
+ {
244
+ PowerSort(array, comparer);
245
+ return;
246
+ }
206
247
  default:
207
248
  {
208
249
  throw new InvalidEnumArgumentException(
@@ -232,22 +273,244 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
232
273
  where TComparer : IComparer<T>
233
274
  {
234
275
  int arrayCount = array.Count;
235
- for (int i = 1; i < arrayCount; ++i)
276
+ if (arrayCount < 2)
236
277
  {
237
- T key = array[i];
238
- int j = i - 1;
239
- while (0 <= j && 0 < comparer.Compare(array[j], key))
278
+ return;
279
+ }
280
+
281
+ InsertionSortRange(array, 0, arrayCount - 1, comparer);
282
+ }
283
+
284
+ /*
285
+ Implementation reference: Meteor Sort by Wiley Looper,
286
+ https://github.com/wileylooper/meteorsort/blob/master/meteorsort.cs
287
+
288
+ Note: Meteor Sort is currently not stable.
289
+ */
290
+ /// <summary>
291
+ /// Sorts the elements in the list using the Meteor Sort algorithm, a gap-sequence-based hybrid sort.
292
+ /// </summary>
293
+ /// <typeparam name="T">The type of elements in the list.</typeparam>
294
+ /// <typeparam name="TComparer">The type of comparer.</typeparam>
295
+ /// <param name="array">The list to sort.</param>
296
+ /// <param name="comparer">The comparer to use for element comparisons.</param>
297
+ /// <remarks>
298
+ /// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
299
+ /// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
300
+ /// <para>Performance: O(n log n) average case using adaptive gap reductions.</para>
301
+ /// <para>Allocations: No allocations.</para>
302
+ /// <para>Edge cases: Not a stable sort - equal elements may be reordered.</para>
303
+ /// </remarks>
304
+ public static void MeteorSort<T, TComparer>(this IList<T> array, TComparer comparer)
305
+ where TComparer : IComparer<T>
306
+ {
307
+ int length = array.Count;
308
+ int gap = length;
309
+
310
+ int i;
311
+ int j;
312
+ while (gap > 15)
313
+ {
314
+ gap = ((gap >> 2) - (gap >> 4)) + (gap >> 3);
315
+ i = gap;
316
+
317
+ while (i < length)
240
318
  {
241
- array[j + 1] = array[j];
242
- j--;
319
+ T element = array[i];
320
+ j = i;
321
+
322
+ while (j >= gap && 0 < comparer.Compare(array[j - gap], element))
323
+ {
324
+ array[j] = array[j - gap];
325
+ j -= gap;
326
+ }
327
+
328
+ array[j] = element;
329
+ i++;
243
330
  }
244
- array[j + 1] = key;
331
+ }
332
+
333
+ i = 1;
334
+ gap = 0;
335
+
336
+ while (i < length)
337
+ {
338
+ T element = array[i];
339
+ j = i;
340
+
341
+ while (j > 0 && 0 < comparer.Compare(array[gap], element))
342
+ {
343
+ array[j] = array[gap];
344
+ j = gap;
345
+ gap--;
346
+ }
347
+
348
+ array[j] = element;
349
+ gap = i;
350
+ i++;
351
+ }
352
+ }
353
+
354
+ /*
355
+ Implementation reference: Pattern-Defeating Quicksort by Orson Peters,
356
+ https://github.com/orlp/pdqsort (zlib License)
357
+
358
+ This is a C# adaptation that retains the pattern-detection heuristics while operating on IList<T>.
359
+ Note: PatternDefeatingQuickSort is not stable.
360
+ */
361
+ /// <summary>
362
+ /// Sorts the elements in the list using pattern-defeating quicksort, an adaptive quicksort variant with
363
+ /// introspective fallbacks and pattern detection.
364
+ /// </summary>
365
+ /// <typeparam name="T">The type of elements in the list.</typeparam>
366
+ /// <typeparam name="TComparer">The type of comparer.</typeparam>
367
+ /// <param name="array">The list to sort.</param>
368
+ /// <param name="comparer">The comparer to use for element comparisons.</param>
369
+ /// <remarks>
370
+ /// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
371
+ /// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
372
+ /// <para>Performance: O(n log n) on average with protection against quadratic worst cases via heapsort fallback.</para>
373
+ /// <para>Allocations: No allocations.</para>
374
+ /// <para>Edge cases: Not a stable sort - equal elements may be reordered.</para>
375
+ /// </remarks>
376
+ public static void PatternDefeatingQuickSort<T, TComparer>(
377
+ this IList<T> array,
378
+ TComparer comparer
379
+ )
380
+ where TComparer : IComparer<T>
381
+ {
382
+ int count = array.Count;
383
+ if (count < 2)
384
+ {
385
+ return;
386
+ }
387
+
388
+ int depthLimit = 2 * FloorLog2(count);
389
+ PatternDefeatingQuickSortRange(array, 0, count - 1, comparer, depthLimit);
390
+ }
391
+
392
+ /*
393
+ Implementation reference: Grail Sort by Mrrl (MIT License),
394
+ https://github.com/Mrrl/GrailSort
395
+
396
+ This adaptation uses pooled buffers instead of manual block buffers while keeping stability.
397
+ */
398
+ /// <summary>
399
+ /// Sorts the elements in the list using the Grail Sort algorithm, a stable mergesort that adapts buffer usage.
400
+ /// </summary>
401
+ /// <typeparam name="T">The type of elements in the list.</typeparam>
402
+ /// <typeparam name="TComparer">The type of comparer.</typeparam>
403
+ /// <param name="array">The list to sort.</param>
404
+ /// <param name="comparer">The comparer to use for element comparisons.</param>
405
+ /// <remarks>
406
+ /// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
407
+ /// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
408
+ /// <para>Performance: O(n log n) worst/average case. Stable sort.</para>
409
+ /// <para>Allocations: Uses pooled temporary buffers sized to half of the list.</para>
410
+ /// <para>Edge cases: Empty or single element lists require no sorting.</para>
411
+ /// </remarks>
412
+ public static void GrailSort<T, TComparer>(this IList<T> array, TComparer comparer)
413
+ where TComparer : IComparer<T>
414
+ {
415
+ int count = array.Count;
416
+ if (count < 2)
417
+ {
418
+ return;
419
+ }
420
+
421
+ int bufferLength = count / 2 + 1;
422
+ using PooledResource<T[]> bufferLease = WallstopFastArrayPool<T>.Get(
423
+ bufferLength,
424
+ out T[] buffer
425
+ );
426
+ GrailSortRange(array, buffer, 0, count - 1, comparer);
427
+ }
428
+
429
+ /*
430
+ Implementation reference: Powersort (Munro, Wild) - adaptive mergesort leveraging natural runs.
431
+ https://arxiv.org/abs/1805.04154 (Creative Commons Attribution 4.0)
432
+
433
+ This adaptation detects natural runs and merges them using pooled buffers, providing a stable adaptive sort.
434
+ */
435
+ /// <summary>
436
+ /// Sorts the elements in the list using the Power Sort algorithm, which exploits existing runs and merges them
437
+ /// in near-optimal order.
438
+ /// </summary>
439
+ /// <typeparam name="T">The type of elements in the list.</typeparam>
440
+ /// <typeparam name="TComparer">The type of comparer.</typeparam>
441
+ /// <param name="array">The list to sort.</param>
442
+ /// <param name="comparer">The comparer to use for element comparisons.</param>
443
+ /// <remarks>
444
+ /// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
445
+ /// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
446
+ /// <para>Performance: O(n log n) worst/average case, approaching O(n) on partially sorted data. Stable sort.</para>
447
+ /// <para>Allocations: Uses pooled lists and buffers for run management and merging.</para>
448
+ /// <para>Edge cases: Empty or single element lists require no sorting.</para>
449
+ /// </remarks>
450
+ public static void PowerSort<T, TComparer>(this IList<T> array, TComparer comparer)
451
+ where TComparer : IComparer<T>
452
+ {
453
+ int count = array.Count;
454
+ if (count < 2)
455
+ {
456
+ return;
457
+ }
458
+
459
+ using PooledResource<List<(int start, int length)>> runBuffer = Buffers<(
460
+ int start,
461
+ int length
462
+ )>.List.Get(out List<(int start, int length)> runs);
463
+ using PooledResource<List<(int start, int length)>> mergeBuffer = Buffers<(
464
+ int start,
465
+ int length
466
+ )>.List.Get(out List<(int start, int length)> mergedRuns);
467
+
468
+ CollectNaturalRuns(array, comparer, runs);
469
+ if (runs.Count <= 1)
470
+ {
471
+ return;
472
+ }
473
+
474
+ int bufferLength = count / 2 + 1;
475
+ using PooledResource<T[]> tempLease = WallstopFastArrayPool<T>.Get(
476
+ bufferLength,
477
+ out T[] buffer
478
+ );
479
+
480
+ while (runs.Count > 1)
481
+ {
482
+ mergedRuns.Clear();
483
+ int runCount = runs.Count;
484
+ for (int i = 0; i < runCount; i += 2)
485
+ {
486
+ if (i + 1 >= runCount)
487
+ {
488
+ mergedRuns.Add(runs[i]);
489
+ continue;
490
+ }
491
+
492
+ (int start, int length) leftRun = runs[i];
493
+ (int start, int length) rightRun = runs[i + 1];
494
+ MergeRuns(
495
+ array,
496
+ buffer,
497
+ leftRun.start,
498
+ leftRun.length,
499
+ rightRun.start,
500
+ rightRun.length,
501
+ comparer
502
+ );
503
+ mergedRuns.Add((leftRun.start, leftRun.length + rightRun.length));
504
+ }
505
+
506
+ (runs, mergedRuns) = (mergedRuns, runs);
245
507
  }
246
508
  }
247
509
 
248
510
  /*
249
- Implementation copyright Will Stafford Parsons,
250
- https://github.com/wstaffordp/ghostsort/blob/master/src/ghostsort.c
511
+ Implementation copyright Will Stafford Parsons.
512
+ Original reference: ghostsort by Will Stafford Parsons (formerly hosted on GitHub).
513
+ Repository is currently unavailable; preserved locally until an official mirror returns.
251
514
 
252
515
  Note: Ghost Sort is currently not stable.
253
516
 
@@ -316,6 +579,451 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
316
579
  }
317
580
  }
318
581
 
582
+ private static void PatternDefeatingQuickSortRange<T, TComparer>(
583
+ IList<T> array,
584
+ int left,
585
+ int right,
586
+ TComparer comparer,
587
+ int depthLimit
588
+ )
589
+ where TComparer : IComparer<T>
590
+ {
591
+ const int insertionThreshold = 16;
592
+ while (right - left > insertionThreshold)
593
+ {
594
+ if (depthLimit == 0)
595
+ {
596
+ HeapSortRange(array, left, right, comparer);
597
+ return;
598
+ }
599
+
600
+ int pivotIndex = SelectPivotIndex(array, left, right, comparer);
601
+ (int pivotStart, int pivotEnd, bool swapped) = PartitionRange(
602
+ array,
603
+ left,
604
+ right,
605
+ pivotIndex,
606
+ comparer
607
+ );
608
+
609
+ if (!swapped && IsRangeSorted(array, left, right, comparer))
610
+ {
611
+ return;
612
+ }
613
+
614
+ depthLimit--;
615
+
616
+ int leftSize = pivotStart - left;
617
+ int rightSize = right - pivotEnd;
618
+
619
+ if (leftSize < rightSize)
620
+ {
621
+ if (leftSize > 0)
622
+ {
623
+ PatternDefeatingQuickSortRange(
624
+ array,
625
+ left,
626
+ pivotStart - 1,
627
+ comparer,
628
+ depthLimit
629
+ );
630
+ }
631
+ left = pivotEnd + 1;
632
+ }
633
+ else
634
+ {
635
+ if (rightSize > 0)
636
+ {
637
+ PatternDefeatingQuickSortRange(
638
+ array,
639
+ pivotEnd + 1,
640
+ right,
641
+ comparer,
642
+ depthLimit
643
+ );
644
+ }
645
+ right = pivotStart - 1;
646
+ }
647
+ }
648
+
649
+ InsertionSortRange(array, left, right, comparer);
650
+ }
651
+
652
+ private static int SelectPivotIndex<T, TComparer>(
653
+ IList<T> array,
654
+ int left,
655
+ int right,
656
+ TComparer comparer
657
+ )
658
+ where TComparer : IComparer<T>
659
+ {
660
+ int mid = left + ((right - left) >> 1);
661
+ if (0 < comparer.Compare(array[left], array[mid]))
662
+ {
663
+ array.Swap(left, mid);
664
+ }
665
+ if (0 < comparer.Compare(array[left], array[right]))
666
+ {
667
+ array.Swap(left, right);
668
+ }
669
+ if (0 < comparer.Compare(array[mid], array[right]))
670
+ {
671
+ array.Swap(mid, right);
672
+ }
673
+ return mid;
674
+ }
675
+
676
+ private static (int pivotStart, int pivotEnd, bool swapped) PartitionRange<T, TComparer>(
677
+ IList<T> array,
678
+ int left,
679
+ int right,
680
+ int pivotIndex,
681
+ TComparer comparer
682
+ )
683
+ where TComparer : IComparer<T>
684
+ {
685
+ array.Swap(left, pivotIndex);
686
+ T pivot = array[left];
687
+ int i = left + 1;
688
+ int j = right;
689
+ bool swapped = false;
690
+
691
+ while (i <= j)
692
+ {
693
+ while (i <= j && comparer.Compare(array[i], pivot) < 0)
694
+ {
695
+ i++;
696
+ }
697
+
698
+ while (i <= j && comparer.Compare(array[j], pivot) > 0)
699
+ {
700
+ j--;
701
+ }
702
+
703
+ if (i > j)
704
+ {
705
+ break;
706
+ }
707
+
708
+ if (i < j)
709
+ {
710
+ array.Swap(i, j);
711
+ swapped = true;
712
+ }
713
+
714
+ i++;
715
+ j--;
716
+ }
717
+
718
+ int pivotPosition = j;
719
+ array.Swap(left, pivotPosition);
720
+
721
+ int pivotStart = pivotPosition;
722
+ int pivotEnd = pivotPosition;
723
+
724
+ while (pivotStart > left && comparer.Compare(array[pivotStart - 1], pivot) == 0)
725
+ {
726
+ pivotStart--;
727
+ }
728
+
729
+ while (pivotEnd < right && comparer.Compare(array[pivotEnd + 1], pivot) == 0)
730
+ {
731
+ pivotEnd++;
732
+ }
733
+
734
+ return (pivotStart, pivotEnd, swapped || pivotIndex != pivotPosition);
735
+ }
736
+
737
+ private static void GrailSortRange<T, TComparer>(
738
+ IList<T> array,
739
+ T[] buffer,
740
+ int left,
741
+ int right,
742
+ TComparer comparer
743
+ )
744
+ where TComparer : IComparer<T>
745
+ {
746
+ if (left >= right)
747
+ {
748
+ return;
749
+ }
750
+
751
+ int mid = left + ((right - left) >> 1);
752
+ GrailSortRange(array, buffer, left, mid, comparer);
753
+ GrailSortRange(array, buffer, mid + 1, right, comparer);
754
+
755
+ if (comparer.Compare(array[mid], array[mid + 1]) <= 0)
756
+ {
757
+ return;
758
+ }
759
+
760
+ MergeRuns(array, buffer, left, mid - left + 1, mid + 1, right - mid, comparer);
761
+ }
762
+
763
+ private static void MergeRuns<T, TComparer>(
764
+ IList<T> array,
765
+ T[] buffer,
766
+ int leftStart,
767
+ int leftLength,
768
+ int rightStart,
769
+ int rightLength,
770
+ TComparer comparer
771
+ )
772
+ where TComparer : IComparer<T>
773
+ {
774
+ if (leftLength == 0 || rightLength == 0)
775
+ {
776
+ return;
777
+ }
778
+
779
+ int leftEnd = leftStart + leftLength - 1;
780
+ int rightEnd = rightStart + rightLength - 1;
781
+ if (comparer.Compare(array[leftEnd], array[rightStart]) <= 0)
782
+ {
783
+ return;
784
+ }
785
+
786
+ if (leftLength <= rightLength)
787
+ {
788
+ for (int i = 0; i < leftLength; ++i)
789
+ {
790
+ buffer[i] = array[leftStart + i];
791
+ }
792
+
793
+ int leftIndex = 0;
794
+ int rightIndex = rightStart;
795
+ int dest = leftStart;
796
+ int leftLimit = leftLength;
797
+
798
+ while (leftIndex < leftLimit && rightIndex <= rightEnd)
799
+ {
800
+ if (0 < comparer.Compare(buffer[leftIndex], array[rightIndex]))
801
+ {
802
+ array[dest] = array[rightIndex];
803
+ rightIndex++;
804
+ }
805
+ else
806
+ {
807
+ array[dest] = buffer[leftIndex];
808
+ leftIndex++;
809
+ }
810
+ dest++;
811
+ }
812
+
813
+ while (leftIndex < leftLimit)
814
+ {
815
+ array[dest] = buffer[leftIndex];
816
+ leftIndex++;
817
+ dest++;
818
+ }
819
+ }
820
+ else
821
+ {
822
+ for (int i = 0; i < rightLength; ++i)
823
+ {
824
+ buffer[i] = array[rightStart + i];
825
+ }
826
+
827
+ int leftIndex = leftEnd;
828
+ int rightIndex = rightLength - 1;
829
+ int dest = rightEnd;
830
+
831
+ while (leftIndex >= leftStart && rightIndex >= 0)
832
+ {
833
+ if (0 < comparer.Compare(array[leftIndex], buffer[rightIndex]))
834
+ {
835
+ array[dest] = array[leftIndex];
836
+ leftIndex--;
837
+ }
838
+ else
839
+ {
840
+ array[dest] = buffer[rightIndex];
841
+ rightIndex--;
842
+ }
843
+ dest--;
844
+ }
845
+
846
+ while (rightIndex >= 0)
847
+ {
848
+ array[dest] = buffer[rightIndex];
849
+ rightIndex--;
850
+ dest--;
851
+ }
852
+ }
853
+ }
854
+
855
+ private static void CollectNaturalRuns<T, TComparer>(
856
+ IList<T> array,
857
+ TComparer comparer,
858
+ List<(int start, int length)> runs
859
+ )
860
+ where TComparer : IComparer<T>
861
+ {
862
+ runs.Clear();
863
+ int count = array.Count;
864
+ int index = 0;
865
+ while (index < count)
866
+ {
867
+ int start = index;
868
+ index++;
869
+ if (index == count)
870
+ {
871
+ runs.Add((start, 1));
872
+ break;
873
+ }
874
+
875
+ int compare = comparer.Compare(array[index - 1], array[index]);
876
+ bool ascending = compare <= 0;
877
+
878
+ while (index < count)
879
+ {
880
+ int nextCompare = comparer.Compare(array[index - 1], array[index]);
881
+ if (ascending)
882
+ {
883
+ if (nextCompare <= 0)
884
+ {
885
+ index++;
886
+ continue;
887
+ }
888
+ }
889
+ else
890
+ {
891
+ if (nextCompare >= 0)
892
+ {
893
+ index++;
894
+ continue;
895
+ }
896
+ }
897
+ break;
898
+ }
899
+
900
+ int end = index - 1;
901
+ if (!ascending && start < end)
902
+ {
903
+ Reverse(array, start, end);
904
+ }
905
+
906
+ runs.Add((start, end - start + 1));
907
+ }
908
+ }
909
+
910
+ private static void InsertionSortRange<T, TComparer>(
911
+ IList<T> array,
912
+ int left,
913
+ int right,
914
+ TComparer comparer
915
+ )
916
+ where TComparer : IComparer<T>
917
+ {
918
+ if (left >= right)
919
+ {
920
+ return;
921
+ }
922
+
923
+ for (int i = left + 1; i <= right; ++i)
924
+ {
925
+ T key = array[i];
926
+ int j = i - 1;
927
+ while (j >= left && 0 < comparer.Compare(array[j], key))
928
+ {
929
+ array[j + 1] = array[j];
930
+ j--;
931
+ }
932
+ array[j + 1] = key;
933
+ }
934
+ }
935
+
936
+ private static void HeapSortRange<T, TComparer>(
937
+ IList<T> array,
938
+ int start,
939
+ int end,
940
+ TComparer comparer
941
+ )
942
+ where TComparer : IComparer<T>
943
+ {
944
+ int length = end - start + 1;
945
+ if (length <= 1)
946
+ {
947
+ return;
948
+ }
949
+
950
+ for (int i = (length >> 1) - 1; i >= 0; --i)
951
+ {
952
+ SiftDown(array, start, length, i, comparer);
953
+ }
954
+
955
+ for (int i = length - 1; i > 0; --i)
956
+ {
957
+ array.Swap(start, start + i);
958
+ SiftDown(array, start, i, 0, comparer);
959
+ }
960
+ }
961
+
962
+ private static void SiftDown<T, TComparer>(
963
+ IList<T> array,
964
+ int start,
965
+ int length,
966
+ int root,
967
+ TComparer comparer
968
+ )
969
+ where TComparer : IComparer<T>
970
+ {
971
+ while (true)
972
+ {
973
+ int child = (root << 1) + 1;
974
+ if (child >= length)
975
+ {
976
+ return;
977
+ }
978
+
979
+ int rightChild = child + 1;
980
+ if (
981
+ rightChild < length
982
+ && comparer.Compare(array[start + child], array[start + rightChild]) < 0
983
+ )
984
+ {
985
+ child = rightChild;
986
+ }
987
+
988
+ if (comparer.Compare(array[start + root], array[start + child]) >= 0)
989
+ {
990
+ return;
991
+ }
992
+
993
+ array.Swap(start + root, start + child);
994
+ root = child;
995
+ }
996
+ }
997
+
998
+ private static bool IsRangeSorted<T, TComparer>(
999
+ IList<T> array,
1000
+ int left,
1001
+ int right,
1002
+ TComparer comparer
1003
+ )
1004
+ where TComparer : IComparer<T>
1005
+ {
1006
+ for (int i = left + 1; i <= right; ++i)
1007
+ {
1008
+ if (0 < comparer.Compare(array[i - 1], array[i]))
1009
+ {
1010
+ return false;
1011
+ }
1012
+ }
1013
+ return true;
1014
+ }
1015
+
1016
+ private static int FloorLog2(int value)
1017
+ {
1018
+ int result = 0;
1019
+ while (value > 1)
1020
+ {
1021
+ value >>= 1;
1022
+ result++;
1023
+ }
1024
+ return result;
1025
+ }
1026
+
319
1027
  /// <summary>
320
1028
  /// Sorts a list of Unity Objects by their name property in ascending alphabetical order.
321
1029
  /// </summary>