Refactoring

rx
Inga 🏳‍🌈 7 years ago
parent 2ad1385a20
commit eb47f42d74
  1. 141
      WhiteRabbit/Processor.cs
  2. 2
      WhiteRabbit/Program.cs
  3. 56
      WhiteRabbit/StringsProcessor.cs
  4. 121
      WhiteRabbit/VectorsProcessor.cs
  5. 3
      WhiteRabbit/WhiteRabbit.csproj

@ -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<byte> Negative { get; } = new Vector<byte>(Enumerable.Repeat((byte)128, 16).ToArray());
private VectorsConverter VectorsConverter { get; }
private Vector<byte> Target { get; }
private int MaxWordsCount { get; }
private long Iterations { get; set; } = 0;
public IEnumerable<string> GeneratePhrases(IEnumerable<string> 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<Vector<byte>>(), 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<Tuple<Vector<byte>, string[]>> FormatWords(IEnumerable<string> 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<byte>.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<Vector<byte>[]> GenerateOrderedPhrases(Vector<byte> currentState, ImmutableStack<Vector<byte>> phraseStack, ImmutableStack<Vector<byte>> dictionaryStack)
{
var count = phraseStack.Count() + 1;
if (count < this.MaxWordsCount)
{
var remainder = dictionaryStack;
while (!remainder.IsEmpty)
{
Vector<byte> 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<byte>.Zero)
{
yield return phraseStack.Push(currentWord).Reverse().ToArray();
}
else if ((newState & Negative) == Vector<byte>.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<byte> 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<byte>.Zero)
{
yield return phraseStack.Push(currentWord).Reverse().ToArray();
}
remainder = nextRemainder;
}
}
}
private IEnumerable<T[]> GeneratePermutations<T>(T[] original)
{
foreach (var permutation in PermutationsGenerator.HamiltonianPermutations(original.Length))
{
yield return permutation.Select(i => original[i]).ToArray();
}
}
private IEnumerable<ImmutableStack<string>> Flatten(ImmutableStack<string[]> phrase)
{
if (phrase.IsEmpty)
{
return new[] { ImmutableStack.Create<string>() };
}
string[] wordVariants;
var newStack = phrase.Pop(out wordVariants);
return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word)));
}
}
}

@ -16,7 +16,7 @@
/// </summary>
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);

@ -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<string> GeneratePhrases(IEnumerable<string> 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<ImmutableStack<string>> Flatten(ImmutableStack<string[]> phrase)
{
if (phrase.IsEmpty)
{
return new[] { ImmutableStack.Create<string>() };
}
string[] wordVariants;
var newStack = phrase.Pop(out wordVariants);
return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word)));
}
}
}

@ -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<byte> target, int maxVectorsCount, Func<Vector<byte>, string> vectorToString)
{
this.Target = target;
this.MaxVectorsCount = maxVectorsCount;
this.VectorToString = vectorToString;
}
/// <summary>
/// Negative sign bit.
/// (byte)b &amp; (byte)128 equals zero for non-negative (0..127) bytes and equals (byte)128 for negative (128..255) bytes.
/// Similarly, vector &amp; Negative equals zero if all bytes are non-negative, and does not equal zero if some bytes are negative.
/// Use <code>(vector &amp; Negative) == Vector&lt;byte&gt;.Zero</code> to determine if all components are non-negative.
/// </summary>
private static Vector<byte> Negative { get; } = new Vector<byte>(Enumerable.Repeat((byte)128, 16).ToArray());
private Vector<byte> Target { get; }
private int MaxVectorsCount { get; }
private Func<Vector<byte>, string> VectorToString { get; }
private long Iterations { get; set; } = 0;
public IEnumerable<Vector<byte>[]> GenerateSums(IEnumerable<Vector<byte>> vectors)
{
var filteredVectors = FilterVectors(vectors);
var dictionary = ImmutableStack.Create(filteredVectors.ToArray());
var orderedSums = GenerateOrderedSums(this.Target, ImmutableStack.Create<Vector<byte>>(), dictionary);
var allSums = orderedSums.SelectMany(GeneratePermutations);
return allSums;
}
private IEnumerable<Vector<byte>> FilterVectors(IEnumerable<Vector<byte>> vectors)
{
return vectors
.Where(vector => ((this.Target - vector) & Negative) == Vector<byte>.Zero)
//.OrderByDescending(vector => this.GetWeight(vector)) //so that letters that are more rare will come first
.ToList();
}
[Conditional("DEBUG")]
private void DebugState(ImmutableStack<Vector<byte>> partialSumStack, Vector<byte> 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<Vector<byte>[]> GenerateOrderedSums(Vector<byte> remainder, ImmutableStack<Vector<byte>> partialSumStack, ImmutableStack<Vector<byte>> dictionaryStack)
{
var count = partialSumStack.Count() + 1;
if (count < this.MaxVectorsCount)
{
var dictionaryTail = dictionaryStack;
while (!dictionaryTail.IsEmpty)
{
Vector<byte> currentVector;
var nextDictionaryTail = dictionaryTail.Pop(out currentVector);
DebugState(partialSumStack, currentVector);
var newRemainder = remainder - currentVector;
if (newRemainder == Vector<byte>.Zero)
{
yield return partialSumStack.Push(currentVector).Reverse().ToArray();
}
else if ((newRemainder & Negative) == Vector<byte>.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<byte> currentVector;
dictionaryTail = dictionaryTail.Pop(out currentVector);
DebugState(partialSumStack, currentVector);
var newRemainder = remainder - currentVector;
if (newRemainder == Vector<byte>.Zero)
{
yield return partialSumStack.Push(currentVector).Reverse().ToArray();
}
}
}
}
private IEnumerable<T[]> GeneratePermutations<T>(T[] original)
{
foreach (var permutation in PermutationsGenerator.HamiltonianPermutations(original.Length))
{
yield return permutation.Select(i => original[i]).ToArray();
}
}
}
}

@ -54,9 +54,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="PermutationsGenerator.cs" />
<Compile Include="Processor.cs" />
<Compile Include="StringsProcessor.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VectorsProcessor.cs" />
<Compile Include="VectorsConverter.cs" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save