Refactored to DataBlocks

dataflow
Inga 🏳‍🌈 8 years ago
parent e4a1cc0d23
commit 08ef75d4cf
  1. 23
      WhiteRabbit/Extensions.cs
  2. 62
      WhiteRabbit/Program.cs
  3. 50
      WhiteRabbit/StringsProcessor.cs
  4. 22
      WhiteRabbit/VectorsProcessor.cs
  5. 5
      WhiteRabbit/WhiteRabbit.csproj
  6. 1
      WhiteRabbit/packages.config

@ -0,0 +1,23 @@
namespace WhiteRabbit
{
using System.Threading.Tasks.Dataflow;
internal static class Extensions
{
public static void LinkForever<TOutput>(this ISourceBlock<TOutput> source, ITargetBlock<TOutput> target)
{
source.LinkTo(target);
source.Completion.ContinueWith(t =>
{
if (t.IsFaulted)
{
target.Fault(t.Exception);
}
else
{
target.Complete();
}
});
}
}
}

@ -7,7 +7,7 @@
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks.Dataflow;
/// <summary>
/// Main class
/// </summary>
@ -18,10 +18,6 @@
/// </summary>
public static void Main()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var processor = new StringsProcessor("poultry outwits ants", 4, ReadInput());
var expectedHashes = new[]
{
"e4820b45d2277f3844eac66c903e84be",
@ -31,17 +27,50 @@
var expectedHashesAsVectors = new HashSet<Vector<byte>>(expectedHashes.Select(hash => new Vector<byte>(StringToByteArray(hash))));
foreach (var result in AddHashes(processor.GeneratePhrases()))
var stopwatch = new Stopwatch();
stopwatch.Start();
using (var hasher = MD5.Create())
{
if (expectedHashesAsVectors.Contains(result.Item2))
var processor = new StringsProcessor("poultry outwits ants", 4, ReadInput());
var unorderedSequencesToPhrases = processor.CreateUnorderedSequencesToPhrasesTransform();
var phrasesToPhrasesWithHash = new TransformBlock<string, PhraseWithHash>(phrase =>
{
Console.WriteLine($"Found phrase: {result.Item1} (spent {stopwatch.Elapsed})");
}
var hash = new Vector<byte>(hasher.ComputeHash(Encoding.ASCII.GetBytes(phrase)));
return new PhraseWithHash(phrase, hash);
});
var phrasesWithHashFilter = new TransformManyBlock<PhraseWithHash, PhraseWithHash>(phraseWithHash =>
{
if (!expectedHashesAsVectors.Contains(phraseWithHash.Hash))
{
return Enumerable.Empty<PhraseWithHash>();
}
stopwatch.Stop();
return new PhraseWithHash[]
{
phraseWithHash,
};
});
var printPhrases = new ActionBlock<PhraseWithHash>(phraseWithHash =>
{
Console.WriteLine($"Found phrase for hash {phraseWithHash.Hash}: {phraseWithHash.Phrase} (spent {stopwatch.Elapsed})");
});
unorderedSequencesToPhrases.LinkForever(phrasesToPhrasesWithHash);
phrasesToPhrasesWithHash.LinkForever(phrasesWithHashFilter);
phrasesWithHashFilter.LinkForever(printPhrases);
processor.PostUnorderedSequences(unorderedSequencesToPhrases);
printPhrases.Completion.Wait();
Console.WriteLine($"Total time spent: {stopwatch.Elapsed}");
}
}
// Code taken from http://stackoverflow.com/a/321404/831314
private static byte[] StringToByteArray(string hex)
@ -72,5 +101,18 @@
yield return line;
}
}
private class PhraseWithHash
{
public PhraseWithHash(string phrase, Vector<byte> hash)
{
this.Phrase = phrase;
this.Hash = hash;
}
public string Phrase { get; }
public Vector<byte> Hash { get; }
}
}
}

@ -4,7 +4,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks.Dataflow;
internal class StringsProcessor
{
public StringsProcessor(string sourceString, int maxWordsCount, IEnumerable<string> words)
@ -28,24 +28,26 @@
this.VectorsConverter.GetString);
}
private VectorsConverter VectorsConverter { get; }
private VectorsProcessor VectorsProcessor { get; }
private VectorsConverter VectorsConverter { get; }
private Dictionary<Vector<byte>, string[]> VectorsToWords { get; }
public IEnumerable<string> GeneratePhrases()
public void PostUnorderedSequences(ITargetBlock<Vector<byte>[]> target) => this.VectorsProcessor.PostUnorderedSequences(target);
public IPropagatorBlock<Vector<byte>[], string> CreateUnorderedSequencesToPhrasesTransform()
{
// task of finding anagrams could be reduced to the task of finding sequences of dictionary vectors with the target sum
var sums = this.VectorsProcessor.GenerateSequences();
var unorderedSequencesToOrderedSequences = this.VectorsProcessor.CreateUnorderedSequencesToOrderedSequencesTransform();
var orderedSequencesToWordVariants = this.CreateOrderedSequencesToWordVariantsTransform();
var wordVariantsToFlatWords = this.CreateWordVariantsToFlatWordsTransform();
var flatWordsToPhrases = this.CreateFlatWordsToPhrasesTransform();
// converting sequences of vectors to the sequences of words...
var anagramsWords = sums
.Select(sum => ImmutableStack.Create(sum.Select(vector => this.VectorsToWords[vector]).ToArray()))
.SelectMany(this.Flatten)
.Select(stack => stack.ToArray());
unorderedSequencesToOrderedSequences.LinkForever(orderedSequencesToWordVariants);
orderedSequencesToWordVariants.LinkForever(wordVariantsToFlatWords);
wordVariantsToFlatWords.LinkForever(flatWordsToPhrases);
return anagramsWords.Select(list => string.Join(" ", list));
return DataflowBlock.Encapsulate(unorderedSequencesToOrderedSequences, flatWordsToPhrases);
}
// 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]]
@ -60,5 +62,29 @@
var newStack = phrase.Pop(out wordVariants);
return this.Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word)));
}
private IPropagatorBlock<Vector<byte>[], ImmutableStack<string[]>> CreateOrderedSequencesToWordVariantsTransform()
{
return new TransformBlock<Vector<byte>[], ImmutableStack<string[]>>(sum =>
{
return ImmutableStack.CreateRange(sum.Select(vector => this.VectorsToWords[vector]));
});
}
private IPropagatorBlock<ImmutableStack<string[]>, ImmutableStack<string>> CreateWordVariantsToFlatWordsTransform()
{
return new TransformManyBlock<ImmutableStack<string[]>, ImmutableStack<string>>(wordVariants =>
{
return this.Flatten(wordVariants);
});
}
private IPropagatorBlock<ImmutableStack<string>, string> CreateFlatWordsToPhrasesTransform()
{
return new TransformBlock<ImmutableStack<string>, string>(words =>
{
return string.Join(" ", words);
});
}
}
}

@ -6,7 +6,7 @@
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks.Dataflow;
internal class VectorsProcessor
{
public VectorsProcessor(Vector<byte> target, int maxVectorsCount, IEnumerable<Vector<byte>> vectors, Func<Vector<byte>, string> vectorToString)
@ -37,13 +37,23 @@
private long Iterations { get; set; } = 0;
// Produces all sequences of vectors with the target sum
public IEnumerable<Vector<byte>[]> GenerateSequences()
public IPropagatorBlock<Vector<byte>[], Vector<byte>[]> CreateUnorderedSequencesToOrderedSequencesTransform()
{
return new TransformManyBlock<Vector<byte>[], Vector<byte>[]>(sequence =>
{
var unorderedSequences = this.GenerateUnorderedSequences(this.Target, ImmutableStack.Create<Vector<byte>>(), this.Vectors);
var allSequences = unorderedSequences.SelectMany(this.GeneratePermutations);
return this.GeneratePermutations(sequence);
});
}
public void PostUnorderedSequences(ITargetBlock<Vector<byte>[]> target)
{
var sequences = this.GenerateUnorderedSequences(this.Target, ImmutableStack.Create<Vector<byte>>(), this.Vectors);
foreach (var sequence in sequences)
{
target.Post(sequence);
}
return allSequences;
target.Complete();
}
// We want words with more letters (and among these, words with more "rare" letters) to appear first, to reduce the searching time somewhat.

@ -45,6 +45,10 @@
<HintPath>..\packages\System.Numerics.Vectors.4.3.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -53,6 +57,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions.cs" />
<Compile Include="PrecomputedPermutationsGenerator.cs" />
<Compile Include="PermutationsGenerator.cs" />
<Compile Include="StringsProcessor.cs" />

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Tpl.Dataflow" version="4.5.24" targetFramework="net46" />
<package id="System.Collections.Immutable" version="1.3.1" targetFramework="net46" />
<package id="System.Numerics.Vectors" version="4.3.0" targetFramework="net46" />
</packages>
Loading…
Cancel
Save