content: import           Control.Concurrent      (forkIO, setNumCapabilities)
  import           Control.Concurrent.Chan (Chan, newChan, readChan, writeList2Chan)
  import           Control.Monad           (replicateM, replicateM_, forever)
  import           Crypto.Hash             (SHA256(..), Digest, hashWith)
  import           Data.Bifunctor          (first)
  import           Data.ByteString         (ByteString, pack)
  import           Data.Char               (isDigit)
  import           Data.List.Split         (chunksOf)
  import           Data.Word               (Word8)
  import           GHC.Conc                (getNumProcessors)
  import           System.Environment      (getArgs)
  import           Text.Printf             (printf)
  
  type Decrypted = String
  type Encrypted = Digest SHA256
  type TestString = [Word8]
  
  hashedValues :: [Encrypted]
  hashedValues = read <$>
    [ "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b"
    , "74e1bb62f8dabb8125a58852b63bdf6eaef667cb56ac7f7cdba6d7305c50a22f"
    , "1115dd800feaacefdf481f1f9070374a2a81e27880f187396db67958b207cbad" ]
  
  chunks :: [[TestString]]
  chunks = chunksOf (10^3) $ replicateM 5 [97..122]
  
  findMatch :: TestString -> [(Encrypted, Decrypted)] -> [(Encrypted, Decrypted)]
  findMatch w acc
    | hashed `elem` hashedValues = (hashed, show bStr):acc
    | otherwise = acc
    where
      bStr = pack w
      hashed = hashWith SHA256 bStr
  
  searchWorker :: Chan [TestString] -> Chan (Encrypted, Decrypted) -> IO ()
  searchWorker batchChan resultChan = forever (readChan batchChan >>= writeList2Chan resultChan . foldr findMatch [])
  
  parseInput :: [String] -> Int -> Int
  parseInput [] n    = n
  parseInput (s:_) n = if all isDigit s then read s else n
  
  main :: IO ()
  main = do
    workers <- getArgs
    cpus <- getNumProcessors
    let wCount = parseInput workers cpus
    setNumCapabilities wCount
    printf "Using %d workers on %d cpus.\n" wCount cpus
    resultChan <- newChan
    batchChan <- newChan
    replicateM_ wCount (forkIO $ searchWorker batchChan resultChan)
    writeList2Chan batchChan chunks
    replicateM_ (length hashedValues) (readChan resultChan >>= uncurry (printf "%s -> %s\n") . first show)
  