namespace WhiteRabbit
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Text;
using Org.BouncyCastle.Crypto.Digests;
///
/// Main class
///
public static class Program
{
private const string SourcePhrase = "poultry outwits ants";
private const int MaxWordsInPhrase = 4;
///
/// Main entry point
///
public static void Main()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var expectedHashes = new[]
{
"e4820b45d2277f3844eac66c903e84be",
"23170acc097c24edb98fc5488ab033fe",
"665e5bcb0c20062fe8abaaf4628bb154",
};
var expectedHashesAsVectors = expectedHashes.Select(hash => new Vector(HexadecimalStringToByteArray(hash))).ToArray();
var processor = new StringsProcessor(Encoding.ASCII.GetBytes(SourcePhrase), MaxWordsInPhrase, ReadInput());
Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}");
#if DEBUG
// it makes the program slow (as all anagrams are generated twice), but this code is only run in a debug mode
var totalAnagramsCount = processor.GeneratePhrases().Count();
Console.WriteLine($"Total anagrams count: {totalAnagramsCount}; time from start: {stopwatch.Elapsed}");
#endif
processor.GeneratePhrases()
.Select(phraseBytes => new { phraseBytes, hashVector = ComputeHashVector(phraseBytes) })
.Where(tuple => Array.IndexOf(expectedHashesAsVectors, tuple.hashVector) >= 0)
.Select(tuple => new { phrase = Encoding.ASCII.GetString(tuple.phraseBytes), hash = VectorToHexadecimalString(tuple.hashVector) })
.ForAll(phraseInfo => Console.WriteLine($"Found phrase for {phraseInfo.hash}: {phraseInfo.phrase}; time from start is {stopwatch.Elapsed}"));
stopwatch.Stop();
Console.WriteLine($"Done; time from start: {stopwatch.Elapsed}");
}
// Code taken from http://stackoverflow.com/a/321404/831314
private static byte[] HexadecimalStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
// Bouncy Castle is used instead of standard .NET methods for performance reasons
private static Vector ComputeHashVector(byte[] input)
{
var digest = new MD5Digest();
digest.BlockUpdate(input, 0, input.Length);
byte[] hash = new byte[16];
digest.DoFinal(hash, 0);
return new Vector(hash);
}
private static string VectorToHexadecimalString(Vector hash)
{
return string.Concat(Enumerable.Range(0, 16).Select(i => hash[i].ToString("x2")));
}
private static IEnumerable ReadInput()
{
string line;
while ((line = Console.ReadLine()) != null)
{
yield return Encoding.ASCII.GetBytes(line);
}
}
#if SINGLE_THREADED
private static void ForAll(this IEnumerable source, Action action)
{
foreach (var entry in source)
{
action(entry);
}
}
#endif
}
}