#r "nuget: FSharpx.Collections, 3.0.1" open System.Security.Cryptography open System.Text open FSharpx.Collections let salt = "yjdafjpo" let md5 (data: string): string = use md5 = MD5.Create() (StringBuilder (), md5.ComputeHash(Encoding.ASCII.GetBytes data)) ||> Array.fold (fun sb b -> sb.Append(b.ToString("x2"))) |> string // returns Some char being repeated for n many times in input hash, otherwise None if no repetitions let getSameChar (n: int) (hash: string) : char option = let sameChar (window: char array): char = Array.reduce (fun x y -> if x = y then y else char 0) window hash |> Seq.windowed n |> Seq.map sameChar |> Seq.filter (fun x -> x <> char 0) |> Seq.toArray |> fun x -> if x.Length > 0 then Some x[0] else None // returns true if any of the hashes contain ch repeated 5 times let hashRepeats (ch: char) (hashes: Deque): bool = Seq.map (getSameChar 5) hashes |> Seq.filter (function | Some x -> x = ch | None -> false) |> fun x -> Seq.length x > 0 // hashes contains hash of salt + index, plus the next 1000 hashes let rec getPadKey (currInd: int) (indices: int list) (hashes: Deque) (hashfunc: string -> string) = if indices.Length = 64 then indices.Head else let current, rest = hashes.Uncons let hasMatch = match getSameChar 3 current with | Some x -> hashRepeats x rest | None -> false let newHashes = rest.Conj <| hashfunc (salt + string (currInd + 1001)) if hasMatch then getPadKey (currInd + 1) (currInd::indices) newHashes hashfunc else getPadKey (currInd + 1) indices newHashes hashfunc let part1hash = md5 let part2hash = seq { for n in 0..2016 do yield md5 } |> Seq.reduce (>>) // part 1 let p1first1000 = Deque.ofList [ for i in 0..1000 do yield part1hash (salt + string i) ] getPadKey 0 [] p1first1000 part1hash |> printfn "%d" // part 2 let p2first1000 = Deque.ofList [ for i in 0..1000 do yield part2hash (salt + string i) ] getPadKey 0 [] p2first1000 part2hash |> printfn "%d"