From 9d4051ece145ede078d1b2f69ab79ba2324df517 Mon Sep 17 00:00:00 2001 From: Sebastian Lindemeier Date: Tue, 16 Dec 2025 07:56:20 +0100 Subject: [PATCH] Refactor `Permutations` in `EnumerableExtensions`, add `PermutationsWithRepeats` method, and update tests for improved validation and clarity --- .../EnumerableExtensionsTest.cs | 54 ++++++++++--------- .../EnumerableExtensions.cs | 43 ++++++++++++++- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/AdventOfCode.Extensions.Tests/EnumerableExtensionsTest.cs b/AdventOfCode.Extensions.Tests/EnumerableExtensionsTest.cs index 5635770..7f0e479 100644 --- a/AdventOfCode.Extensions.Tests/EnumerableExtensionsTest.cs +++ b/AdventOfCode.Extensions.Tests/EnumerableExtensionsTest.cs @@ -9,9 +9,8 @@ public class EnumerableExtensionsTest int[] data = [1, 2]; var actual = data.Combinations(2).ToArray(); - - Assert.Contains([1, 2], actual); - Assert.Single(actual); + + Assert.Equal([[1, 2]], actual); } [Fact] @@ -21,10 +20,7 @@ public class EnumerableExtensionsTest var actual = data.Combinations(2).ToArray(); - Assert.Contains([1, 2], actual); - Assert.Contains([1, 3], actual); - Assert.Contains([2, 3], actual); - Assert.Equal(3, actual.Length); + Assert.Equal([[1, 2], [1, 3], [2, 3]], actual); } [Fact] @@ -33,9 +29,8 @@ public class EnumerableExtensionsTest int[] data = [1, 2, 3]; var actual = data.Combinations(3).ToArray(); - - Assert.Contains([1, 2, 3], actual); - Assert.Single(actual); + + Assert.Equal([[1, 2, 3]], actual); } [Fact] @@ -69,33 +64,42 @@ public class EnumerableExtensionsTest } [Fact] - public void Permutations_12_2_equals_11_12_21_22() + public void Permutations_repeated_12_2_equals_11_12_21_22() + { + int[] data = [1, 2]; + + var actual = data.PermutationsWithRepeats(2).ToArray(); + + Assert.Equal([[1, 1], [1, 2], [2, 1], [2, 2]], actual); + } + + [Fact] + public void Permutations_12_2_equals_12_21() { int[] data = [1, 2]; var actual = data.Permutations(2).ToArray(); - Assert.Contains([1, 1], actual); - Assert.Contains([1, 2], actual); - Assert.Contains([2, 1], actual); - Assert.Contains([2, 2], actual); - Assert.Equal(4, actual.Length); + Assert.Equal([[1, 2], [2, 1]], actual); } [Fact] - public void Permutations_123_2_equals_11_12_13_21_22_23_31_32_33() + public void Permutations_repeated_123_2_equals_11_12_13_21_22_23_31_32_33() + { + int[] data = [1, 2, 3]; + + var actual = data.PermutationsWithRepeats(2).ToArray(); + + Assert.Equal([[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]], actual); + } + + [Fact] + public void Permutations_123_2_equals_12_13_21_23_31_32() { int[] data = [1, 2, 3]; var actual = data.Permutations(2).ToArray(); - Assert.Contains([1, 1], actual); - Assert.Contains([1, 2], actual); - Assert.Contains([2, 1], actual); - Assert.Contains([2, 2], actual); - Assert.Contains([3, 1], actual); - Assert.Contains([3, 2], actual); - Assert.Contains([3, 3], actual); - Assert.Equal(9, actual.Length); + Assert.Equal([[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]], actual); } } \ No newline at end of file diff --git a/AdventOfCode.Extensions/EnumerableExtensions.cs b/AdventOfCode.Extensions/EnumerableExtensions.cs index 1994e6d..6df27a8 100644 --- a/AdventOfCode.Extensions/EnumerableExtensions.cs +++ b/AdventOfCode.Extensions/EnumerableExtensions.cs @@ -68,7 +68,7 @@ public static class EnumerableExtensions } } - public static IEnumerable Permutations(this IEnumerable values, int count) + public static IEnumerable PermutationsWithRepeats(this IEnumerable values, int count) { var pool = values.ToArray(); if (count > pool.Length) @@ -96,6 +96,47 @@ public static class EnumerableExtensions } } + public static IEnumerable Permutations(this IEnumerable values, int count) + { + var pool = values.ToArray(); + var poolLength = pool.Length; + if (count > poolLength) + yield break; + var indices = Enumerable.Range(0, poolLength).ToArray(); + var cycles = Enumerable.Range(0, poolLength + 1) + .Skip(poolLength - count + 1) + .Reverse() + .ToArray(); + yield return GetCombination(indices[..count], pool); + var notDone = true; + while (notDone) + { + notDone = false; + for (var i = count - 1; i >= 0; i--) + { + cycles[i] -= 1; + if (cycles[i] == 0) + { + var tmp = indices[i]; + for (var j = i; j < indices.Length - 1; j++) + { + indices[j] = indices[j + 1]; + } + indices[^1] = tmp; + cycles[i] = poolLength - i; + } + else + { + var j = cycles[i]; + (indices[i], indices[^j]) = (indices[^j], indices[i]); + yield return GetCombination(indices[..count], pool); + notDone = true; + break; + } + } + } + } + private static TValue[] GetCombination(int[] innerIndices, TValue[] innerPool) => innerIndices.Select(i => innerPool[i]).ToArray(); } \ No newline at end of file