diff --git a/VocaluxeLib/Utils/Combinatorics/CCombinations.cs b/VocaluxeLib/Utils/Combinatorics/CCombinations.cs
new file mode 100644
index 00000000..5a147fc2
--- /dev/null
+++ b/VocaluxeLib/Utils/Combinatorics/CCombinations.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace VocaluxeLib.Utils.Combinatorics
+{
+ ///
+ /// Calculates all combinations (without repetition) of a given input sequence
+ /// Example: {A B C}, select 2 --> {A B}, {A C}, {B C}
+ ///
+ /// Type of the input elements
+ public class CCombinations : IEnumerable>
+ {
+ private readonly List _Values;
+ private readonly int _NumSelected;
+ private readonly int _Count;
+
+ ///
+ /// Construct combinations for the input sequence when selecting numSelected elements
+ /// The values should not contain duplicates as this will result in duplicates in the output
+ ///
+ /// Input sequence
+ /// Number of elements to select
+ public CCombinations(List values, int numSelected)
+ {
+ _Values = values;
+ _NumSelected = numSelected;
+ // This also acts as a parameter check
+ _Count = Count(_Values.Count, _NumSelected);
+ }
+
+ ///
+ /// Calculates how many combinations are possible when k out of n elements are selected
+ ///
+ /// Number of elements
+ /// Number of selected elements
+ ///
+ public static int Count(int n, int k)
+ {
+ if (n < 0 || k < 0)
+ throw new ArgumentException("Parameters must not be negative");
+ if (n < k)
+ return 0;
+ // Try to optimize because (n k) = (n n-k)
+ if (n - k < k)
+ k = n - k;
+ if (k == 0) // or k == n but both are the same after the optimization above
+ return 1;
+
+ long result = n;
+ for (long i = 1; i < k; i++)
+ {
+ checked
+ {
+ result = result * (n - i) / (i + 1);
+ }
+ }
+ return (int)result;
+ }
+
+ ///
+ /// Gets the number of combinations that are generated
+ ///
+ ///
+ public int Count()
+ {
+ return _Count;
+ }
+
+ ///
+ /// Gets all possible combinations as a list
+ ///
+ ///
+ public List> GetAll()
+ {
+ List> result = new List>(Count());
+ result.AddRange(this);
+ return result;
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return new CEnumerator(this);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ private class CEnumerator : IEnumerator>
+ {
+ private readonly int _N;
+ private readonly int _K;
+ private readonly CCombinations _Parent;
+ private int[] _CurSet;
+
+ public CEnumerator(CCombinations parent)
+ {
+ _N = parent._Values.Count;
+ _K = parent._NumSelected;
+ _Parent = parent;
+ Debug.Assert(_N >= 0 && _K >= 0);
+ }
+
+ public void Dispose() {}
+
+ public bool MoveNext()
+ {
+ if (_N < _K)
+ return false;
+
+ if (_CurSet == null)
+ {
+ _CurSet = new int[_K];
+ for (int i = 0; i < _K; i++)
+ _CurSet[i] = i;
+ return true;
+ }
+
+ // Check if we reached the end
+ if (_K == 0 || _CurSet[0] == _N - _K)
+ return false;
+ // Search for element to increment
+ int idx = _K - 1;
+ while (idx > 0 && _CurSet[idx] == _N - _K + idx)
+ idx--;
+ _CurSet[idx]++;
+ // Increment all elements "right" of this to 1+their left neighbour
+ for (int j = idx; j < _K - 1; j++)
+ _CurSet[j + 1] = _CurSet[j] + 1;
+ return true;
+ }
+
+ public void Reset()
+ {
+ _CurSet = null;
+ }
+
+ public List Current
+ {
+ get
+ {
+ List result = new List(_K);
+ for (int i = 0; i < _K; i++)
+ result.Add(_Parent._Values[_CurSet[i]]);
+ return result;
+ }
+ }
+ object IEnumerator.Current
+ {
+ get { return Current; }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj
index b452b5e5..cd947ae2 100644
--- a/VocaluxeLib/VocaluxeLib.csproj
+++ b/VocaluxeLib/VocaluxeLib.csproj
@@ -108,6 +108,7 @@
+
diff --git a/VocaluxeTests/CTestHelpers.cs b/VocaluxeTests/CTestHelpers.cs
new file mode 100644
index 00000000..4ad00bf3
--- /dev/null
+++ b/VocaluxeTests/CTestHelpers.cs
@@ -0,0 +1,21 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace VocaluxeTests
+{
+ static class CTestHelpers
+ {
+ public static void AssertFail(Action test) where T : Exception
+ {
+ try
+ {
+ test.Invoke();
+ Assert.Fail("Exception " + typeof(T).Name + " not thrown!");
+ }
+ catch (Exception e)
+ {
+ Assert.IsInstanceOfType(e, typeof(T));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/VocaluxeTests/CombinatoricsTest.cs b/VocaluxeTests/CombinatoricsTest.cs
new file mode 100644
index 00000000..50d274e1
--- /dev/null
+++ b/VocaluxeTests/CombinatoricsTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using VocaluxeLib.Utils.Combinatorics;
+
+namespace VocaluxeTests
+{
+ [TestClass]
+ public class CCombinatoricsTest
+ {
+ [TestMethod]
+ public void TestCounts()
+ {
+ Assert.AreEqual(4, CCombinations.Count(4, 3));
+ Assert.AreEqual(6, CCombinations.Count(4, 2));
+ Assert.AreEqual(66, CCombinations.Count(12, 2));
+ // Corner cases
+ for (int i = 1; i < 12; i++)
+ {
+ Assert.AreEqual(1, CCombinations.Count(i, 0));
+ Assert.AreEqual(1, CCombinations.Count(i, i));
+ Assert.AreEqual(0, CCombinations.Count(i, i + 1));
+ }
+ // Check identity (n k) = (n n-k)
+ for (int i = 0; i <= 12; i++)
+ Assert.AreEqual(CCombinations.Count(12, i), CCombinations.Count(12, 12 - i));
+ CTestHelpers.AssertFail(() => CCombinations.Count(-1, -1));
+ CTestHelpers.AssertFail(() => CCombinations.Count(-1, 1));
+ CTestHelpers.AssertFail(() => CCombinations.Count(1, -1));
+ }
+
+ // ReSharper disable UnusedParameter.Local
+ private static void _CheckResults(CCombinations combs, List input)
+ // ReSharper restore UnusedParameter.Local
+ {
+ List> results = new List>();
+ foreach (List comb in combs)
+ {
+ foreach (List other in results)
+ Assert.IsFalse(other.SequenceEqual(comb), "Result must be unique");
+ for (int i = 0; i < comb.Count - 1; i++)
+ {
+ Assert.IsTrue(comb.IndexOf(comb[i], i + 1) < 0, "Repetition not allowed");
+ Assert.IsTrue(input.Contains(comb[i]), "Output must be in input sequence");
+ }
+ results.Add(comb);
+ }
+ Assert.AreEqual(combs.Count(), results.Count);
+ List> results2 = combs.GetAll();
+ Assert.AreEqual(results.Count, results2.Count);
+ for (int i = 0; i < results2.Count; i++)
+ Assert.IsTrue(results[i].SequenceEqual(results2[i]));
+ }
+
+ [TestMethod]
+ public void TestResults()
+ {
+ Random rand = new Random();
+ for (int n = 0; n < 8; n++)
+ {
+ for (int k = 0; k <= n; k++)
+ {
+ List collection = new List(n);
+ int r = rand.Next(1000);
+ for (int i = 0; i < n; i++)
+ collection.Add(i + r);
+ CCombinations combs = new CCombinations(collection, k);
+ Assert.AreEqual(CCombinations.Count(n, k), combs.Count());
+ _CheckResults(combs, collection);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/VocaluxeTests/VocaluxeTests.csproj b/VocaluxeTests/VocaluxeTests.csproj
index b3a0dade..3b8f65dc 100644
--- a/VocaluxeTests/VocaluxeTests.csproj
+++ b/VocaluxeTests/VocaluxeTests.csproj
@@ -70,7 +70,9 @@
+
+
diff --git a/VocaluxeTests/XmlSerializerTest.cs b/VocaluxeTests/XmlSerializerTest.cs
index 6b59e5ec..56e45410 100644
--- a/VocaluxeTests/XmlSerializerTest.cs
+++ b/VocaluxeTests/XmlSerializerTest.cs
@@ -91,8 +91,7 @@ namespace VocaluxeTests
private struct SDefaultSub
{
- [DefaultValue(111)]
- public int I;
+ [DefaultValue(111)] public int I;
}
private struct SDefault
@@ -107,23 +106,10 @@ namespace VocaluxeTests
#pragma warning restore 169
#pragma warning restore 649
- private static void _AssertFail(Action test) where T : Exception
- {
- try
- {
- test.Invoke();
- Assert.Fail("Exception " + typeof(T).Name + " not thrown!");
- }
- catch (Exception e)
- {
- Assert.IsInstanceOfType(e, typeof(T));
- }
- }
-
private static void _AssertFail(String xmlString) where T2 : Exception where T : new()
{
var deserializer = new CXmlDeserializer();
- _AssertFail(() => deserializer.DeserializeString(xmlString));
+ CTestHelpers.AssertFail(() => deserializer.DeserializeString(xmlString));
}
private static T _AssertSerDeserMatch(string xmlString) where T : new()
@@ -454,7 +440,7 @@ namespace VocaluxeTests
Assert.AreEqual(foo.D, 666);
Assert.AreEqual(foo.Sub.I, 111);
string newXml = new CXmlSerializer().Serialize(foo);
- Assert.AreEqual(_Head +@"
+ Assert.AreEqual(_Head + @"
", newXml);
}