Skip to content

Commit a8e38d8

Browse files
Testing, fixes, and documentation.
1 parent 50015f9 commit a8e38d8

10 files changed

Lines changed: 230 additions & 174 deletions

Open.Collections.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Collections", "source\
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Collections.Tests", "testing\Open.Collections.Tests\Open.Collections.Tests.csproj", "{44063AEE-8909-4A26-80BE-8623F5EBBA1F}"
99
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Collections.Benchmarking", "benchmarking\Open.Collections.Benchmarking.csproj", "{A3EA31B7-6E9B-4780-808A-BBDF116521EA}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
2123
{44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{A3EA31B7-6E9B-4780-808A-BBDF116521EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{A3EA31B7-6E9B-4780-808A-BBDF116521EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{A3EA31B7-6E9B-4780-808A-BBDF116521EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{A3EA31B7-6E9B-4780-808A-BBDF116521EA}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using BenchmarkDotNet.Attributes;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Open.Collections.Benchmarks
6+
{
7+
public class SubsetBenchmarks
8+
{
9+
readonly IEnumerable<int> FullSet = Enumerable.Range(0, 10);
10+
11+
[Benchmark]
12+
public int Subset()
13+
{
14+
var buffer = new int[7];
15+
var sum = FullSet.MemoizeUnsafe().Subsets(7, buffer).SelectMany(e => e).Sum();
16+
return sum;
17+
}
18+
19+
[Benchmark]
20+
public int SubsetProgressive()
21+
{
22+
var buffer = new int[7];
23+
var sum = FullSet.MemoizeUnsafe().SubsetsProgressive(7, buffer).SelectMany(e => e).Sum();
24+
return sum;
25+
}
26+
}
27+
}

benchmarking/Open.Collections.Benchmarking.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
<LangVersion>latest</LangVersion>
1616
</PropertyGroup>
1717

18+
<ItemGroup>
19+
<Compile Remove="BenchmarkDotNet.Artifacts\**" />
20+
<EmbeddedResource Remove="BenchmarkDotNet.Artifacts\**" />
21+
<None Remove="BenchmarkDotNet.Artifacts\**" />
22+
</ItemGroup>
23+
1824
<ItemGroup>
1925
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
2026
<PackageReference Include="Open.Diagnostics" Version="1.4.1" />

benchmarking/Program.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
1-
using Open.Collections;
1+
using BenchmarkDotNet.Running;
2+
using Open.Collections;
3+
using Open.Collections.Benchmarks;
24
using Open.Collections.Synchronized;
35
using Open.Diagnostics;
46
using System;
57
using System.Collections.Generic;
68
using System.Diagnostics;
9+
using System.Linq;
710
using System.Threading.Tasks;
811

912
internal class Program
1013
{
1114
static void Main()
1215
{
13-
Console.Clear();
14-
var perms = new char[] { 'A', 'B', 'C' }.Permutations(new char[3]);
15-
foreach (var p in perms)
16-
{
17-
Console.WriteLine(new string(p));
18-
}
19-
20-
//var combs = new Open.Collections.Benchmarking.Combinations();
21-
//OutputList(combs.AllPossible());
22-
//OutputList(combs.Subsets());
23-
24-
//BenchmarkRunner.Run<Open.Collections.Benchmarking.Combinations>();
16+
BenchmarkRunner.Run<SubsetBenchmarks>();
2517

2618
//TestEntry.Test1();
2719
//TestEntry.Test2();

source/Extensions.Combinations.cs

Lines changed: 83 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Open.Collections
99
{
1010
public static partial class Extensions
1111
{
12-
static IEnumerable<T[]> CombinationsCore<T>(IReadOnlyList<T> source, int length, bool distinctSet, T[]? buffer = null)
12+
static IEnumerable<T[]> CombinationsCore<T>(IReadOnlyList<T> source, int length, bool distinctSet, T[] buffer)
1313
{
1414
Debug.Assert(length != 0);
1515
var count = source.Count;
@@ -59,12 +59,11 @@ bool GetNext()
5959

6060
while (GetNext())
6161
{
62-
var result = buffer ?? new T[length];
6362
for (var i = 0; i < length; i++)
6463
{
65-
result[i] = source[indexes[i]];
64+
buffer![i] = source[indexes[i]];
6665
}
67-
yield return result;
66+
yield return buffer!;
6867
}
6968
}
7069
finally
@@ -73,17 +72,25 @@ bool GetNext()
7372
}
7473
}
7574

76-
/// <summary>
77-
/// Enumerates all possible combinations of values.
78-
/// Results can be different permutations of another set.
79-
///
80-
/// Example:
81-
/// [0, 0], [0, 1], [1, 0], [1, 1] where [0, 1] and [1, 0] are a different permutatation of the same set.
82-
/// </summary>
83-
/// <param name="elements">The elements to draw from.</param>
84-
/// <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>
86-
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int length, T[]? buffer = null)
75+
static IEnumerable<T[]> CombinationsCore<T>(IReadOnlyList<T> source, int length, bool distinctSet)
76+
{
77+
var pool = ArrayPool<T>.Shared;
78+
var buffer = pool.Rent(length);
79+
try
80+
{
81+
foreach (var b in CombinationsCore(source, length,distinctSet, buffer))
82+
yield return b;
83+
}
84+
finally
85+
{
86+
pool.Return(buffer, true);
87+
}
88+
}
89+
90+
91+
/// <inheritdoc cref="Combinations{T}(IEnumerable{T}, int)"/>
92+
/// <param name="buffer">A buffer that is filled with the values and returned as the yielded value instead of a new array.</param>
93+
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int length, T[] buffer)
8794
{
8895
if (elements is null)
8996
throw new ArgumentNullException(nameof(elements));
@@ -96,17 +103,10 @@ public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int
96103
return source.Count == 0 ? Enumerable.Empty<T[]>() : CombinationsCore(source, length, false, buffer);
97104
}
98105

99-
/// <summary>
100-
/// Enumerates all possible distinct set combinations.
101-
/// A set that has its items reordered is not distinct from the original.
102-
///
103-
/// Example:
104-
/// [0, 0], [0, 1], [1, 1] where [1, 0] is not included as it is not a disticnt set from [0, 1].
105-
/// </summary>
106-
/// <param name="elements">The elements to draw from.</param>
107-
/// <param name="length">The length of each result.</param>
106+
107+
/// <inheritdoc cref="CombinationsDistinct{T}(IEnumerable{T}, int)"/>
108108
/// <param name="buffer">An optional buffer that is filled with the values and returned as the yielded value instead of a new array</param>
109-
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements, int length, T[]? buffer = null)
109+
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements, int length, T[] buffer)
110110
{
111111
if (elements is null)
112112
throw new ArgumentNullException(nameof(elements));
@@ -133,69 +133,105 @@ public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int
133133
var count = source.Count;
134134
if (count == 0) return Enumerable.Empty<T[]>();
135135

136-
return uniqueOnly ? source.Subsets(length) : CombinationsCore(source, length, true);
136+
if (uniqueOnly) return source.Subsets(length);
137+
return uniqueOnly ? source.Subsets(length) : CombinationsCore(source, length, true).Select(e=>e.AsCopy(length));
137138
}
138139

139-
/// <summary>
140-
/// Enumerates all possible (order retained) combinations of the source elements up to the length.
141-
/// </summary>
142-
/// <param name="elements">The elements to draw from.</param>
143-
/// <param name="length">The length of each result.</param>
144-
public static IEnumerable<T[]> CombinationsBuffered<T>(this IEnumerable<T> elements, int length)
140+
/// <inheritdoc cref="Combinations{T}(IEnumerable{T}, int)"/>
141+
/// <remarks>Values are yielded as read only memory buffer that should not be retained as its array is returned to pool afterwards.</remarks>
142+
/// <returns>An enumerable the yields as read only memory buffer that should not be retained as its array is returned to pool afterwards.</returns>
143+
public static IEnumerable<ReadOnlyMemory<T>> CombinationsBuffered<T>(this IEnumerable<T> elements, int length)
145144
{
146145
if (elements is null)
147146
throw new ArgumentNullException(nameof(elements));
148147
if (length < 0)
149148
throw new ArgumentOutOfRangeException(nameof(length), length, "Cannot be less than zero.");
150149
Contract.EndContractBlock();
151150

152-
if (length == 0)
151+
if (length == 0) yield break;
152+
153+
var pool = ArrayPool<T>.Shared;
154+
var buffer = pool.Rent(length);
155+
var readBuffer = new ReadOnlyMemory<T>(buffer, 0, length);
156+
try
157+
{
158+
foreach (var _ in Combinations(elements, length, buffer))
159+
yield return readBuffer;
160+
}
161+
finally
153162
{
154-
yield break;
163+
pool.Return(buffer, true);
155164
}
165+
}
166+
167+
168+
/// <inheritdoc cref="CombinationsDistinct{T}(IEnumerable{T}, int)"/>
169+
/// <remarks>Values are yielded as read only memory buffer that should not be retained as its array is returned to pool afterwards.</remarks>
170+
/// <returns>An enumerable the yields as read only memory buffer that should not be retained as its array is returned to pool afterwards.</returns>
171+
public static IEnumerable<ReadOnlyMemory<T>> CombinationsDistinctBuffered<T>(this IEnumerable<T> elements, int length)
172+
{
173+
if (elements is null)
174+
throw new ArgumentNullException(nameof(elements));
175+
if (length < 0)
176+
throw new ArgumentOutOfRangeException(nameof(length), length, "Cannot be less than zero.");
177+
Contract.EndContractBlock();
178+
179+
if (length == 0) yield break;
156180

157181
var pool = ArrayPool<T>.Shared;
158182
var buffer = pool.Rent(length);
183+
var readBuffer = new ReadOnlyMemory<T>(buffer, 0, length);
159184
try
160185
{
161-
foreach (var c in Combinations(elements, length, buffer))
162-
yield return c;
186+
foreach (var _ in CombinationsDistinct(elements, length, buffer))
187+
yield return readBuffer;
163188
}
164189
finally
165190
{
166191
pool.Return(buffer, true);
167192
}
168193
}
169194

195+
/// <inheritdoc cref="Combinations{T}(IEnumerable{T})"/>
196+
/// <param name="length">The length of each result.</param>
197+
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements, int length)
198+
{
199+
foreach (var c in CombinationsBuffered(elements, length))
200+
yield return c.ToArray();
201+
}
202+
203+
/// <inheritdoc cref="CombinationsDistinct{T}(IEnumerable{T})"/>
204+
/// <param name="length">The length of each result.</param>
205+
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements, int length)
206+
{
207+
foreach (var c in CombinationsDistinctBuffered(elements, length))
208+
yield return c.ToArray();
209+
}
210+
170211

171212
/// <summary>
172213
/// Enumerates all possible combinations of values.
173214
/// 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.
176215
/// </summary>
177-
/// <param name="elements">The elements to draw from.</param>\
216+
/// <example>[0, 0], [0, 1], [1, 0], [1, 1] where [0, 1] and [1, 0] are a different permutatation of the same set.</example>
217+
/// <param name="elements">The elements to draw from.</param>
178218
public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> elements)
179219
{
180-
if (elements is null)
181-
throw new ArgumentNullException(nameof(elements));
220+
if (elements is null) throw new ArgumentNullException(nameof(elements));
182221
Contract.EndContractBlock();
183222
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
184223
return source.Count == 0 ? Enumerable.Empty<T[]>() : Combinations(source, source.Count);
185224
}
186225

187226
/// <summary>
188227
/// 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-
///
228+
/// In contrast a set that has its items reordered is not distinct from the original.
193229
/// </summary>
230+
/// <example>[0, 0], [0, 1], [1, 1] where [1, 0] is not included as it is not a disticnt set from [0, 1].</example>
194231
/// <param name="elements">The elements to draw from.</param>
195232
public static IEnumerable<T[]> CombinationsDistinct<T>(this IEnumerable<T> elements)
196233
{
197-
if (elements is null)
198-
throw new ArgumentNullException(nameof(elements));
234+
if (elements is null) throw new ArgumentNullException(nameof(elements));
199235
Contract.EndContractBlock();
200236
var source = elements as IReadOnlyList<T> ?? elements.ToArray();
201237
return source.Count == 0 ? Enumerable.Empty<T[]>() : CombinationsDistinct(source, source.Count);

0 commit comments

Comments
 (0)