namespace WhiteRabbit
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
///
/// Code taken from https://ericlippert.com/2013/04/22/producing-permutations-part-three/
///
internal sealed class PermutationsGenerator
{
public static IEnumerable HamiltonianPermutations(int n)
{
if (n < 0)
{
throw new ArgumentOutOfRangeException("n");
}
return Permutation.HamiltonianPermutationsIterator(n);
}
public struct Permutation : IEnumerable
{
private Permutation(int[] permutation)
{
this.PermutationData = permutation;
}
private Permutation(IEnumerable permutation)
: this(permutation.ToArray())
{
}
public static Permutation Empty { get; } = new Permutation(new int[] { });
private int[] PermutationData { get; }
public int this[int index] => this.PermutationData[index];
public static IEnumerable HamiltonianPermutationsIterator(int n)
{
if (n == 0)
{
yield return Empty;
yield break;
}
bool forwards = false;
foreach (Permutation permutation in HamiltonianPermutationsIterator(n - 1))
{
for (int index = 0; index < n; index += 1)
{
yield return new Permutation(Extensions.InsertAt(permutation, forwards ? index : n - index - 1, n - 1));
}
forwards = !forwards;
}
}
public IEnumerator GetEnumerator()
{
foreach (int item in this.PermutationData)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
public static class Extensions
{
public static IEnumerable InsertAt(IEnumerable items, int position, T newItem)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
if (position < 0)
{
throw new ArgumentOutOfRangeException("position");
}
return InsertAtIterator(items, position, newItem);
}
private static IEnumerable InsertAtIterator(IEnumerable items, int position, T newItem)
{
int index = 0;
bool yieldedNew = false;
foreach (T item in items)
{
if (index == position)
{
yield return newItem;
yieldedNew = true;
}
yield return item;
index += 1;
}
if (index == position)
{
yield return newItem;
yieldedNew = true;
}
if (!yieldedNew)
{
throw new ArgumentOutOfRangeException("position");
}
}
}
}
}