Skip to content

Commit 91d9788

Browse files
Added Subsets static for getting indexes instead of values.
1 parent 5b72ccb commit 91d9788

6 files changed

Lines changed: 143 additions & 9 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,4 @@ __pycache__/
274274
benchmarking/.vscode/launch.json
275275
benchmarking/.vscode/tasks.json
276276
/source/nuget.config
277+
BenchmarkDotNet.Artifacts

benchmarking/Benchmarks/SubsetBenchmarks.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ namespace Open.Collections.Benchmarks
66
{
77
public class SubsetBenchmarks
88
{
9-
readonly IEnumerable<int> FullSet = Enumerable.Range(0, 10);
9+
readonly IEnumerable<int> FullSet = Enumerable.Range(0, 32);
1010

1111
[Benchmark]
1212
public int Subset()
1313
{
1414
var buffer = new int[7];
15-
var sum = FullSet.MemoizeUnsafe().Subsets(7, buffer).SelectMany(e => e).Sum();
15+
var sum = FullSet.MemoizeUnsafe().Subsets(7, buffer).Count();
1616
return sum;
1717
}
1818

1919
[Benchmark]
2020
public int SubsetProgressive()
2121
{
2222
var buffer = new int[7];
23-
var sum = FullSet.MemoizeUnsafe().SubsetsProgressive(7, buffer).SelectMany(e => e).Sum();
23+
var sum = FullSet.MemoizeUnsafe().SubsetsProgressive(7, buffer).Count();
2424
return sum;
2525
}
2626
}

source/Extensions.SubsetsProgressive.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,10 @@ public static IEnumerable<T[]> SubsetsProgressive<T>(this IReadOnlyList<T> sourc
3939

4040
// Setup the first result and make sure there's enough for the count.
4141
var n = 0;
42-
var lowerSubset = new List<int>(count);
4342
for (; n < count; ++n)
4443
{
4544
if (!e.MoveNext()) throw new ArgumentOutOfRangeException(nameof(count), count, "Is greater than the length of the source.");
4645
buffer[n] = e.Current;
47-
lowerSubset.Add(n);
4846
}
4947

5048
// First result.
@@ -53,14 +51,14 @@ public static IEnumerable<T[]> SubsetsProgressive<T>(this IReadOnlyList<T> sourc
5351
while (e.MoveNext())
5452
{
5553
buffer[lastSlot] = e.Current;
56-
foreach (var _ in lowerSubset.Subsets(lastSlot, indices))
54+
foreach (var _ in Collections.Subsets.IndexesInternal(n, lastSlot, indices))
5755
{
5856
for(var i = 0; i<lastSlot; i++)
5957
buffer[i] = source[indices[i]];
6058

6159
yield return buffer;
6260
}
63-
lowerSubset.Add(n++);
61+
++n;
6462
}
6563
}
6664
finally
@@ -91,7 +89,7 @@ public static IEnumerable<ReadOnlyMemory<T>> SubsetsProgressiveBuffered<T>(this
9189

9290
/// <summary>
9391
/// Progressively enumerates the possible (ordered) subsets of the list, limited by the provided count.
94-
/// In contrast to the .Subsets(count) extension, this will produce consistent results regardless of source set size.
92+
/// In contrast to the .Subsets(count) extension, this will produce consistent results regardless of source set size but is not as fast.
9593
/// Favorable for larger source sets that enumeration may cause evaluation.
9694
/// </summary>
9795
/// <param name="source">The source list to derive from.</param>

source/Extensions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Buffers;
33
using System.Collections;
44
using System.Collections.Generic;
5+
using System.Collections.Immutable;
56
using System.Diagnostics.Contracts;
67
using System.Dynamic;
78
using System.Linq;
@@ -879,5 +880,35 @@ public static Span<T> CopyToSpan<T>(this IEnumerable<T> source, Span<T> target)
879880
return target.Slice(0, count);
880881
}
881882

883+
/// <summary>
884+
/// Builds an immutable array using the contents of the span.
885+
/// </summary>
886+
public static ImmutableArray<T> ToImmutableArray<T>(this ReadOnlySpan<T> span)
887+
{
888+
var builder = ImmutableArray.CreateBuilder<T>(span.Length);
889+
foreach (var e in span)
890+
builder.Add(e);
891+
return builder.MoveToImmutable();
892+
}
893+
894+
/// <inheritdoc cref="ToImmutableArray{T}(ReadOnlySpan{T})"/>
895+
public static ImmutableArray<T> ToImmutableArray<T>(this Span<T> span)
896+
{
897+
var builder = ImmutableArray.CreateBuilder<T>(span.Length);
898+
foreach (var e in span)
899+
builder.Add(e);
900+
return builder.MoveToImmutable();
901+
}
902+
903+
/// <summary>
904+
/// Builds an immutable array using the contents of the memory.
905+
/// </summary>
906+
public static ImmutableArray<T> ToImmutableArray<T>(this ReadOnlyMemory<T> memory)
907+
=> memory.Span.ToImmutableArray();
908+
909+
/// <inheritdoc cref="ToImmutableArray{T}(ReadOnlyMemory{T})"/>
910+
public static ImmutableArray<T> ToImmutableArray<T>(this Memory<T> memory)
911+
=> memory.Span.ToImmutableArray();
912+
882913
}
883914
}

source/Open.Collections.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Collections/</PackageProjectUrl>
1818
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Collections/</RepositoryUrl>
1919
<RepositoryType>git</RepositoryType>
20-
<Version>2.12.0</Version>
20+
<Version>2.12.1</Version>
2121
<PackageReleaseNotes></PackageReleaseNotes>
2222
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2323
<PublishRepositoryUrl>true</PublishRepositoryUrl>

source/Subsets.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Collections.Generic;
4+
using System.Collections.Immutable;
5+
6+
namespace Open.Collections
7+
{
8+
public static class Subsets
9+
{
10+
internal static IEnumerable<int[]> IndexesInternal(int sourceLength, int subsetLength, int[] buffer)
11+
{
12+
if (subsetLength == 1)
13+
{
14+
for (var i = 0; i < sourceLength; ++i)
15+
{
16+
buffer[0] = i;
17+
yield return buffer;
18+
}
19+
yield break;
20+
}
21+
22+
var diff = sourceLength - subsetLength;
23+
24+
var pos = 0;
25+
var index = 0;
26+
27+
loop:
28+
while (pos < subsetLength)
29+
{
30+
buffer[pos] = index;
31+
++pos;
32+
++index;
33+
}
34+
35+
yield return buffer;
36+
37+
do
38+
{
39+
if (pos == 0) yield break;
40+
index = buffer[--pos] + 1;
41+
}
42+
while (index > diff + pos);
43+
44+
goto loop;
45+
}
46+
47+
/// <inheritdoc cref="Indexes(int, int)"/>
48+
/// <param name="buffer">
49+
/// A buffer to use instead of returning new arrays for each iteration.
50+
/// It must be at least the length of the count.
51+
/// </param>
52+
public static IEnumerable<int[]> Indexes(int sourceLength, int subsetLength, int[] buffer)
53+
{
54+
55+
if (subsetLength < 1)
56+
throw new ArgumentOutOfRangeException(nameof(subsetLength), subsetLength, "Must greater than zero.");
57+
if (subsetLength > sourceLength)
58+
throw new ArgumentOutOfRangeException(nameof(subsetLength), subsetLength, "Must be less than or equal to the length of the source set.");
59+
if (buffer is null)
60+
throw new ArgumentNullException(nameof(buffer));
61+
if (buffer.Length < subsetLength)
62+
throw new ArgumentOutOfRangeException(nameof(buffer), buffer, "Length must be greater than or equal to the provided count.");
63+
64+
return IndexesInternal(sourceLength, subsetLength, buffer);
65+
}
66+
67+
/// <inheritdoc cref="Indexes(int, int)"/>
68+
/// <remarks>Values are yielded as read only memory buffer that should not be retained as its array is returned to pool afterwards.</remarks>
69+
public static IEnumerable<ReadOnlyMemory<int>> IndexesBuffered(int sourceLength, int subsetLength)
70+
{
71+
var pool = ArrayPool<int>.Shared;
72+
var buffer = pool.Rent(subsetLength);
73+
var readBuffer = new ReadOnlyMemory<int>(buffer, 0, subsetLength);
74+
try
75+
{
76+
foreach (var _ in Indexes(sourceLength, subsetLength, buffer))
77+
yield return readBuffer;
78+
}
79+
finally
80+
{
81+
pool.Return(buffer, true);
82+
}
83+
}
84+
85+
/// <summary>
86+
/// Enumerates all the possible subset indexes for a given source set length and subset length.
87+
/// </summary>
88+
/// <param name="sourceLength">The length of the source set.</param>
89+
/// <param name="subsetLength">The size of the desired subsets.</param>s
90+
public static IEnumerable<int[]> Indexes(int sourceLength, int subsetLength)
91+
{
92+
foreach (var i in IndexesBuffered(sourceLength, subsetLength))
93+
yield return i.ToArray();
94+
}
95+
96+
/// <inheritdoc cref="Indexes(int, int)"/>
97+
public static IEnumerable<ImmutableArray<int>> IndexesImmutable(int sourceLength, int subsetLength)
98+
{
99+
foreach (var i in IndexesBuffered(sourceLength, subsetLength))
100+
yield return i.ToImmutableArray();
101+
}
102+
}
103+
104+
}

0 commit comments

Comments
 (0)