diff --git a/dotnet/WhiteRabbit/Program.cs b/dotnet/WhiteRabbit/Program.cs index 4b9485d..b88b699 100644 --- a/dotnet/WhiteRabbit/Program.cs +++ b/dotnet/WhiteRabbit/Program.cs @@ -24,11 +24,10 @@ stopwatch.Start(); var sourcePhrase = ConfigurationManager.AppSettings["SourcePhrase"]; - var sourceChars = ToOrderedChars(sourcePhrase); var maxWordsInPhrase = int.Parse(ConfigurationManager.AppSettings["MaxWordsInPhrase"]); - if (sourceChars.Length + maxWordsInPhrase > 28) + if (sourcePhrase.Where(ch => ch != ' ').Count() + maxWordsInPhrase > 28) { Console.WriteLine("Only anagrams of up to 27 characters (including whitespace) are allowed"); return; @@ -78,27 +77,14 @@ stopwatch.Restart(); - processor.CheckPhrases(phraseSet => ProcessPhraseSet(phraseSet, expectedHashesFirstComponents, stopwatch)); - - Console.WriteLine($"Done; time from start: {stopwatch.Elapsed}"); - } - - private static void ProcessPhraseSet(PhraseSet phraseSet, Vector expectedHashesFirstComponents, Stopwatch stopwatch) - { - phraseSet.ComputeMD5(); - for (var i = 0; i < Constants.PhrasesPerSet; i++) + processor.CheckPhrases(expectedHashesFirstComponents, (phraseBytes, hashFirstComponent) => { - /*Debug.Assert( - sourceChars == ToOrderedChars(ToString(phraseSet, i)), - $"StringsProcessor produced incorrect anagram: {ToString(phraseSet, i)}");*/ + var phrase = Encoding.ASCII.GetString(phraseBytes); + var hash = ComputeFullMD5(phraseBytes); + Console.WriteLine($"Found phrase for {hash} ({hashFirstComponent:x8}): {phrase}; time from start is {stopwatch.Elapsed}"); + }); - if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector(phraseSet.GetMD5(i)))) - { - var phrase = ToString(phraseSet, i); - var hash = ComputeFullMD5(phrase); - Console.WriteLine($"Found phrase for {hash} ({phraseSet.GetMD5(i):x8}): {phrase}; time from start is {stopwatch.Elapsed}"); - } - } + Console.WriteLine($"Done; time from start: {stopwatch.Elapsed}"); } // Code taken from http://stackoverflow.com/a/321404/831314 @@ -112,9 +98,8 @@ } // We can afford to spend some time here; this code will only run for matched phrases (and for one in several billion non-matched) - private static string ComputeFullMD5(string phrase) + private static string ComputeFullMD5(byte[] phraseBytes) { - var phraseBytes = Encoding.ASCII.GetBytes(phrase); using (var hashAlgorithm = new MD5CryptoServiceProvider()) { var resultBytes = hashAlgorithm.ComputeHash(phraseBytes); @@ -127,11 +112,6 @@ return hex.Substring(6, 2) + hex.Substring(4, 2) + hex.Substring(2, 2) + hex.Substring(0, 2); } - private static string ToString(PhraseSet phrase, int offset) - { - return Encoding.ASCII.GetString(phrase.GetBytes(offset)); - } - private static IEnumerable ReadInput() { string line; @@ -140,10 +120,5 @@ yield return Encoding.ASCII.GetBytes(line); } } - - private static string ToOrderedChars(string source) - { - return new string(source.Where(ch => ch != ' ').OrderBy(ch => ch).ToArray()); - } } } diff --git a/dotnet/WhiteRabbit/StringsProcessor.cs b/dotnet/WhiteRabbit/StringsProcessor.cs index 1dfcf8d..974ba53 100644 --- a/dotnet/WhiteRabbit/StringsProcessor.cs +++ b/dotnet/WhiteRabbit/StringsProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Numerics; using System.Threading.Tasks; internal sealed class StringsProcessor @@ -60,13 +61,13 @@ private int NumberOfCharacters { get; } - public void CheckPhrases(Action action) + public void CheckPhrases(Vector expectedHashes, Action action) { // 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(); // converting sequences of vectors to the sequences of words... - Parallel.ForEach(sums, new ParallelOptions { MaxDegreeOfParallelism = Constants.NumberOfThreads }, sum => ProcessSum(sum, action)); + Parallel.ForEach(sums, new ParallelOptions { MaxDegreeOfParallelism = Constants.NumberOfThreads }, sum => ProcessSum(sum, expectedHashes, action)); } public long GetPhrasesCount() @@ -118,24 +119,31 @@ return result; } - private void ProcessSum(int[] sum, Action action) + private void ProcessSum(int[] sum, Vector expectedHashes, Action action) { var initialPhraseSet = new PhraseSet(); initialPhraseSet.Init(); initialPhraseSet.FillLength(this.NumberOfCharacters, sum.Length); var phraseSet = new PhraseSet(); phraseSet.Init(); - var filter = ComputeFilter(sum); + var permutationsFilter = ComputeFilter(sum); var wordsVariants = this.ConvertVectorsToWordIndexes(sum); foreach (var wordsArray in Flattener.Flatten(wordsVariants)) { //Console.WriteLine(new string(wordsArray.SelectMany(wordIndex => this.AllWords[wordIndex].Original).Select(b => (char)b).ToArray())); - var permutations = PrecomputedPermutationsGenerator.HamiltonianPermutations(wordsArray.Length, filter); + var permutations = PrecomputedPermutationsGenerator.HamiltonianPermutations(wordsArray.Length, permutationsFilter); for (var i = 0; i < permutations.Length; i += Constants.PhrasesPerSet) { phraseSet.FillPhraseSet(initialPhraseSet, this.AllWords, wordsArray, permutations, i); - action(phraseSet); + phraseSet.ComputeMD5(); + for (var j = 0; j < Constants.PhrasesPerSet; j++) + { + if (Vector.EqualsAny(expectedHashes, new Vector(phraseSet.GetMD5(j)))) + { + action(phraseSet.GetBytes(j), phraseSet.GetMD5(j)); + } + } } } }