diff --git a/WhiteRabbit/Processor.cs b/WhiteRabbit/Processor.cs deleted file mode 100644 index a209602..0000000 --- a/WhiteRabbit/Processor.cs +++ /dev/null @@ -1,141 +0,0 @@ -namespace WhiteRabbit -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - - internal class Processor - { - private const int DifferentChars = 12; - private const int ArraySize = DifferentChars * sizeof(int); - - public Processor(string sourceString, int maxWordsCount) - { - var filteredSource = new string(sourceString.Where(ch => ch != ' ').ToArray()); - this.VectorsConverter = new VectorsConverter(filteredSource); - this.Target = this.VectorsConverter.GetVector(filteredSource).Value; - this.MaxWordsCount = maxWordsCount; - } - - private static Vector Negative { get; } = new Vector(Enumerable.Repeat((byte)128, 16).ToArray()); - - private VectorsConverter VectorsConverter { get; } - - private Vector Target { get; } - - private int MaxWordsCount { get; } - - private long Iterations { get; set; } = 0; - - public IEnumerable GeneratePhrases(IEnumerable words) - { - var formattedWordsList = FormatWords(words); - var formattedWords = formattedWordsList.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2); - - var dictionary = ImmutableStack.Create(formattedWordsList.Select(tuple => tuple.Item1).ToArray()); - var anagrams = GenerateOrderedPhrases(this.Target, ImmutableStack.Create>(), dictionary); - var anagramsWithPermutations = anagrams.SelectMany(GeneratePermutations); - var anagramsWords = anagramsWithPermutations - .Select(list => ImmutableStack.Create(list.Select(wordArray => formattedWords[wordArray]).ToArray())) - .SelectMany(Flatten) - .Select(stack => stack.ToArray()); - - return anagramsWords.Select(list => string.Join(" ", list)); - } - - private List, string[]>> FormatWords(IEnumerable words) - { - return words - .Distinct() - .Select(word => new { word, vector = this.VectorsConverter.GetVector(word) }) - .Where(tuple => tuple.vector != null) - .Select(tuple => new { tuple.word, vector = tuple.vector.Value }) - .Where(tuple => ((this.Target - tuple.vector) & Negative) == Vector.Zero) - .GroupBy(tuple => tuple.vector) - .Select(group => Tuple.Create(group.Key, group.Select(tuple => tuple.word).ToArray())) - .OrderByDescending(tuple => this.VectorsConverter.GetString(tuple.Item1)) //so that letters that are more rare will come first - .ToList(); - } - - // This method takes most of the time, so everything related to it must be optimized - private IEnumerable[]> GenerateOrderedPhrases(Vector currentState, ImmutableStack> phraseStack, ImmutableStack> dictionaryStack) - { - var count = phraseStack.Count() + 1; - if (count < this.MaxWordsCount) - { - var remainder = dictionaryStack; - while (!remainder.IsEmpty) - { - Vector currentWord; - var nextRemainder = remainder.Pop(out currentWord); - - this.Iterations++; - if (this.Iterations % 1000000 == 0) - { - Console.WriteLine($"Iteration #{this.Iterations}: {string.Join(" ", phraseStack.Push(currentWord).Reverse().Select(word => this.VectorsConverter.GetString(word)))}"); - } - - var newState = currentState - currentWord; - if (newState == Vector.Zero) - { - yield return phraseStack.Push(currentWord).Reverse().ToArray(); - } - else if ((newState & Negative) == Vector.Zero) - { - foreach (var result in GenerateOrderedPhrases(newState, phraseStack.Push(currentWord), remainder)) - { - yield return result; - } - } - - remainder = nextRemainder; - } - } - else if (count == this.MaxWordsCount) - { - var remainder = dictionaryStack; - while (!remainder.IsEmpty) - { - Vector currentWord; - var nextRemainder = remainder.Pop(out currentWord); - - this.Iterations++; - if (this.Iterations % 1000000 == 0) - { - Console.WriteLine($"Iteration #{this.Iterations}: {string.Join(" ", phraseStack.Push(currentWord).Reverse().Select(word => this.VectorsConverter.GetString(word)))}"); - } - - var newState = currentState - currentWord; - if (newState == Vector.Zero) - { - yield return phraseStack.Push(currentWord).Reverse().ToArray(); - } - - remainder = nextRemainder; - } - } - } - - private IEnumerable GeneratePermutations(T[] original) - { - foreach (var permutation in PermutationsGenerator.HamiltonianPermutations(original.Length)) - { - yield return permutation.Select(i => original[i]).ToArray(); - } - } - - private IEnumerable> Flatten(ImmutableStack phrase) - { - if (phrase.IsEmpty) - { - return new[] { ImmutableStack.Create() }; - } - - string[] wordVariants; - var newStack = phrase.Pop(out wordVariants); - return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); - } - } -} diff --git a/WhiteRabbit/Program.cs b/WhiteRabbit/Program.cs index f320f04..fe6f2a4 100644 --- a/WhiteRabbit/Program.cs +++ b/WhiteRabbit/Program.cs @@ -16,7 +16,7 @@ /// public static void Main() { - var processor = new Processor("poultry outwits ants", 3); + var processor = new StringsProcessor("poultry outwits ants", 3); foreach (var phrase in processor.GeneratePhrases(ReadInput())) { var hash = GetHash(phrase); diff --git a/WhiteRabbit/StringsProcessor.cs b/WhiteRabbit/StringsProcessor.cs new file mode 100644 index 0000000..d34b53a --- /dev/null +++ b/WhiteRabbit/StringsProcessor.cs @@ -0,0 +1,56 @@ +namespace WhiteRabbit +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + + internal class StringsProcessor + { + public StringsProcessor(string sourceString, int maxWordsCount) + { + var filteredSource = new string(sourceString.Where(ch => ch != ' ').ToArray()); + this.VectorsConverter = new VectorsConverter(filteredSource); + this.VectorsProcessor = new VectorsProcessor( + this.VectorsConverter.GetVector(filteredSource).Value, + maxWordsCount, + this.VectorsConverter.GetString); + } + + private VectorsConverter VectorsConverter { get; } + + private VectorsProcessor VectorsProcessor { get; } + + public IEnumerable GeneratePhrases(IEnumerable words) + { + var formattedWords = words + .Distinct() + .Select(word => new { word, vector = this.VectorsConverter.GetVector(word) }) + .Where(tuple => tuple.vector != null) + .Select(tuple => new { tuple.word, vector = tuple.vector.Value }) + .GroupBy(tuple => tuple.vector) + .ToDictionary(group => group.Key, group => group.Select(tuple => tuple.word).ToArray()); + + + var sums = this.VectorsProcessor.GenerateSums(formattedWords.Keys); + + var anagramsWords = sums + .Select(sum => ImmutableStack.Create(sum.Select(vector => formattedWords[vector]).ToArray())) + .SelectMany(Flatten) + .Select(stack => stack.ToArray()); + + return anagramsWords.Select(list => string.Join(" ", list)); + } + + private IEnumerable> Flatten(ImmutableStack phrase) + { + if (phrase.IsEmpty) + { + return new[] { ImmutableStack.Create() }; + } + + string[] wordVariants; + var newStack = phrase.Pop(out wordVariants); + return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); + } + } +} diff --git a/WhiteRabbit/VectorsProcessor.cs b/WhiteRabbit/VectorsProcessor.cs new file mode 100644 index 0000000..00b8b03 --- /dev/null +++ b/WhiteRabbit/VectorsProcessor.cs @@ -0,0 +1,121 @@ +namespace WhiteRabbit +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Diagnostics; + using System.Linq; + using System.Numerics; + + internal class VectorsProcessor + { + public VectorsProcessor(Vector target, int maxVectorsCount, Func, string> vectorToString) + { + this.Target = target; + this.MaxVectorsCount = maxVectorsCount; + this.VectorToString = vectorToString; + } + + /// + /// Negative sign bit. + /// (byte)b & (byte)128 equals zero for non-negative (0..127) bytes and equals (byte)128 for negative (128..255) bytes. + /// Similarly, vector & Negative equals zero if all bytes are non-negative, and does not equal zero if some bytes are negative. + /// Use (vector & Negative) == Vector<byte>.Zero to determine if all components are non-negative. + /// + private static Vector Negative { get; } = new Vector(Enumerable.Repeat((byte)128, 16).ToArray()); + + private Vector Target { get; } + + private int MaxVectorsCount { get; } + + private Func, string> VectorToString { get; } + + private long Iterations { get; set; } = 0; + + public IEnumerable[]> GenerateSums(IEnumerable> vectors) + { + var filteredVectors = FilterVectors(vectors); + + var dictionary = ImmutableStack.Create(filteredVectors.ToArray()); + var orderedSums = GenerateOrderedSums(this.Target, ImmutableStack.Create>(), dictionary); + var allSums = orderedSums.SelectMany(GeneratePermutations); + + return allSums; + } + + private IEnumerable> FilterVectors(IEnumerable> vectors) + { + return vectors + .Where(vector => ((this.Target - vector) & Negative) == Vector.Zero) + //.OrderByDescending(vector => this.GetWeight(vector)) //so that letters that are more rare will come first + .ToList(); + } + + [Conditional("DEBUG")] + private void DebugState(ImmutableStack> partialSumStack, Vector currentVector) + { + this.Iterations++; + if (this.Iterations % 1000000 == 0) + { + Console.WriteLine($"Iteration #{this.Iterations}: {string.Join(" ", partialSumStack.Push(currentVector).Reverse().Select(vector => this.VectorToString(vector)))}"); + } + } + + // This method takes most of the time, so everything related to it must be optimized + private IEnumerable[]> GenerateOrderedSums(Vector remainder, ImmutableStack> partialSumStack, ImmutableStack> dictionaryStack) + { + var count = partialSumStack.Count() + 1; + if (count < this.MaxVectorsCount) + { + var dictionaryTail = dictionaryStack; + while (!dictionaryTail.IsEmpty) + { + Vector currentVector; + var nextDictionaryTail = dictionaryTail.Pop(out currentVector); + + DebugState(partialSumStack, currentVector); + + var newRemainder = remainder - currentVector; + if (newRemainder == Vector.Zero) + { + yield return partialSumStack.Push(currentVector).Reverse().ToArray(); + } + else if ((newRemainder & Negative) == Vector.Zero) + { + foreach (var result in GenerateOrderedSums(newRemainder, partialSumStack.Push(currentVector), dictionaryTail)) + { + yield return result; + } + } + + dictionaryTail = nextDictionaryTail; + } + } + else if (count == this.MaxVectorsCount) + { + var dictionaryTail = dictionaryStack; + while (!dictionaryTail.IsEmpty) + { + Vector currentVector; + dictionaryTail = dictionaryTail.Pop(out currentVector); + + DebugState(partialSumStack, currentVector); + + var newRemainder = remainder - currentVector; + if (newRemainder == Vector.Zero) + { + yield return partialSumStack.Push(currentVector).Reverse().ToArray(); + } + } + } + } + + private IEnumerable GeneratePermutations(T[] original) + { + foreach (var permutation in PermutationsGenerator.HamiltonianPermutations(original.Length)) + { + yield return permutation.Select(i => original[i]).ToArray(); + } + } + } +} diff --git a/WhiteRabbit/WhiteRabbit.csproj b/WhiteRabbit/WhiteRabbit.csproj index 352e50a..ad3c285 100644 --- a/WhiteRabbit/WhiteRabbit.csproj +++ b/WhiteRabbit/WhiteRabbit.csproj @@ -54,9 +54,10 @@ - + +