diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Francis Lim (c) 2020
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Francis Lim nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,122 @@
+<p align="center"> <img src="longshot.png" height="200"/> </p>
+
+> Is it really a __long shot__ to hope that the COVID-19 pandemic will end?
+
+
+
+[![Build Status](https://travis-ci.com/thyeem/longshot.svg?branch=master)](https://travis-ci.com/thyeem/longshot) <!-- [![Hackage](https://img.shields.io/hackage/v/longshot)](https://hackage.haskell.org/package/longshot) -->
+
+# longshot
+
+__Search for preimage__ from a given hash value using _Brute-force_ method based on _parallelism_
+
+* Support various __search lengths__, __character sets__ and __hashers__.
+* Strict mode: searches only for a given _exact length_
+* Deep mode: searches _everything less than or equal_ to a given length.
+* Use `CPUs` as _much_ as possible. __Get the most out of them!__
+* Use, however, `memory` as _little_ as possible.
+  
+
+Enjoy `longshot`. Good luck!
+
+
+
+```plain
+longshot - Fast and concise Brute-force search
+
+Usage:
+  longshot run        [-n SIZE] [-c CHARS] [-a HASHER] [--deep] HEX
+  longshot image      [-a HASHER] KEY
+
+Commands:
+  run                 Brute-force search with given hexstring and options
+  image               Generate image from given key string and hash algorithm
+
+Arguments:
+  HEX                 Specify target hexstring to search
+  KEY                 Specify key string as a preimage
+
+Options:
+  -h --help           Show this
+  -n SIZE             Specify search length  [default: 8]   
+  -c CHARS            Specify characters available in preimage  [default: 0123456789]
+  -a HASHER           Specify hash algorithm  [default: sha256]
+                      HASHER available below:
+                      md5           sha1          ripemd160     whirlpool
+                      sha256        sha3_256      sha3_384      sha3_512
+                      blake2s_256   blake2b_256   blake2b_384   blake2b_512
+                      blake3_256    blake3_384    blake3_512
+                      keccak_256    keccak_384    keccak_512
+                      skein_256     skein_384     skein_512
+  --deep              Search deeply including less than a given search length
+```
+
+## How to build
+```bash
+$ git clone https://github.com/thyeem/longshot.git
+
+$ make build 
+
+## Optional: test if installed properly using quickcheck
+$ make test
+
+## You can see an executable 'longshot' here
+$ cd app
+```
+
+## Quick start
+```bash
+## Refer to the following usage:
+## Note that the order of arguments and options matter.
+$ ./longshot -h
+
+## Generate test hash values
+$ ./longshot image 12345
+5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5
+
+$ ./longshot image 12345678
+ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f
+
+## Brute-force search with default options:
+## The same as: ./longshot -n 8 -c 0123456789 -a sha256 ef797c..98a64f
+$ ./longshot run ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f
+Found  12345678
+
+## Search below fails because default search length is 8.
+## In default mode, it searches by the exact length of key.
+$ ./longshot run 5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5
+Not found
+
+## If you don't know the key length, use deep search. (--deep)
+## It will try to search with less than or equal to the given length.
+$ ./longshot run --deep 5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5
+Found  12345
+```
+These are all about how to use `longshot`.  
+See below for more interesting detailed examples.
+
+## More examples
+```bash
+## Generate a example image using Blake2b hash algorithm
+## Blake2b-hashed sofia (my first daughter!)
+$ ./longshot image -a blake2b sofia
+bb40f637bb211532318965627f15bb165f701230fd83e0adf8cd673a6ee02830
+
+## Need different character set. Don't forget --deep
+$ ./longshot run -c 'abcdefghijklmnopqrstuvwxyz' -a blake2b --deep bb40f6..e02830
+Found  sofia
+
+## You should consider the following if there might be more characters in preimage.
+## More characters, much longer time to find!
+$ ./longshot run -c 'abcdefghijklmnopqrstuvwxyz0123456789' -a blake2b --deep bb40f6..e02830
+
+## Roughly, time spending is proportional to (Number of char available) ^ (char length).
+## Exponent 'char length' is surely more dominant! Use long-long password as always!
+
+## longshot is very efficient and get the most of CPU's power in parallel.
+## But this kind of work would be very painful time even for longshot.
+$ ./longshot run -n 12 -c 'abcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()-=_+[]{}\|' \
+                 -a blake2b --deep bb40f6..e02830 +RTS -s
+
+## '+RTS -s' the end of line is optional, and that is for a summary of CPU time and memory.
+```
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE QuasiQuotes #-}
+module Main where
+
+import           Control.Monad
+import           System.Environment
+import           System.Console.Docopt
+import qualified Data.ByteString.Char8         as C
+import qualified Data.ByteString.Base16        as H
+import           Crypto.Longshot.Internal
+import           Crypto.Longshot.Hasher
+
+patterns :: Docopt
+patterns = [docopt|
+longshot - Fast and concise Brute-force search
+
+Usage:
+  longshot run        [-n SIZE] [-c CHARS] [-a HASHER] [--deep] HEX
+  longshot image      [-a HASHER] KEY
+
+Commands:
+  run                 Brute-force search with given hexstring and options
+  image               Generate image from given key string and hash algorithm
+
+Arguments:
+  HEX                 Specify target hexstring to search
+  KEY                 Specify key string as a preimage
+
+Options:
+  -h --help           Show this
+  -n SIZE             Specify search length  [default: 8]   
+  -c CHARS            Specify characters in preimage  [default: 0123456789]
+  -a HASHER           Specify hash algorithm  [default: sha256]
+                      HASHER available below:
+                      md5           sha1          ripemd160     whirlpool
+                      sha256        sha3_256      sha3_384      sha3_512
+                      blake2s_256   blake2b_256   blake2b_384   blake2b_512
+                      blake3_256    blake3_384    blake3_512
+                      keccak_256    keccak_384    keccak_512
+                      skein_256     skein_384     skein_512
+  --deep              Search deeply including less than a given search length
+|]
+
+-- | Defines args-ops frequently used
+(><) = isPresent
+(<->|!) = getArgOrExitWith patterns
+(<->) = getArg
+
+main :: IO ()
+main = do
+  args <- parseArgsOrExit patterns =<< getArgs
+  when (args >< command "image") $ genImage args
+  when (args >< command "run") $ run args
+
+-- | Command: image
+genImage :: Arguments -> IO ()
+genImage args = do
+  key    <- C.pack <$> (args <->|! argument "KEY")
+  hasher <- getHasher <$> (args <->|! shortOption 'a')
+  putStrLn . C.unpack . H.encode . hasher $ key
+
+-- | Command: run
+run :: Arguments -> IO ()
+run args = do
+  hex    <- args <->|! argument "HEX"
+  chars  <- args <->|! shortOption 'c'
+  size   <- (read <$> (args <->|! shortOption 'n')) :: IO Int
+  hasher <- getHasher <$> (args <->|! shortOption 'a')
+  let solver | args >< longOption "deep" = bruteforceDeep
+             | otherwise                 = bruteforce
+  let found = solver size chars hex hasher
+  case found of
+    Just key -> putStrLn $ "Found  " <> key
+    _        -> putStrLn "Not found"
diff --git a/longshot.cabal b/longshot.cabal
new file mode 100644
--- /dev/null
+++ b/longshot.cabal
@@ -0,0 +1,119 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.33.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 5c862a15f456f6ba99cb05a6abe2c68fa81755d8855575d2dd63872d01d2dd7c
+
+name:           longshot
+version:        0.1.0.0
+synopsis:       Fast Brute-force search using parallelism
+description:    Longshot enables to search for preimages from a given hash value
+                using a brute-force method based on parallelism.
+                .
+                * Support various search lengths, character sets and hashers
+                * Strict mode: searches only for a given exact length
+                * Deep mode: searches everything less than or equal to a given length.
+                * Use @CPUs@ as much as possible. Get the most out of them!
+                * Use, however, @memory@ as little as possible.
+                .
+                Please see the documentation at https://github.com/thyeem/longshot
+                for usage example.
+                .
+category:       algorithm, search, cryptography, parallelism
+homepage:       https://github.com/thyeem/longshot#readme
+bug-reports:    https://github.com/thyeem/longshot/issues
+author:         Francis Lim, Jongwhan Lee
+maintainer:     thyeem@gmail.com
+copyright:      Francis, 2020
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/thyeem/longshot
+
+library
+  exposed-modules:
+      Crypto.Longshot
+      Crypto.Longshot.Hasher
+      Crypto.Longshot.Internal
+      Crypto.Longshot.TH
+  other-modules:
+      Paths_longshot
+  hs-source-dirs:
+      src
+  default-extensions: TemplateHaskell KindSignatures DataKinds
+  build-depends:
+      base >=4.12.0 && <5
+    , base16-bytestring >=0.1.0 && <0.2
+    , blake2 >=0.3.0 && <0.4
+    , blake3 >=0.2 && <0.3
+    , bytestring >=0.10.8 && <0.11
+    , containers >=0.6 && <0.7
+    , cryptohash-sha256 >=0.11.101 && <0.12
+    , cryptonite >=0.25
+    , deepseq >=1.4.4 && <1.5
+    , docopt >=0.7.0 && <0.8
+    , memory >=0.14.0 && <0.16
+    , parallel >=3.2.2 && <3.3
+    , template-haskell >=2.14.0 && <2.17
+  default-language: Haskell2010
+
+executable longshot
+  main-is: Main.hs
+  other-modules:
+      Paths_longshot
+  hs-source-dirs:
+      app
+  default-extensions: TemplateHaskell KindSignatures DataKinds
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -dynamic -feager-blackholing -g0 -eventlog
+  build-depends:
+      base >=4.12.0 && <5
+    , base16-bytestring >=0.1.0 && <0.2
+    , blake2 >=0.3.0 && <0.4
+    , blake3 >=0.2 && <0.3
+    , bytestring >=0.10.8 && <0.11
+    , containers >=0.6 && <0.7
+    , cryptohash-sha256 >=0.11.101 && <0.12
+    , cryptonite >=0.25
+    , deepseq >=1.4.4 && <1.5
+    , docopt >=0.7.0 && <0.8
+    , longshot
+    , memory >=0.14.0 && <0.16
+    , parallel >=3.2.2 && <3.3
+    , template-haskell >=2.14.0 && <2.17
+  default-language: Haskell2010
+
+test-suite longshot-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_longshot
+  hs-source-dirs:
+      test
+  default-extensions: TemplateHaskell KindSignatures DataKinds
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.12.0 && <5
+    , base16-bytestring >=0.1.0 && <0.2
+    , blake2 >=0.3.0 && <0.4
+    , blake3 >=0.2 && <0.3
+    , bytestring >=0.10.8 && <0.11
+    , containers >=0.6 && <0.7
+    , cryptohash-sha256 >=0.11.101 && <0.12
+    , cryptonite >=0.25
+    , deepseq >=1.4.4 && <1.5
+    , docopt >=0.7.0 && <0.8
+    , longshot
+    , memory >=0.14.0 && <0.16
+    , parallel >=3.2.2 && <3.3
+    , tasty
+    , tasty-hunit
+    , tasty-quickcheck
+    , template-haskell >=2.14.0 && <2.17
+  default-language: Haskell2010
diff --git a/src/Crypto/Longshot.hs b/src/Crypto/Longshot.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Longshot.hs
@@ -0,0 +1,70 @@
+-- |
+-- Module      : Crypto.Longshot
+-- License     : MIT
+-- Maintainer  : Francis Lim <thyeem@gmail.com>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- How big is the search space? The space consists of two axes.
+--
+-- * X-axis: Number of characters available
+-- * Y-axis: Search length of preimage to find
+--
+-- Note that it's proportional to @(X ^ Y)@ rather than @(X * Y)@
+--
+-- The values below are defined by default.
+--
+-- When not provided as options in CUI, the following values are used.
+--
+module Crypto.Longshot
+  ( defChars
+  , defSearchLength
+  , defNumPrefix
+  , defNumBind
+  , image
+  , byteChars
+  , bytePrefixes
+  , toKey
+  )
+where
+
+import           Control.Monad
+import qualified Data.ByteString.Char8         as C
+import qualified Data.ByteString.Base16        as H
+
+-- | Characters available in a preimage
+defChars :: String
+defChars = "0123456789"
+
+-- | Search length of preimage
+defSearchLength :: Int
+defSearchLength = 8
+
+-- | Limit search length of preimage
+limitSearchLength :: Int
+limitSearchLength = 20
+
+-- | Value related to the number of sparks
+defNumPrefix :: Int
+defNumPrefix = 3
+
+-- | Number of actions in TH bruteforceN
+defNumBind :: Int
+defNumBind = limitSearchLength - defNumPrefix
+--------------------------------------------------------------------
+
+-- | Image bytestring: target hash value to find
+image :: String -> C.ByteString
+image = fst . H.decode . C.pack
+
+-- | Bytestring usable for preimage
+byteChars :: String -> [C.ByteString]
+byteChars chars = C.pack . (: []) <$> chars
+
+-- | Combination of prefixes possible: size of (length of chars) ^ (numPrefix)
+bytePrefixes :: Int -> String -> [C.ByteString]
+bytePrefixes numPrefix chars = C.pack <$> replicateM numPrefix chars
+
+-- | Convert preimage found into key string
+toKey :: C.ByteString -> String
+toKey = C.unpack
diff --git a/src/Crypto/Longshot/Hasher.hs b/src/Crypto/Longshot/Hasher.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Longshot/Hasher.hs
@@ -0,0 +1,55 @@
+-- |
+-- Module      : Crypto.Longshot.Hasher
+-- License     : MIT
+-- Maintainer  : Francis Lim <thyeem@gmail.com>
+-- Stability   : experimental
+-- Portability : unknown
+--
+module Crypto.Longshot.Hasher
+  ( Hasher
+  , getHasher
+  )
+where
+
+import           GHC.TypeLits
+import           Numeric.Natural
+import qualified Data.ByteArray                as B
+import qualified Data.ByteString.Char8         as C
+import qualified Crypto.Hash                   as X
+import qualified Crypto.Hash.SHA256            as S
+import qualified Crypto.Hash.BLAKE2.BLAKE2s    as B2s
+import qualified Crypto.Hash.BLAKE2.BLAKE2b    as B2b
+import qualified BLAKE3                        as B3
+
+-- | Type for hash functions available
+type Hasher = C.ByteString -> C.ByteString
+
+type Blake3_256 = C.ByteString -> B3.Digest (32 :: Nat)
+type Blake3_384 = C.ByteString -> B3.Digest (48 :: Nat)
+type Blake3_512 = C.ByteString -> B3.Digest (64 :: Nat)
+
+-- | Select hasher by defined name
+getHasher :: String -> Hasher
+getHasher name = case name of
+  "md5"         -> B.convert . X.hashWith X.MD5
+  "sha1"        -> B.convert . X.hashWith X.SHA1
+  "ripemd160"   -> B.convert . X.hashWith X.RIPEMD160
+  "whirlpool"   -> B.convert . X.hashWith X.Whirlpool
+  "sha256"      -> S.hash
+  "sha3_256"    -> B.convert . X.hashWith X.SHA3_256
+  "sha3_384"    -> B.convert . X.hashWith X.SHA3_384
+  "sha3_512"    -> B.convert . X.hashWith X.SHA3_512
+  "blake2s_256" -> B2s.hash 32 mempty
+  "blake2b_256" -> B2b.hash 32 mempty
+  "blake2b_384" -> B2b.hash 48 mempty
+  "blake2b_512" -> B2b.hash 64 mempty
+  "blake3_256"  -> B.convert . (B3.hash . (: []) :: Blake3_256)
+  "blake3_384"  -> B.convert . (B3.hash . (: []) :: Blake3_384)
+  "blake3_512"  -> B.convert . (B3.hash . (: []) :: Blake3_512)
+  "keccak_256"  -> B.convert . X.hashWith X.Keccak_256
+  "keccak_384"  -> B.convert . X.hashWith X.Keccak_384
+  "keccak_512"  -> B.convert . X.hashWith X.Keccak_512
+  "skein_256"   -> B.convert . X.hashWith X.Skein256_256
+  "skein_384"   -> B.convert . X.hashWith X.Skein512_384
+  "skein_512"   -> B.convert . X.hashWith X.Skein512_512
+  a             -> errorWithoutStackTrace $ "Not allowed hash algorithm  " <> a
diff --git a/src/Crypto/Longshot/Internal.hs b/src/Crypto/Longshot/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Longshot/Internal.hs
@@ -0,0 +1,62 @@
+-- |
+-- Module      : Crypto.Longshot.Internal
+-- License     : MIT
+-- Maintainer  : Francis Lim <thyeem@gmail.com>
+-- Stability   : experimental
+-- Portability : unknown
+--
+module Crypto.Longshot.Internal
+  ( bruteforce
+  , bruteforceDeep
+  )
+where
+
+import           Control.Applicative
+import           Control.DeepSeq
+import           Control.Parallel
+import           Data.Foldable
+import qualified Data.ByteString.Char8         as C
+import           Language.Haskell.TH
+import           Crypto.Longshot
+import           Crypto.Longshot.TH
+import           Crypto.Longshot.Hasher
+
+-- Declaration of bruteforceN: generating code by splicing
+$( funcGenerator )
+
+-- | Brute-force search only for a given exact length
+bruteforce :: Int -> String -> String -> Hasher -> Maybe String
+bruteforce size chars hex hasher = found
+ where
+  found  = foldl' (<|>) empty (runPar <%> prefixes)
+  runPar = bruteforcePar numBind (byteChars chars) (image hex) hasher
+  numPrefix | size < defNumPrefix = 1
+            | otherwise           = defNumPrefix
+  numBind  = size - numPrefix
+  prefixes = bytePrefixes numPrefix chars
+
+-- | Pick up an appropriate search function
+bruteforcePar
+  :: Int
+  -> [C.ByteString]
+  -> C.ByteString
+  -> Hasher
+  -> C.ByteString
+  -> Maybe String
+bruteforcePar n
+  | n `elem` [0 .. defNumBind] = $( funcList ) !! n
+  | otherwise = errorWithoutStackTrace "Not available search length"
+
+-- | Deep Brute-force search including less than a given search size
+bruteforceDeep :: Int -> String -> String -> Hasher -> Maybe String
+bruteforceDeep size x y z = foldl' (<|>) empty found
+ where
+  found = deep x y z <%> [1 .. size]
+  deep a b c d = bruteforce d a b c
+
+-- | Parallel map using deepseq, par and pseq
+(<%>) :: (NFData a, NFData b) => (a -> b) -> [a] -> [b]
+f <%> []       = []
+f <%> (x : xs) = y `par` ys `pseq` (y : ys) where
+  y  = force $ f x
+  ys = f <%> xs
diff --git a/src/Crypto/Longshot/TH.hs b/src/Crypto/Longshot/TH.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Longshot/TH.hs
@@ -0,0 +1,49 @@
+-- |
+-- Module      : Crypto.Longshot.TH
+-- License     : MIT
+-- Maintainer  : Francis Lim <thyeem@gmail.com>
+-- Stability   : experimental
+-- Portability : unknown
+--
+module Crypto.Longshot.TH where
+
+import           Control.Monad
+import           Data.Foldable
+import           Language.Haskell.TH
+import           Crypto.Longshot
+
+-- | Brute-force with N-search-length using TH
+bruteforceN :: Int -> Q Exp -> Q Exp -> Q Exp -> Q Exp -> Q Exp
+bruteforceN numBind chars hex hasher prefix = do
+  names <- replicateM numBind (newName "names")
+  let pats  = varP <$> names
+  let bytes = [| $( prefix ) <>
+                 $( foldl' (\a b -> [| $a <> $b |])
+                    [| mempty |]
+                    (varE <$> names)
+                  )
+              |]
+  let cond  = condE [| $( appE hasher bytes ) == $(hex) |]
+                    [| Just (toKey $( bytes )) |]
+                    [| Nothing |]
+  let stmts = ((`bindS` chars) <$> pats) <> [noBindS cond]
+  [| foldl' (<|>) empty $( compE stmts ) |]
+
+-- | Declare functions to run in parallel for search
+funcGenerator :: Q [Dec]
+funcGenerator = forM [0 .. defNumBind] funcG where 
+  funcG numBind = do
+    let name = mkName $ "bruteforce" <> show numBind
+    chars <- newName "chars"
+    hex <- newName "hex"
+    hasher <- newName "hasher"
+    prefix <- newName "prefix"
+    funD name 
+      [ clause [varP chars, varP hex, varP hasher, varP prefix] 
+        (normalB (bruteforceN numBind (varE chars) (varE hex) (varE hasher) (varE prefix))) 
+        []
+      ]
+
+-- | Get list of functions to run in parallel for search
+funcList :: Q Exp
+funcList = listE (varE . mkName . ("bruteforce" <>) . show <$> [0 .. defNumBind])
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,84 @@
+import           Test.Tasty
+import           Test.Tasty.QuickCheck
+import           Test.Tasty.HUnit
+import           Control.Monad
+import qualified Data.ByteString.Char8         as C
+import qualified Data.ByteString.Base16        as H
+import           Crypto.Longshot.Internal
+import           Crypto.Longshot.Hasher
+
+main :: IO ()
+main = defaultMain tests
+
+-- | Search Length limit
+-- The key length was limited to 4 as it takes a long time to test
+limit :: Int
+limit = 4
+
+-- | Characters prepared for test
+chars :: String
+chars = "0123456789abcdef~'*+-%^?[]/"
+
+-- | Hash algorithms available
+hashers :: [String]
+hashers =
+  [ "md5"
+  , "sha1"
+  , "ripemd160"
+  , "whirlpool"
+  , "sha256"
+  , "sha3_256"
+  , "sha3_384"
+  , "sha3_512"
+  , "blake2s_256"
+  , "blake2b_256"
+  , "blake2b_384"
+  , "blake2b_512"
+  , "blake3_256"
+  , "blake3_384"
+  , "blake3_512"
+  , "keccak_256"
+  , "keccak_384"
+  , "keccak_512"
+  , "skein_256"
+  , "skein_384"
+  , "skein_512"
+  ]
+
+-- | Test for strict mode of Brute-force search
+testLongshot :: Hasher -> Gen Bool
+testLongshot hasher = do
+  size <- choose (1, limit) :: Gen Int
+  key  <- replicateM size (elements chars :: Gen Char)
+  let hex   = C.unpack . H.encode . hasher . C.pack $ key
+  let found = bruteforce size chars hex hasher
+  case found of
+    Just x | x == key -> return True
+    _                 -> return False
+
+-- | Test for deep mode of Brute-force search
+testLongshotDeep :: Hasher -> Gen Bool
+testLongshotDeep hasher = do
+  let size = limit
+  size' <- choose (1, limit) :: Gen Int
+  key   <- replicateM size' (elements chars :: Gen Char)
+  let hex   = C.unpack . H.encode . hasher . C.pack $ key
+  let found = bruteforceDeep size chars hex hasher
+  case found of
+    Just x | x == key -> return True
+    _                 -> return False
+
+-- | Property test generator
+genTestProp :: (Hasher -> Gen Bool) -> TestName -> TestName -> TestTree
+genTestProp f desc algo = testProperty (desc <> algo) (f $ getHasher algo)
+
+-- | Main test of properties
+tests :: TestTree
+tests = testGroup
+  "Longshot Tests"
+  [ testGroup "Strict-Longshot search"
+              (genTestProp testLongshot "testLongshot with " <$> hashers)
+  , testGroup
+    "Deep-Longshot search"
+    (genTestProp testLongshotDeep "testLongshotDeep with " <$> hashers)
+  ]
