1 import Data.List (nub, tails, null)
2 import Data.Hash.MD5 (md5s, Str(..))
4 import Data.ByteString.Char8 (pack)
5 import Crypto.Hash (hash, Digest, MD5)
17 part1 = print $ head $ drop 63 $ filter (\i -> possibleStart sq i && confirmStart sq i) [0..]
18 where sq = md5sequence
21 part2 = print $ head $ drop 63 $ filter (\i -> possibleStart sq i && confirmStart sq i) [0..]
22 where sq = md5sequenceS
25 getHash :: String -> String
26 getHash bs = show (hash $ pack bs :: Digest MD5)
28 md5sequence :: [String]
29 md5sequence = [makeMd5 i | i <- [0..]]
30 where makeMd5 i = getHash (salt ++ show i)
32 md5sequenceS :: [String]
33 md5sequenceS = [makeMd5 i | i <- [0..]]
34 where makeMd5 i = stretch $ md5s (Str (salt ++ show i))
35 stretch h0 = foldr (\_ h -> getHash h) h0 [1..2016]
37 possibleStart :: [String] -> Int-> Bool
38 -- possibleStart s i = not $ null $ repeats 3 $ s!!i
39 possibleStart s = not . null . repeats 3 . ((!!) s)
41 confirmStart :: [String] -> Int -> Bool
42 confirmStart s i = any (confirmation) $ take 1000 $ drop (i+1) s
43 where c = head $ repeats 3 $ s!!i
44 confirmation m = c `elem` (repeats 5 m)
47 repeats :: Int -> String -> [String]
48 repeats n = filter (null . tail) . map (nub) . substrings n
50 substrings :: Int -> [a] -> [[a]]
51 substrings l = filter (\s -> (length s) == l) . map (take l) . tails