Skip to content

Commit c14a11f

Browse files
Updated docs. Added more tests.
1 parent 93505c9 commit c14a11f

File tree

9 files changed

+260
-87
lines changed

9 files changed

+260
-87
lines changed

source/Combinations.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ public static IEnumerable<int[]> GetPossibleIndexesBuffer(int length, int? bound
5757
var result = pool.Rent(length);
5858
try
5959
{
60-
return CopyTo(result, length, bounds);
60+
foreach (var e in CopyTo(result, length, bounds))
61+
yield return e;
6162
}
6263
finally
6364
{

source/Extensions.Combinations.cs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ bool GetNext()
7676
/// <summary>
7777
/// Enumerates all possible combinations of values.
7878
/// Results can be different permutations of another set.
79-
/// Examples:
79+
///
80+
/// Example:
8081
/// [0, 0], [0, 1], [1, 0], [1, 1] where [0, 1] and [1, 0] are a different permutatation of the same set.
8182
/// </summary>
8283
/// <param name="elements">The elements to draw from.</param>
8384
/// <param name="length">The length of each result.</param>
85+
/// <param name="buffer">An optional buffer that is filled with the values and returned as the yielded value instead of a new array</param>
8486
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int length, T[]? buffer = null)
8587
{
8688
if (elements is null)
@@ -97,12 +99,13 @@ public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int
9799
/// <summary>
98100
/// Enumerates all possible distinct set combinations.
99101
/// A set that has its items reordered is not distinct from the original.
100-
/// Examples:
102+
///
103+
/// Example:
101104
/// [0, 0], [0, 1], [1, 1] where [1, 0] is not included as it is not a disticnt set from [0, 1].
102-
///
103105
/// </summary>
104106
/// <param name="elements">The elements to draw from.</param>
105107
/// <param name="length">The length of each result.</param>
108+
/// <param name="buffer">An optional buffer that is filled with the values and returned as the yielded value instead of a new array</param>
106109
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements, int length, T[]? buffer = null)
107110
{
108111
if (elements is null)
@@ -138,7 +141,6 @@ public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int
138141
/// </summary>
139142
/// <param name="elements">The elements to draw from.</param>
140143
/// <param name="length">The length of each result.</param>
141-
/// <param name="uniqueOnly">Finds all possible subsets instead of all possible combinations of values.</param>
142144
public static IEnumerable<T[]> CombinationsBuffered<T>(this IEnumerable<T> elements, int length)
143145
{
144146
if (elements is null)
@@ -149,20 +151,54 @@ public static IEnumerable<T[]> CombinationsBuffered<T>(this IEnumerable<T> eleme
149151

150152
if (length == 0)
151153
{
152-
return Enumerable.Empty<T[]>();
154+
yield break;
153155
}
154156

155-
var arrayPool = ArrayPool<T>.Shared;
156-
var buffer = arrayPool.Rent(length);
157+
var pool = ArrayPool<T>.Shared;
158+
var buffer = pool.Rent(length);
157159
try
158160
{
159-
return Combinations(elements, length, buffer);
161+
foreach(var c in Combinations(elements, length, buffer))
162+
yield return c;
160163
}
161164
finally
162165
{
163-
arrayPool.Return(buffer, true);
166+
pool.Return(buffer, true);
164167
}
165168
}
166169

170+
171+
/// <summary>
172+
/// Enumerates all possible combinations of values.
173+
/// Results can be different permutations of another set.
174+
/// Examples:
175+
/// [0, 0], [0, 1], [1, 0], [1, 1] where [0, 1] and [1, 0] are a different permutatation of the same set.
176+
/// </summary>
177+
/// <param name="elements">The elements to draw from.</param>\
178+
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements)
179+
{
180+
if (elements is null)
181+
throw new ArgumentNullException(nameof(elements));
182+
Contract.EndContractBlock();
183+
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
184+
return source.Count == 0 ? Enumerable.Empty<T[]>() : Combinations(source, source.Count);
185+
}
186+
187+
/// <summary>
188+
/// Enumerates all possible distinct set combinations.
189+
/// A set that has its items reordered is not distinct from the original.
190+
/// Examples:
191+
/// [0, 0], [0, 1], [1, 1] where [1, 0] is not included as it is not a disticnt set from [0, 1].
192+
///
193+
/// </summary>
194+
/// <param name="elements">The elements to draw from.</param>
195+
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements)
196+
{
197+
if (elements is null)
198+
throw new ArgumentNullException(nameof(elements));
199+
Contract.EndContractBlock();
200+
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
201+
return source.Count == 0 ? Enumerable.Empty<T[]>() : CombinationsDistinct(source, source.Count);
202+
}
167203
}
168204
}

source/Extensions.Permutations.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Collections.Generic;
4+
using System.Diagnostics.Contracts;
5+
using System.Linq;
6+
7+
namespace Open.Collections
8+
{
9+
public static partial class Extensions
10+
{
11+
/// <summary>
12+
/// Enumerates all possible unique permutations of a given set.
13+
/// [A, B, C] results in [A, B, C], [A, C, B], [B, A, C], [C, A, B], [B, C, A], [C, B, A]
14+
/// </summary>]
15+
/// <param name="elements">The elements to derive from.</param>
16+
/// <param name="buffer">An optional buffer that is filled with the values and returned as the yielded value instead of a new array</param>
17+
public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> elements, T[]? buffer = null)
18+
{
19+
// based on: https://stackoverflow.com/questions/1145703/permutation-of-string-without-recursion
20+
21+
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
22+
var count = source.Count;
23+
if (count == 0) yield break;
24+
if (buffer != null && count > buffer.Length)
25+
throw new ArgumentOutOfRangeException(nameof(buffer), buffer, "Length is less than the number of elements.");
26+
27+
var max = 1;
28+
for (var i = 2; i <= count; i++) max *= i;
29+
30+
var a = new int[count];
31+
var pos = new List<T>(count);
32+
33+
for (int j = 0; j < max; ++j)
34+
{
35+
pos.AddRange(source);
36+
37+
int i;
38+
var n = j;
39+
var c = 0;
40+
41+
for (i = count; i > 0; --i)
42+
{
43+
var m = n; n /= i;
44+
a[c++] = m % i;
45+
}
46+
47+
// Avoid copy if not needed.
48+
var result = buffer ?? new T[count];
49+
for (i = 0; i < count; i++)
50+
{
51+
var index = a[i];
52+
result[i] = pos[index];
53+
pos.RemoveAt(index);
54+
}
55+
56+
yield return result;
57+
}
58+
}
59+
60+
/// <summary>
61+
/// Enumerates all possible unique permutations of a given set.
62+
/// Values are yielded as a reused array.
63+
///
64+
/// Example:
65+
/// [A, B, C] results in [A, B, C], [A, C, B], [B, A, C], [C, A, B], [B, C, A], [C, B, A]
66+
/// </summary>]
67+
/// <param name="elements">The elements to derive from.</param>
68+
public static IEnumerable<T[]> PermutationsBuffered<T>(this IReadOnlyList<T> elements)
69+
{
70+
if (elements is null)
71+
throw new ArgumentNullException(nameof(elements));
72+
Contract.EndContractBlock();
73+
74+
var count = elements.Count;
75+
if (count == 0) yield break;
76+
77+
var pool = ArrayPool<T>.Shared;
78+
var buffer = pool.Rent(count);
79+
try
80+
{
81+
foreach (var p in Permutations(elements, buffer))
82+
yield return p;
83+
}
84+
finally
85+
{
86+
pool.Return(buffer, true);
87+
}
88+
}
89+
}
90+
}

source/Extensions.Subsets.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Buffers;
33
using System.Collections.Generic;
4-
using System.Diagnostics;
54

65
namespace Open.Collections
76
{

source/Extensions.cs

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -835,71 +835,5 @@ public static int IndexOf<T>(this T[] source, T value)
835835
return -1;
836836
}
837837

838-
public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> elements, T[]? buffer = null)
839-
{
840-
// based on: https://stackoverflow.com/questions/1145703/permutation-of-string-without-recursion
841-
842-
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
843-
var count = source.Count;
844-
if (count == 0) yield break;
845-
if (buffer != null && count > buffer.Length)
846-
throw new ArgumentOutOfRangeException(nameof(buffer), buffer, "Length is less than the number of elements.");
847-
848-
var max = 1;
849-
for (var i = 2; i <= count; i++) max *= i;
850-
851-
var a = new int[count];
852-
var pos = new List<T>(count);
853-
854-
for (int j = 0; j < max; ++j)
855-
{
856-
pos.AddRange(source);
857-
858-
int i;
859-
var n = j;
860-
var c = 0;
861-
862-
for (i = count; i > 0; --i)
863-
{
864-
var m = n; n /= i;
865-
a[c++] = m % i;
866-
}
867-
868-
// Avoid copy if not needed.
869-
var result = buffer ?? new T[count];
870-
for (i = 0; i < count; i++)
871-
{
872-
var index = a[i];
873-
result[i] = pos[index];
874-
pos.RemoveAt(index);
875-
}
876-
877-
yield return result;
878-
}
879-
}
880-
881-
public static IEnumerable<T[]> PermutationsBuffered<T>(this IReadOnlyList<T> elements)
882-
{
883-
if (elements is null)
884-
throw new ArgumentNullException(nameof(elements));
885-
Contract.EndContractBlock();
886-
887-
var count = elements.Count;
888-
if (count == 0)
889-
{
890-
return Enumerable.Empty<T[]>();
891-
}
892-
893-
var arrayPool = ArrayPool<T>.Shared;
894-
var buffer = arrayPool.Rent(count);
895-
try
896-
{
897-
return Permutations(elements, buffer);
898-
}
899-
finally
900-
{
901-
arrayPool.Return(buffer, true);
902-
}
903-
}
904838
}
905839
}

source/Open.Collections.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Collections/</PackageProjectUrl>
1717
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Collections/</RepositoryUrl>
1818
<RepositoryType>git</RepositoryType>
19-
<Version>2.9.1</Version>
19+
<Version>2.10.0</Version>
2020
<PackageReleaseNotes></PackageReleaseNotes>
2121
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2222
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
using System.Linq;
4+
using Xunit;
5+
6+
namespace Open.Collections.Tests
7+
{
8+
public class CombinationTests
9+
{
10+
static ImmutableArray<int> Set1 = ImmutableArray.Create(1, 2);
11+
static ImmutableArray<char> Set2 = ImmutableArray.Create('A', 'C', 'E');
12+
13+
[Fact]
14+
public void TestCombination1()
15+
{
16+
var expected = new int[][] {
17+
new int[] { 1, 1 },
18+
new int[] { 1, 2 },
19+
new int[] { 2, 1 },
20+
new int[] { 2, 2 },
21+
};
22+
var actual = Set1.Combinations().ToArray();
23+
Assert.Equal(expected, actual);
24+
}
25+
26+
[Fact]
27+
public void TestCombination1Distinct()
28+
{
29+
var expected = new int[][] {
30+
new int[] { 1, 1 },
31+
new int[] { 1, 2 },
32+
new int[] { 2, 2 },
33+
};
34+
var actual = Set1.CombinationsDistinct().ToArray();
35+
Assert.Equal(expected, actual);
36+
}
37+
38+
[Fact]
39+
public void TestCombination2()
40+
{
41+
var expected = new char[][] {
42+
new char[] { 'A', 'A' },
43+
new char[] { 'A', 'C' },
44+
new char[] { 'A', 'E' },
45+
new char[] { 'C', 'A' },
46+
new char[] { 'C', 'C' },
47+
new char[] { 'C', 'E' },
48+
new char[] { 'E', 'A' },
49+
new char[] { 'E', 'C' },
50+
new char[] { 'E', 'E' },
51+
};
52+
var actual = Set2.Combinations(2).ToArray();
53+
Assert.Equal(expected, actual);
54+
}
55+
56+
[Fact]
57+
public void TestCombination2Distinct()
58+
{
59+
var expected = new char[][] {
60+
new char[] { 'A', 'A' },
61+
new char[] { 'A', 'C' },
62+
new char[] { 'A', 'E' },
63+
new char[] { 'C', 'C' },
64+
new char[] { 'C', 'E' },
65+
new char[] { 'E', 'E' },
66+
};
67+
var actual = Set2.CombinationsDistinct(2).ToArray();
68+
Assert.Equal(expected, actual);
69+
}
70+
71+
}
72+
}

0 commit comments

Comments
 (0)