diff --git a/Open.Collections.sln b/Open.Collections.sln
new file mode 100644
index 0000000..1ee8142
--- /dev/null
+++ b/Open.Collections.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31410.357
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Collections", "source\Open.Collections.csproj", "{F1C64CD8-E90A-46C8-9373-3BAB5498CC14}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Collections.Tests", "testing\Open.Collections.Tests\Open.Collections.Tests.csproj", "{44063AEE-8909-4A26-80BE-8623F5EBBA1F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F1C64CD8-E90A-46C8-9373-3BAB5498CC14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1C64CD8-E90A-46C8-9373-3BAB5498CC14}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1C64CD8-E90A-46C8-9373-3BAB5498CC14}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1C64CD8-E90A-46C8-9373-3BAB5498CC14}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44063AEE-8909-4A26-80BE-8623F5EBBA1F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {323AE57A-3C44-4EB8-890D-FD1107AADD8D}
+ EndGlobalSection
+EndGlobal
diff --git a/source/CollectionWrapper.cs b/source/CollectionWrapper.cs
index 1edf7c0..58e4940 100644
--- a/source/CollectionWrapper.cs
+++ b/source/CollectionWrapper.cs
@@ -48,13 +48,9 @@ public virtual void Clear()
public virtual bool Remove(T item)
=> InternalSource.Remove(item);
- ///
+ ///
public override bool IsReadOnly
=> InternalSource.IsReadOnly;
-
- ///
- public override void CopyTo(T[] array, int arrayIndex)
- => InternalSource.CopyTo(array, arrayIndex);
#endregion
}
}
diff --git a/source/DictionaryToHashSetWrapper.cs b/source/DictionaryToHashSetWrapper.cs
index 668d395..c5323eb 100644
--- a/source/DictionaryToHashSetWrapper.cs
+++ b/source/DictionaryToHashSetWrapper.cs
@@ -1,4 +1,5 @@
-using System.Collections;
+using System;
+using System.Collections;
using System.Collections.Generic;
namespace Open.Collections
@@ -13,12 +14,15 @@ public DictionaryToHashSetWrapper(IDictionary source)
InternalSource = source;
}
+ ///
public int Count
=> InternalSource.Count;
+ ///
public bool IsReadOnly
=> InternalSource.IsReadOnly;
+ ///
public virtual bool Add(T item)
{
if (InternalSource.ContainsKey(item))
@@ -35,31 +39,45 @@ public virtual bool Add(T item)
return true;
}
+ ///
public bool Remove(T item)
// ReSharper disable once AssignNullToNotNullAttribute
=> InternalSource.Remove(item);
+ ///
public void Clear()
=> InternalSource.Clear();
+ ///
public bool Contains(T item)
// ReSharper disable once AssignNullToNotNullAttribute
=> InternalSource.ContainsKey(item);
+ ///
public void CopyTo(T[] array, int arrayIndex)
=> InternalSource.Keys.CopyTo(array, arrayIndex);
+ ///
+ public virtual Span CopyTo(Span span)
+ => InternalSource.Keys.CopyToSpan(span);
+
+ ///
+ /// Returns a copy of the underlying keys.
+ ///
public HashSet ToHashSet()
=> new(InternalSource.Keys);
+ ///
public IEnumerator GetEnumerator()
=> InternalSource.Keys.GetEnumerator();
+ ///
public void ExceptWith(IEnumerable other)
{
foreach (var e in other) Remove(e);
}
+ ///
public void IntersectWith(IEnumerable other)
{
foreach (var e in other)
@@ -69,24 +87,31 @@ public void IntersectWith(IEnumerable other)
}
}
+ ///
public bool IsProperSubsetOf(IEnumerable other)
=> ToHashSet().IsProperSubsetOf(other);
+ ///
public bool IsProperSupersetOf(IEnumerable other)
=> ToHashSet().IsProperSupersetOf(other);
+ ///
public bool IsSubsetOf(IEnumerable other)
=> ToHashSet().IsSubsetOf(other);
+ ///
public bool IsSupersetOf(IEnumerable other)
=> ToHashSet().IsSupersetOf(other);
+ ///
public bool Overlaps(IEnumerable other)
=> ToHashSet().Overlaps(other);
+ ///
public bool SetEquals(IEnumerable other)
=> ToHashSet().SetEquals(other);
+ ///
public void SymmetricExceptWith(IEnumerable other)
{
foreach (var e in other)
@@ -98,6 +123,7 @@ public void SymmetricExceptWith(IEnumerable other)
}
}
+ ///
public void UnionWith(IEnumerable other)
{
foreach (var e in other) Add(e);
diff --git a/source/Extensions.cs b/source/Extensions.cs
index 1b15c2f..38c3fff 100644
--- a/source/Extensions.cs
+++ b/source/Extensions.cs
@@ -816,9 +816,30 @@ public static IEnumerable Weave(this IEnumerable> source)
}
}
+ ///
+ /// Caches the results up to the last index requested.
+ ///
+ /// The source enumerable to cache.
+ /// When true, will throw an InvalidOperationException if anything causes the list to evaluate to completion.
+ /// A LazyList for accessing the cached results.
public static LazyList Memoize(this IEnumerable list, bool isEndless = false)
=> new(list, isEndless);
+ ///
+ /// Caches the results up to the last index requested.
+ ///
+ /// WARNING:
+ /// - Is not thread safe.
+ /// - There is a risk of recursion.
+ /// - An endless enumerable may result in a stack overflow.
+ ///
+ /// If any of the above are of concern, then use .Memoize() instead.
+ ///
+ /// The source enumerable to cache.
+ /// A LazyList for accessing the cached results.
+ public static LazyListUnsafe MemoizeUnsafe(this IEnumerable list)
+ => new(list);
+
///
/// .IndexOf extension optimized for an array.
///
@@ -834,5 +855,29 @@ public static int IndexOf(this T[] source, T value)
return -1;
}
+ ///
+ /// Copies the results to the provided span up to its length or until the end of the results.
+ ///
+ /// The span to copy to.
+ ///
+ /// A span representing the results.
+ /// If the count was less than the target length, a new span representing the results.
+ /// Otherwise the target is returned.
+ ///
+ public static Span CopyToSpan(this IEnumerable source, Span target)
+ {
+ var len = target.Length;
+ if (len == 0) return target;
+
+ var count = 0;
+ foreach (var e in source)
+ {
+ target[count] = e;
+ if (len == ++count) return target;
+ }
+
+ return target.Slice(0, count);
+ }
+
}
}
diff --git a/source/LazyList.cs b/source/LazyList.cs
index 4eca0eb..50e5550 100644
--- a/source/LazyList.cs
+++ b/source/LazyList.cs
@@ -11,125 +11,46 @@
namespace Open.Collections
{
- public class LazyList : DisposableBase, IReadOnlyList
+ ///
+ /// A a thread-safe list for caching the results of an enumerable.
+ /// Note: should be disposed manually whenever possible as the locking mechanism is a ReaderWriterLockSlim.
+ ///
+ public class LazyList : LazyListUnsafe
{
- List _cached;
- IEnumerator _enumerator;
-
ReaderWriterLockSlim Sync;
+ int _safeCount;
+ ///
+ /// A value indicating whether the results are known or expected to be finite.
+ /// A list that was constructed as endless but has reached the end of the results will return false.
+ ///
public bool IsEndless { get; private set; }
- public LazyList(IEnumerable source, bool isEndless = false)
+
+ public LazyList(IEnumerable source, bool isEndless = false) : base(source)
{
- _enumerator = source.GetEnumerator();
- _cached = new List();
Sync = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); // This is important as it's possible to recurse infinitely to generate a result. :(
IsEndless = isEndless; // To indicate if a source is not allowed to fully enumerate.
}
protected override void OnDispose()
{
- using (Sync.WriteLock())
- {
- DisposeOf(ref _enumerator);
- Nullify(ref _cached)?.Clear();
- }
+ using (Sync.WriteLock()) base.OnDispose();
DisposeOf(ref Sync);
}
- public T this[int index]
- {
- get
- {
- AssertIsAlive();
-
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Cannot be less than zero.");
- if (!EnsureIndex(index))
- throw new ArgumentOutOfRangeException(nameof(index), "Greater than total count.");
-
- return _cached[index];
- }
- }
+ private const string MARKED_ENDLESS = "This list is marked as endless and may never complete.";
- private int _safeCount;
- public int Count
+ ///
+ public override int IndexOf(T item)
{
- get
- {
- AssertIsAlive();
- Finish();
- return _cached.Count;
- }
- }
+ const string MESSAGE = MARKED_ENDLESS+" Use an enumerator, then Take(x).IndexOf().";
+ if (IsEndless) throw new InvalidOperationException(MESSAGE);
- public bool TryGetValueAt(int index, out T value)
- {
- if (EnsureIndex(index))
- {
- value = _cached[index];
- return true;
- }
-
- value = default!;
- return false;
- }
-
- public IEnumerator GetEnumerator()
- {
- AssertIsAlive();
-
- var index = -1;
- // Interlocked allows for multi-threaded access to this enumerator.
- while (TryGetValueAt(Interlocked.Increment(ref index), out var value))
- yield return value;
- }
-
- public int IndexOf(T item)
- {
- AssertIsAlive();
- if (IsEndless)
- throw new InvalidOperationException("This list is marked as endless and may never complete. Use an enumerator, then Take(x).IndexOf().");
-
- var index = 0;
- while (EnsureIndex(index))
- {
- var value = _cached[index];
- if (value is null)
- {
- if (item is null) return index;
- }
- else if (value.Equals(item))
- return index;
-
- index++;
- }
-
- return -1;
-
- }
-
- public bool Contains(T item)
- {
- AssertIsAlive();
- return IndexOf(item) != -1;
- }
-
- public void CopyTo(T[] array, int arrayIndex = 0)
- {
- AssertIsAlive();
- var len = Math.Min(IsEndless ? int.MaxValue : Count, array.Length - arrayIndex);
- for (var i = 0; i < len; i++)
- array[i + arrayIndex] = this[i];
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
+ return base.IndexOf(item);
}
- private bool EnsureIndex(int maxIndex)
+ protected override bool EnsureIndex(int maxIndex)
{
if (maxIndex < _safeCount)
return true;
@@ -186,11 +107,11 @@ private bool EnsureIndex(int maxIndex)
return false;
}
- private void Finish()
+ protected override void Finish()
{
if (IsEndless)
- throw new InvalidOperationException("This list is marked as endless and may never complete.");
- while (EnsureIndex(int.MaxValue)) { }
+ throw new InvalidOperationException(MARKED_ENDLESS);
+ base.Finish();
}
}
diff --git a/source/LazyListUnsafe.cs b/source/LazyListUnsafe.cs
new file mode 100644
index 0000000..d88be0c
--- /dev/null
+++ b/source/LazyListUnsafe.cs
@@ -0,0 +1,170 @@
+/*!
+ * @author electricessence / https://github.com/electricessence/
+ * Partly based on: http://www.fallingcanbedeadly.com/posts/crazy-extention-methods-tolazylist/
+ */
+
+using Open.Disposable;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Open.Collections
+{
+ ///
+ /// An "unsafe" lazy list is one that is not thread safe, does not manage recursion, and does not protect against stack overflow (infinite length).
+ /// Only use if you know the results are finite and access is thread safe.
+ /// Note: disposing releases the underlying enumerator if it never reached the end of the results.
+ ///
+ public class LazyListUnsafe : DisposableBase, IReadOnlyList
+ {
+ protected List _cached;
+ protected IEnumerator _enumerator;
+
+ public LazyListUnsafe(IEnumerable source)
+ {
+ _enumerator = source.GetEnumerator();
+ _cached = new List();
+ }
+
+ protected override void OnDispose()
+ {
+ DisposeOf(ref _enumerator);
+ Nullify(ref _cached)?.Clear();
+ }
+
+ const string MUST_BE_AT_LEAST_ZERO = "Must be at least zero.";
+ const string GREATER_THAN_TOTAL = "Greater than total count.";
+
+ ///
+ public T this[int index]
+ {
+ get
+ {
+ AssertIsAlive();
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index), MUST_BE_AT_LEAST_ZERO);
+ if (!EnsureIndex(index))
+ throw new ArgumentOutOfRangeException(nameof(index), GREATER_THAN_TOTAL);
+
+ return _cached[index];
+ }
+ }
+
+ ///
+ public int Count
+ {
+ get
+ {
+ AssertIsAlive();
+ Finish();
+ return _cached.Count;
+ }
+ }
+
+ ///
+ /// Will attempt to get a value in the list if it is within the count of the results.
+ ///
+ /// The index to look up.
+ /// The value at that index.
+ /// True if acquired. False if the index is greater
+ public bool TryGetValueAt(int index, out T value)
+ {
+ AssertIsAlive();
+ if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), index, MUST_BE_AT_LEAST_ZERO);
+
+ if (EnsureIndex(index))
+ {
+ value = _cached[index];
+ return true;
+ }
+
+ value = default!;
+ return false;
+ }
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ AssertIsAlive();
+
+ var index = -1;
+ // Interlocked allows for multi-threaded access to this enumerator.
+ while (TryGetValueAt(Interlocked.Increment(ref index), out var value))
+ yield return value;
+ }
+
+ ///
+ public virtual int IndexOf(T item)
+ {
+ AssertIsAlive();
+
+ var index = 0;
+ while (EnsureIndex(index))
+ {
+ var value = _cached[index];
+ if (value is null)
+ {
+ if (item is null) return index;
+ }
+ else if (value.Equals(item))
+ return index;
+
+ index++;
+ }
+
+ return -1;
+
+ }
+
+ ///
+ public bool Contains(T item)
+ {
+ AssertIsAlive();
+ return IndexOf(item) != -1;
+ }
+
+ ///
+ public Span CopyTo(T[] array, int startIndex = 0)
+ {
+ if (array is null) throw new ArgumentNullException(nameof(array));
+ if (startIndex >= array.Length)
+ throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, "Must be less than the length of the provided array.");
+
+ var span = array.AsSpan();
+ return this.CopyToSpan(startIndex == 0 ? span : span.Slice(startIndex));
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
+
+ protected virtual bool EnsureIndex(int maxIndex)
+ {
+ if (maxIndex < _cached.Count)
+ return true;
+
+ if (_enumerator is null)
+ return false;
+
+ while (_enumerator.MoveNext())
+ {
+ if (_cached.Count == int.MaxValue)
+ throw new Exception("Reached maximium contents for a single list. Cannot memoize further.");
+
+ _cached.Add(_enumerator.Current);
+
+ if (maxIndex < _cached.Count)
+ return true;
+ }
+
+ DisposeOf(ref _enumerator);
+
+ return false;
+ }
+
+ protected virtual void Finish()
+ {
+ while (EnsureIndex(int.MaxValue)) { }
+ }
+
+ }
+}
diff --git a/source/Open.Collections.csproj b/source/Open.Collections.csproj
index d445bb7..972d1e0 100644
--- a/source/Open.Collections.csproj
+++ b/source/Open.Collections.csproj
@@ -17,7 +17,7 @@
https://github.com/Open-NET-Libraries/Open.Collections/
https://github.com/Open-NET-Libraries/Open.Collections/
git
- 2.10.2
+ 2.10.3
MIT
true
diff --git a/source/OrderedDictionary.cs b/source/OrderedDictionary.cs
index 5aafeee..335be74 100644
--- a/source/OrderedDictionary.cs
+++ b/source/OrderedDictionary.cs
@@ -505,19 +505,15 @@ public bool TryGetValue(TKey key, out TValue value)
///
public ICollection Values => Dictionary.Values;
- ///
void ICollection>.Add(KeyValuePair item)
=> Add(item.Key, item.Value);
- ///
bool ICollection>.Contains(KeyValuePair item)
=> ((ICollection>)Dictionary).Contains(item);
- ///
void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
=> ((ICollection>)Dictionary).CopyTo(array, arrayIndex);
- ///
bool ICollection>.Remove(KeyValuePair item)
=> Remove(item.Key);
diff --git a/source/ReadOnlyCollectionWrapper.cs b/source/ReadOnlyCollectionWrapper.cs
index a6a1887..efc2ea8 100644
--- a/source/ReadOnlyCollectionWrapper.cs
+++ b/source/ReadOnlyCollectionWrapper.cs
@@ -44,6 +44,10 @@ public virtual void CopyTo(T[] array, int arrayIndex)
=> InternalSource.CopyTo(array, arrayIndex);
#endregion
+ ///
+ public virtual Span CopyTo(Span span)
+ => InternalSource.CopyToSpan(span);
+
///
public virtual void Export(ICollection to)
=> to.Add(InternalSource);
diff --git a/source/Synchronized/LockSynchronizedCollectionWrapper.cs b/source/Synchronized/LockSynchronizedCollectionWrapper.cs
index 43a3004..4191002 100644
--- a/source/Synchronized/LockSynchronizedCollectionWrapper.cs
+++ b/source/Synchronized/LockSynchronizedCollectionWrapper.cs
@@ -111,6 +111,19 @@ public override void CopyTo(T[] array, int arrayIndex)
lock (Sync) InternalSource.CopyTo(array, arrayIndex);
}
+ ///
+ /// Copies the results to the provided span up to its length or until the end of the results.
+ ///
+ ///
+ /// A span representing the results.
+ /// If the count was less than the target length, a new span representing the results.
+ /// Otherwise the target is returned.
+ ///
+ public Span CopyTo(Span span)
+ {
+ lock (Sync) return InternalSource.CopyToSpan(span);
+ }
+
///
public void Modify(Action action)
{
diff --git a/testing/Open.Collections.Tests/LazyListTests.cs b/testing/Open.Collections.Tests/LazyListTests.cs
new file mode 100644
index 0000000..d13e1c9
--- /dev/null
+++ b/testing/Open.Collections.Tests/LazyListTests.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Open.Collections.Tests
+{
+ public class LazyListTests
+ {
+ [Fact]
+ public void LazyListSmokeTest()
+ {
+ {
+ var e = Enumerable.Range(0, 5);
+ Assert.Equal(5, e.Memoize().Count);
+ Assert.Equal(5, e.MemoizeUnsafe().Count);
+
+ Assert.Throws(() => e.Memoize(true).Count);
+ }
+
+ {
+ var e = Enumerable.Range(0, 30);
+ var a = e.Memoize();
+ var b = e.MemoizeUnsafe();
+ Assert.Equal(7, a[7]);
+ Assert.Equal(7, b[7]);
+
+ Assert.Equal(6, a.IndexOf(6));
+ Assert.Equal(6, b.IndexOf(6));
+ Assert.Equal(9, a.IndexOf(9));
+ Assert.Equal(9, b.IndexOf(9));
+ }
+ }
+ }
+}
diff --git a/testing/Open.Collections.Tests/Open.Collections.Tests.csproj b/testing/Open.Collections.Tests/Open.Collections.Tests.csproj
index 74736fb..c2379ce 100644
--- a/testing/Open.Collections.Tests/Open.Collections.Tests.csproj
+++ b/testing/Open.Collections.Tests/Open.Collections.Tests.csproj
@@ -20,7 +20,6 @@
-
diff --git a/testing/Open.Collections.Tests/SumCombinationTests.cs b/testing/Open.Collections.Tests/SumCombinationTests.cs
deleted file mode 100644
index 1ccb1ee..0000000
--- a/testing/Open.Collections.Tests/SumCombinationTests.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using Open.Collections.Numeric;
-using Xunit;
-
-namespace Open.Collections.Tests
-{
- public class SumCombinationTests
- {
- readonly PossibleAddends SC = new();
-
- [Fact]
- public void NoAddendsLessThan2()
- {
- for (int i = 0; i < 2; i++)
- Assert.Equal(0, SC.UniqueAddendsFor(7, i).Count);
- }
-
- [Fact]
- public void NoAddendsWithLowSum()
- {
- for (int i = 0; i < 4; i++)
- Assert.Equal(0, SC.UniqueAddendsFor(2, i).Count);
- }
-
- [Fact]
- public void AddendsFor2()
- {
- {
- var result = SC.UniqueAddendsFor(3, 2);
- Assert.Equal(1, result.Count);
- Assert.Equal(new int[] { 1, 2 }, result[0]);
- }
- {
- var result = SC.UniqueAddendsFor(4, 2);
- Assert.Equal(1, result.Count);
- Assert.Equal(new int[] { 1, 3 }, result[0]);
- }
- {
- var result = SC.UniqueAddendsFor(5, 2);
- Assert.Equal(2, result.Count);
- Assert.Equal(new int[] { 1, 4 }, result[0]);
- Assert.Equal(new int[] { 2, 3 }, result[1]);
- }
- {
- var result = SC.UniqueAddendsFor(6, 2);
- Assert.Equal(2, result.Count);
- Assert.Equal(new int[] { 1, 5 }, result[0]);
- Assert.Equal(new int[] { 2, 4 }, result[1]);
- }
- {
- var result = SC.UniqueAddendsFor(7, 2);
- Assert.Equal(3, result.Count);
- Assert.Equal(new int[] { 1, 6 }, result[0]);
- Assert.Equal(new int[] { 2, 5 }, result[1]);
- Assert.Equal(new int[] { 3, 4 }, result[2]);
- }
- }
-
-
- [Fact]
- public void AddendsFor3()
- {
- {
- for (int i = 0; i < 6; i++)
- {
- var result = SC.UniqueAddendsFor(i, 3);
- Assert.Equal(0, result.Count);
- }
- }
- {
- var result = SC.UniqueAddendsFor(6, 3);
- Assert.Equal(1, result.Count);
- Assert.Equal(new int[] { 1, 2, 3 }, result[0]);
- }
- {
- var result = SC.UniqueAddendsFor(7, 3);
- Assert.Equal(1, result.Count);
- Assert.Equal(new int[] { 1, 2, 4 }, result[0]);
- }
- {
- var result = SC.UniqueAddendsFor(8, 3);
- Assert.Equal(2, result.Count);
- Assert.Equal(new int[] { 1, 2, 5 }, result[0]);
- Assert.Equal(new int[] { 1, 3, 4 }, result[1]);
- }
- {
- var result = SC.UniqueAddendsFor(9, 3);
- Assert.Equal(3, result.Count);
- Assert.Equal(new int[] { 1, 2, 6 }, result[0]);
- Assert.Equal(new int[] { 1, 3, 5 }, result[1]);
- Assert.Equal(new int[] { 2, 3, 4 }, result[2]);
- }
- {
- var result = SC.UniqueAddendsFor(10, 3);
- Assert.Equal(4, result.Count);
- Assert.Equal(new int[] { 1, 2, 7 }, result[0]);
- Assert.Equal(new int[] { 1, 3, 6 }, result[1]);
- Assert.Equal(new int[] { 1, 4, 5 }, result[2]);
- Assert.Equal(new int[] { 2, 3, 5 }, result[3]);
- }
-
- {
- var result = SC.UniqueAddendsFor(15, 3);
- Assert.Equal(12, result.Count);
- Assert.Equal(new int[] { 1, 2, 12 }, result[0]);
- Assert.Equal(new int[] { 1, 3, 11 }, result[1]);
- Assert.Equal(new int[] { 1, 4, 10 }, result[2]);
- Assert.Equal(new int[] { 2, 3, 10 }, result[3]);
- Assert.Equal(new int[] { 1, 5, 9 }, result[4]);
- Assert.Equal(new int[] { 2, 4, 9 }, result[5]);
- Assert.Equal(new int[] { 1, 6, 8 }, result[6]);
- Assert.Equal(new int[] { 2, 5, 8 }, result[7]);
- Assert.Equal(new int[] { 3, 4, 8 }, result[8]);
- Assert.Equal(new int[] { 2, 6, 7 }, result[9]);
- Assert.Equal(new int[] { 3, 5, 7 }, result[10]);
- Assert.Equal(new int[] { 4, 5, 6 }, result[11]);
- }
- }
-
- }
-}