namespace WhiteRabbit { using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; /// /// Converts e.g. pair of variants [[a, b, c], [d, e]] into all possible pairs: [[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]] /// internal static class Flattener { // Slow universal implementation private static IEnumerable> FlattenAny(ImmutableStack phrase) { if (phrase.IsEmpty) { return new[] { ImmutableStack.Create() }; } T[] wordVariants; var newStack = phrase.Pop(out wordVariants); return FlattenAny(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); } // Fast hard-coded implementation for 3 words private static IEnumerable Flatten3(T[][] phrase) { for (var i0 = 0; i0 < phrase[0].Length; i0++) { for (var i1 = 0; i1 < phrase[1].Length; i1++) { for (var i2 = 0; i2 < phrase[2].Length; i2++) { yield return new T[] { phrase[0][i0], phrase[1][i1], phrase[2][i2], }; } } } } // Fast hard-coded implementation for 4 words private static IEnumerable Flatten4(T[][] phrase) { for (var i0 = 0; i0 < phrase[0].Length; i0++) { for (var i1 = 0; i1 < phrase[1].Length; i1++) { for (var i2 = 0; i2 < phrase[2].Length; i2++) { for (var i3 = 0; i3 < phrase[3].Length; i3++) { yield return new T[] { phrase[0][i0], phrase[1][i1], phrase[2][i2], phrase[3][i3], }; } } } } } // Fast hard-coded implementation for 5 words private static IEnumerable Flatten5(T[][] phrase) { for (var i0 = 0; i0 < phrase[0].Length; i0++) { for (var i1 = 0; i1 < phrase[1].Length; i1++) { for (var i2 = 0; i2 < phrase[2].Length; i2++) { for (var i3 = 0; i3 < phrase[3].Length; i3++) { for (var i4 = 0; i4 < phrase[4].Length; i4++) { yield return new T[] { phrase[0][i0], phrase[1][i1], phrase[2][i2], phrase[3][i3], phrase[4][i4], }; } } } } } } // Fast hard-coded implementation for 6 words private static IEnumerable Flatten6(T[][] phrase) { for (var i0 = 0; i0 < phrase[0].Length; i0++) { for (var i1 = 0; i1 < phrase[1].Length; i1++) { for (var i2 = 0; i2 < phrase[2].Length; i2++) { for (var i3 = 0; i3 < phrase[3].Length; i3++) { for (var i4 = 0; i4 < phrase[4].Length; i4++) { for (var i5 = 0; i5 < phrase[5].Length; i5++) { yield return new T[] { phrase[0][i0], phrase[1][i1], phrase[2][i2], phrase[3][i3], phrase[4][i4], }; } } } } } } } // Fast hard-coded implementation for 7 words private static IEnumerable Flatten7(T[][] phrase) { for (var i0 = 0; i0 < phrase[0].Length; i0++) { for (var i1 = 0; i1 < phrase[1].Length; i1++) { for (var i2 = 0; i2 < phrase[2].Length; i2++) { for (var i3 = 0; i3 < phrase[3].Length; i3++) { for (var i4 = 0; i4 < phrase[4].Length; i4++) { for (var i5 = 0; i5 < phrase[5].Length; i5++) { for (var i6 = 0; i6 < phrase[6].Length; i6++) { yield return new T[] { phrase[0][i0], phrase[1][i1], phrase[2][i2], phrase[3][i3], phrase[4][i4], }; } } } } } } } } public static IEnumerable Flatten(T[][] wordVariants) { switch (wordVariants.Length) { case 3: return Flatten3(wordVariants); case 4: return Flatten4(wordVariants); case 5: return Flatten5(wordVariants); case 6: return Flatten6(wordVariants); case 7: return Flatten7(wordVariants); default: return FlattenAny(ImmutableStack.Create(wordVariants)).Select(words => words.ToArray()); } } } }