packages feed

memorable-bits (empty) → 0.1.0.0

raw patch · 13 files changed

+6944/−0 lines, 13 filesdep +HUnitdep +basedep +binarysetup-changed

Dependencies added: HUnit, base, binary, bits, bytes, bytestring, criterion, cryptonite, data-dword, doctest, hashable, memorable-bits, memory, mtl, network-ip, optparse-applicative, random, split, tasty, tasty-hunit, tasty-quickcheck, tasty-smallcheck

Files

+ LICENSE view
@@ -0,0 +1,26 @@+Copyright (c) 2016, Luke Clifton+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. 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.++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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Membits.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Main where++import Unsafe.Coerce+import Control.Exception+import Crypto.Hash+import qualified Data.ByteString.Lazy as B+import Data.Char+import Data.Maybe+import Data.Memorable+import Data.Memorable.Theme.Words+import Data.Monoid+import Data.Type.Equality+import GHC.TypeLits+import Options.Applicative+import System.Environment+import System.IO++type Algorithm = B.ByteString -> String++data Opt+    = HashOpt+        { algorithm :: Algorithm+        , files :: [FilePath]+        }+    | Convert++algOption ::+    ( Show a+    , 40 <= MemLen (Digest a)+    , KnownNat (MemLen (Digest a))+    , Memorable (Digest a)+    , HashAlgorithm a+    ) => a -> Parser Algorithm+algOption a = flag' (goAlg a) (long (map toLower $ show a))++algs :: [Parser Algorithm]+algs =+    [ algOption Blake2b_512+    , algOption Blake2bp_512+    , algOption Blake2s_224+    , algOption Blake2s_256+    , algOption Blake2sp_224+    , algOption Blake2sp_256+    , algOption Keccak_224+    , algOption Keccak_256+    , algOption Keccak_384+    , algOption Keccak_512+    , algOption MD2+    , algOption MD4+    , algOption MD5+    , algOption RIPEMD160+    , algOption SHA1+    , algOption SHA224+    , algOption SHA256+    , algOption SHA384+    , algOption SHA3_224+    , algOption SHA3_256+    , algOption SHA3_384+    , algOption SHA3_512+    , algOption SHA512+    , algOption SHA512t_224+    , algOption SHA512t_256+    , algOption Skein256_224+    , algOption Skein256_256+    , algOption Skein512_224+    , algOption Skein512_256+    , algOption Skein512_384+    , algOption Skein512_512+    , algOption Tiger+    , algOption Whirlpool+    ]++choice :: Alternative f => f a -> [f a] -> f a+choice = foldr (<|>)++opts :: Parser Opt+opts = parseHashOpts <|> parseConv+    where+        parseHashOpts = do+            algorithm <- parseAlgorithm+            files <- parseFiles+            pure HashOpt{..}+        parseAlgorithm = choice empty algs+        parseFiles = many $ strArgument (metavar "FILE" <> help "Files to hash" <> action "file")+        parseConv = flag' Convert (long "conv" <> help "Convert hex lines on stdin to a memorable pattern")++main :: IO ()+main = do+    opts <- execParser (info opts fullDesc)+    case opts of+        Convert -> do+            ls <- lines <$> getContents+            mapM_ (either (hPutStrLn stderr) putStrLn . doConv) ls+        HashOpt{..} -> mapM_ (run algorithm) files++run :: Algorithm -> FilePath -> IO ()+run a f = do+    res <- try $ do+        bs <- B.readFile f+        return $! a bs+    case res of+        Right h -> do+            putStr h+            putStr "\t"+            putStrLn f+        Left (e :: SomeException) -> hPutStrLn stderr (displayException e)++goAlg :: forall a.+    ( 40 <= MemLen (Digest a)+    , KnownNat (MemLen (Digest a))+    , Memorable (Digest a)+    , HashAlgorithm a+    ) => a -> Algorithm+goAlg a b =+    let+        h = hashlazy b :: Digest a+    in+        renderMemorable (padHex $ four flw10) h++moreThan40 :: KnownNat n => Proxy (n :: Nat) -> Maybe ((40 <=? n) :~: True)+moreThan40 p = if natVal p > 40 then Just $ unsafeCoerce Refl else Nothing++doConv :: String -> Either String String+doConv i =+    let+        Just bitsIn = someNatVal $ fromIntegral $ length i * 4+    in+        case bitsIn of+            SomeNat (p :: Proxy n) ->+                case moreThan40 p of+                    Just Refl -> +                        let+                            s = rerender (hex @n) (padHex $ four flw10) i+                        in+                            maybe (Left $ "failed to convert '" ++ i ++ "'") Right s+                    Nothing -> Left $ "failed to convert '" ++ i ++ "'"+
+ bench/Main.hs view
@@ -0,0 +1,28 @@+module Main where++import Control.Monad+import Criterion.Main+import Data.ByteString.Lazy (ByteString, pack)+import Data.Memorable+import Data.Memorable.Theme.Words+import System.Random+import Data.Word++benchmarks :: Word8 -> Word32 -> Word64 -> [Benchmark]+benchmarks w8 w32 w64 =+    [ bgroup "words"+        [ bench "word8"               $ nf (renderMemorable words8) w8+        , bench "threeWordsFor32Bits" $ nf (renderMemorable threeWordsFor32Bits) w32+        , bench "fourWordsFor32Bits"  $ nf (renderMemorable fourEqualWordsFor32Bits) w32+        , bench "sixWordsFor64Bits"   $ nf (renderMemorable sixWordsFor64Bits) w64+        , bench "eightWordsFor64Bits" $ nf (renderMemorable eightEqualWordsFor64Bits) w64+        , bench "big tuple"           $ nf (renderMemorable (four eightEqualWordsFor64Bits)) (w64,w64,w64,w64)+        ]+    ]++main :: IO ()+main = do+    w8 <- randomIO+    w32 <- randomIO+    w64 <- randomIO+    defaultMain $ benchmarks w8 w32 w64
+ memorable-bits.cabal view
@@ -0,0 +1,95 @@+-- Initial memorable-bits.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++name:                memorable-bits+version:             0.1.0.0+synopsis:            Generate human memorable strings from binary data.+license:             BSD2+license-file:        LICENSE+author:              Luke Clifton+maintainer:          lukec@themk.net+category:            Data+build-type:          Simple+cabal-version:       >=1.10++flag data-dword-inst+  description: Include data-dword and provide instances for its types.+  default: True++flag network-ip-inst+  description: Include network-ip and provide instances for its types.+  default: True++flag cryptonite-inst+  description: Include cryptonite and provide instances for its types.+  default: True++flag exes+  description: Build a set of executables for printing memorable hashes of files.+  default: True++library+  exposed-modules:     Data.Memorable+                     , Data.Memorable.Internal+                     , Data.Memorable.Theme.Food+                     , Data.Memorable.Theme.Fantasy+                     , Data.Memorable.Theme.Metric+                     , Data.Memorable.Theme.Science+                     , Data.Memorable.Theme.Words+  build-depends:       base >=4.8 && <5+                     , bits >=0.5+                     , bytes >=0.15+                     , binary >=0.8+                     , bytestring >=0.10+                     , hashable >=1.2+                     , split >=0.2+                     , random >=1.1+                     , mtl >=2.2.1+  hs-source-dirs:      src+  default-language:    Haskell2010+  if flag(network-ip-inst) {+    cpp-options: -DNETWORK_IP -DDATA_DWORD+    build-depends: network-ip, data-dword+  }+  if flag(data-dword-inst) {+    cpp-options: -DDATA_DWORD+    build-depends: data-dword+  }+  if flag(cryptonite-inst) {+    cpp-options: -DCRYPTONITE+    build-depends: cryptonite, memory+  }++test-suite test-memorable-bits+  type:                exitcode-stdio-1.0+  main-is:             test/Main.hs+  build-depends:       base >=4.8 && <4.10+                     , memorable-bits+                     , tasty+                     , tasty-hunit+                     , tasty-quickcheck+                     , tasty-smallcheck+                     , doctest+                     , HUnit++benchmark bench-memorable-bits+  type:                exitcode-stdio-1.0+  main-is:             bench/Main.hs+  build-depends:       base >=4.8 && <4.10+                     , criterion+                     , memorable-bits+                     , random+                     , bytestring++executable membits+  main-is: app/Membits.hs+  default-language: Haskell2010+  build-depends: base >=4.8 && <4.10+               , memorable-bits+               , bytestring+               , cryptonite+               , optparse-applicative++  if !flag(exes) {+    buildable: False+  }
+ src/Data/Memorable.hs view
@@ -0,0 +1,31 @@+module Data.Memorable+    ( renderMemorable+    , parseMemorable+    , rerender+    , Memorable(..)+    -- * Pattern Building+    , (.-), (.|)+    , two, three, four, five+    , padHex, padDec+    , hex, hex4, hex8, hex16, hex32, dec, dec4, dec8, dec16, dec32+    , ToTree+    , leftSide+    , rightSide+    -- * Pattern Types+    , (:-)+    , MemRender()+    , Number+    , NumberWithOffset+    , PadTo+    , Dec+    , Hex+    , Depth+    , getDepth+    , LeftSide+    , RightSide+    -- * Re-export+    , Proxy(..)+    ) where++import Data.Memorable.Internal+import Data.Proxy
+ src/Data/Memorable/Internal.hs view
@@ -0,0 +1,891 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeOperators #-}+module Data.Memorable.Internal where++import Control.Arrow (first)+import Text.Printf+import Control.Applicative+import Control.Monad.Except+import Data.Maybe+import Data.List.Split+import Control.Monad.State+import Control.Monad.Writer+import Data.Hashable+import Data.Binary.Get+import Data.Binary.Put+import qualified Data.Binary+import Data.Bits+import Data.Bits.Coding hiding (putUnaligned)+import Data.Bytes.Put+import Data.Bytes.Get+import Data.Type.Equality+import Data.Type.Bool+import Data.ByteString.Lazy (ByteString, pack, unpack)+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString as B+import Data.List+import Data.Proxy+import Data.Word+import Data.Int+import GHC.TypeLits+import GHC.Exts+import Numeric+import System.Random (randomIO)+#ifdef DATA_DWORD+import Data.DoubleWord+#endif+#ifdef NETWORK_IP+import Network.IP.Addr+#endif+#ifdef CRYPTONITE+import Data.ByteArray (convert)+import Crypto.Hash hiding (hash)+#endif+#ifdef HASHABLE+import Data.Hashable+#endif++-- | Choice between two sub patterns. It's not safe to use this directly.+-- Use `.|` instead.+--+-- Also, if you are parsing back rendered phrases, you must make sure that+-- the selected word is enough to choose a side. That is, `a` and `b` must+-- have unique first words. This is NOT checked, as it causes a HUGE+-- compile-time performance hit. If we can make it performant it may be+-- checked one day.+data a :| b++-- | Append two patterns together by doing the first, then the second. See+-- also `.-`+data a :- b++-- | Proxy version of `:|`. It also constraints the two subpatterns to+-- being the same depth. Use this to add an extra bit to the pattern depth,+-- where the bit chooses to proceed down either the left or right side.+--+-- >>> :set -XTypeApplications+-- >>> :set -XDataKinds+-- >>> import Data.Word+-- >>> let myPattern = padHex (Proxy @"foo" .| Proxy @"bar")+-- >>> renderMemorable myPattern (0x00 :: Word8)+-- "bar-00"+-- >>> renderMemorable myPattern (0xff :: Word8)+-- "foo-7f"+--+-- See also 'ToTree'+--+-- WARNING: Each side of the split must be unique. See the warning about `:|`.+(.|) :: (Depth a ~ Depth b) => Proxy a -> Proxy b -> Proxy (a :| b)+_ .| _ = Proxy++-- | Proxy version of `:-`.+-- The new pattern depth is the sum of the two parts.+-- >>> import Data.Word+-- >>> import Data.Memorable.Theme.Words+-- >>> let myPattern = words8 .- words8+-- >>> renderMemorable myPattern (0xabcd :: Word16)+-- "ages-old"+(.-) :: Proxy a -> Proxy b -> Proxy (a :- b)+_ .- _ = Proxy++-- | Captures `n` bits and converts them to a string via the `nt` ("number type")+-- argument. See `Dec`, `Hex`.+type Number nt n = NumberWithOffset nt n 0++-- | Captures `n` bits and convertes them to a string via the `nt` ("number type")+-- argument after adding the offset. See `Dec`, `Hex`.+data NumberWithOffset nt (n :: Nat) (o :: Nat)++-- | Pad the `a` argument out to length `n` by taking the remaining bits+-- and converting them via `nt` (see `Dec` and `Hex`). If padding is required,+-- it is separated by a dash.+--+-- See `padHex` and `padDec` for convinence functions.+data PadTo nt (n :: Nat) a++---------------------------------------------------------------------+-- Utility type functions+---------------------------------------------------------------------++-- Helper for `ToTree`+type family ToTreeH (a :: [k]) :: [*] where+    ToTreeH '[] = '[]+    ToTreeH (x1 ': x2 ': x3 ': x4 ': x5 ': x6 ': x7 ': x8 ': x9 ': x10 ': x11 ': x12 ': x13 ': x14 ': x15 ': x16 ': x17 ': x18 ': x19 ': x20 ': x21 ': x22 ': x23 ': x24 ': x25 ': x26 ': x27 ': x28 ': x29 ': x30 ': x31 ': x32 ': x33 ': x34 ': x35 ': x36 ': x37 ': x38 ': x39 ': x40 ': x41 ': x42 ': x43 ': x44 ': x45 ': x46 ': x47 ': x48 ': x49 ': x50 ': x51 ': x52 ': x53 ': x54 ': x55 ': x56 ': x57 ': x58 ': x59 ': x60 ': x61 ': x62 ': x63 ': x64 ': xs) = ToTree64 (x1 ': x2 ': x3 ': x4 ': x5 ': x6 ': x7 ': x8 ': x9 ': x10 ': x11 ': x12 ': x13 ': x14 ': x15 ': x16 ': x17 ': x18 ': x19 ': x20 ': x21 ': x22 ': x23 ': x24 ': x25 ': x26 ': x27 ': x28 ': x29 ': x30 ': x31 ': x32 ': x33 ': x34 ': x35 ': x36 ': x37 ': x38 ': x39 ': x40 ': x41 ': x42 ': x43 ': x44 ': x45 ': x46 ': x47 ': x48 ': x49 ': x50 ': x51 ': x52 ': x53 ': x54 ': x55 ': x56 ': x57 ': x58 ': x59 ': x60 ': x61 ': x62 ': x63 ': x64 ': xs)+    ToTreeH as = ToTree2 as++type family ToTree2 (as :: [k]) :: [*] where    +    ToTree2 '[] = '[]+    ToTree2 (a ': b ': bs) = (a :| b) ': ToTree2 bs++type family ToTree64 (as :: [k]) :: [*] where    +    ToTree64 '[] = '[]+    ToTree64 (x1 ': x2 ': x3 ': x4 ': x5 ': x6 ': x7 ': x8 ': x9 ': x10 ': x11 ': x12 ': x13 ': x14 ': x15 ': x16 ': x17 ': x18 ': x19 ': x20 ': x21 ': x22 ': x23 ': x24 ': x25 ': x26 ': x27 ': x28 ': x29 ': x30 ': x31 ': x32 ': x33 ': x34 ': x35 ': x36 ': x37 ': x38 ': x39 ': x40 ': x41 ': x42 ': x43 ': x44 ': x45 ': x46 ': x47 ': x48 ': x49 ': x50 ': x51 ': x52 ': x53 ': x54 ': x55 ': x56 ': x57 ': x58 ': x59 ': x60 ': x61 ': x62 ': x63 ': x64 ': xs) =+        (+            (+                (+                    (+                        (+                            (x1 :| x2) :| (x3 :| x4)+                        ) :|+                        (+                            (x5 :| x6) :| (x7 :| x8)+                        )+                    ) :|+                    (+                        (+                            (x9 :| x10) :| (x11 :| x12)+                        ) :|+                        (+                            (x13 :| x14) :| (x15 :| x16)+                        )+                    )+                ) :|+                (+                    (+                        (+                            (x17 :| x18) :| (x19 :| x20)+                        ) :|+                        (+                            (x21 :| x22) :| (x23 :| x24)+                        )+                    ) :|+                    (+                        (+                            (x25 :| x26) :| (x27 :| x28)+                        ) :|+                        (+                            (x29 :| x30) :| (x31 :| x32)+                        )+                    )+                )+            )  :|+            (+                (+                    (+                        (+                            (x33 :| x34) :| (x35 :| x36)+                        ) :|+                        (+                            (x37 :| x38) :| (x39 :| x40)+                        )+                    ) :|+                    (+                        (+                            (x41 :| x42) :| (x43 :| x44)+                        ) :|+                        (+                            (x45 :| x46) :| (x47 :| x48)+                        )+                    )+                ) :|+                (+                    (+                        (+                            (x49 :| x50) :| (x51 :| x52)+                        ) :|+                        (+                            (x53 :| x54) :| (x55 :| x56)+                        )+                    ) :|+                    (+                        (+                            (x57 :| x58) :| (x59 :| x60)+                        ) :|+                        (+                            (x61 :| x62) :| (x63 :| x64)+                        )+                    )+                )+            ) +        ) ': ToTree64 xs++type family Len (a :: [Symbol]) :: Nat where+    Len (a ': b ': c ': d ': e ': f ': g ': h ': i ': j ': k ': l ': m ': n ': o ': p ': q ': r ': s ': t ': u ': v ': w ': x ': y ': z ': as) = Len as + 26+    Len (a ': as) = Len as + 1+    Len '[] = 0++-- | Convert a @'[Symbol]@ to a balanced tree of `:|`. Each result has equal+-- probability of occurring. Length of the list must be a power of two. This+-- is very useful for converting long lists of words into a usable pattern.+--+-- >>> :kind! ToTree '["a", "b", "c", "d"]+-- ToTree '["a", "b", "c", "d"] :: *+-- = ("a" :| "b") :| ("c" :| "d")+type family ToTree (a :: [k]) :: * where+    ToTree (x ': y ': '[] ) = x :| y+    ToTree '[(x :| y)] = x :| y+    ToTree xs = ToTree (ToTreeH xs)++type family Concat (a :: [k]) :: * where+    Concat (a ': b ': '[]) = a :- b+    Concat (a ': b ': cs) = a :- b :- Concat cs++type family Intersperse (a :: k) (b :: [k]) :: [k] where+    Intersperse a '[] = '[]+    Intersperse a (b ': '[]) = b ': '[]+    Intersperse a (b ': cs) = b ': a ': Intersperse a cs+++-- | Useful to prevent haddock from expanding the type.+type family LeftSide (a :: *) :: * where+    LeftSide (a :| b) = a++-- | Useful to prevent haddock from expanding the type.+type family RightSide (a :: *) :: * where+    RightSide (a :| b) = b++-- | Shrink a branching pattern by discarding the right hand side.+leftSide :: Proxy (a :| b) -> Proxy a+leftSide _ = Proxy++-- | Shrink a branching pattern by discarding the left hand side.+rightSide :: Proxy (a :| b) -> Proxy b+rightSide _ = Proxy+++type PowerOfTwo n = (IsPowerOfTwo n ~ True)++type family IsPowerOfTwo (a :: Nat) :: Bool where+    IsPowerOfTwo 1 = True+    IsPowerOfTwo 2 = True+    IsPowerOfTwo 4 = True+    IsPowerOfTwo 8 = True+    IsPowerOfTwo 16 = True+    IsPowerOfTwo 32 = True+    IsPowerOfTwo 64 = True+    IsPowerOfTwo 128 = True+    IsPowerOfTwo 256 = True+    IsPowerOfTwo 512 = True+    IsPowerOfTwo 1024 = True+    IsPowerOfTwo 2048 = True+    IsPowerOfTwo 4096 = True+    IsPowerOfTwo 8192 = True++type family BitsInPowerOfTwo (a :: Nat) :: Nat where+    BitsInPowerOfTwo 1 = 0+    BitsInPowerOfTwo 2 = 1+    BitsInPowerOfTwo 4 = 2+    BitsInPowerOfTwo 8 = 3+    BitsInPowerOfTwo 16 = 4+    BitsInPowerOfTwo 32 = 5+    BitsInPowerOfTwo 64 = 6+    BitsInPowerOfTwo 128 = 7+    BitsInPowerOfTwo 256 = 8+    BitsInPowerOfTwo 512 = 9+    BitsInPowerOfTwo 1024 = 10+    BitsInPowerOfTwo 2048 = 11+    BitsInPowerOfTwo 4096 = 12+    BitsInPowerOfTwo 8192 = 13++type family Find a as :: Bool where+    Find a '[] = 'False+    Find a (a ': as) = 'True+    Find a (b ': a ': as) = 'True+    Find a (c ': b ': a ': as) = 'True+    Find a (d ': c ': b ': a ': as) = 'True+    Find a (e ': d ': c ': b ': a ': as) = 'True+    Find a (f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (w ': v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (x ': w ': v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (y ': x ': w ': v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (z ': y ': x ': w ': v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': a ': as) = 'True+    Find a (z ': y ': x ': w ': v ': u ': t ': s ': r ': q ': p ': o ': n ': m ': l ': k ': j ': i ': h ': g ': f ': e ': d ': c ': b ': aa ': as) = Find a as+    Find a (b ': as ) = Find a as+++type family HasDups (a :: [Symbol]) :: Bool where+    HasDups (a ': as) = Find a as || HasDups as+    HasDups '[] = 'False++type family NoDups (a :: [Symbol]) :: Constraint where+    NoDups (a ': as) = If (Find a as) (TypeError (Text "Pattern is ambiguous because of " :<>: ShowType a)) (NoDups as)+    NoDups '[] = ()++-- | Determines the number of bits that a pattern will consume.+type family Depth (a :: k) :: Nat where+    Depth (a :: Symbol) = 0+    Depth (a :- b) = Depth a + Depth b+    Depth (a :| b) = 1 + Depth a+    Depth (NumberWithOffset nt a o) = a+    Depth (PadTo nt n a) = n++-- | Get the depth of a pattern as a value-level `Integer`.+-- >>> :set -XTypeApplications -XDataKinds+-- >>> getDepth (Proxy @"foo" .| Proxy @"bar")+-- 1+getDepth :: forall a. KnownNat (Depth a) => Proxy a -> Integer+getDepth _ = natVal (Proxy :: Proxy (Depth a))+++---------------------------------------------------------------------+-- Utility functions+---------------------------------------------------------------------++type family NTimes (n :: Nat) (p :: *) where+    NTimes 1 a = a+    NTimes n a = a :- NTimes (n - 1) a++-- | Put five things next to each other.+-- Same as using '.-' repeatedly+five :: Proxy a -> Proxy (a :- a :- a :- a :- a)+five _ = Proxy++-- | Put four things next to each other.+four :: Proxy a -> Proxy (a :- a :- a :- a)+four _ = Proxy++-- | Put three things next to each other.+three :: Proxy a -> Proxy (a :- a :- a)+three _ = Proxy++-- | Put two things next to each other.+two :: Proxy a -> Proxy (a :- a)+two _ = Proxy++-- | Pad this pattern out with hex digits. Useful when you want some human+-- readability, but also want full coverage of the data. See 'Hex' for details.+--+-- >>> import Data.Word+-- >>> import Data.Memorable.Theme.Fantasy+-- >>> renderMemorable (padHex rpgWeapons) (0xdeadbeef01020304 :: Word64)+-- "sacred-club-of-ghoul-charming-eef01020304"+--+-- The depth to pad to is the first type-level argument. If you have+-- `TypeApplications` set, you can use it like `padHex @256` to be+-- explicit.+padHex :: forall n a. Proxy a -> Proxy (PadTo Hex n a)+padHex _ = Proxy++-- | Pad with decimal digits. See 'padHex' and 'Dec' for details. This does+-- not pad with 0's+padDec :: forall n a. Proxy a -> Proxy (PadTo Dec n a)+padDec _ = Proxy++-- | A single hex number consuming 4 bits (with leading 0's).+hex4 :: Proxy (Number Hex 4)+hex4 = Proxy++-- | A single hex number consuming 8 bits (with leading 0's).+hex8 :: Proxy (Number Hex 8)+hex8 = Proxy++-- | A single hex number consuming 16 bits (with leading 0's).+hex16 :: Proxy (Number Hex 16)+hex16 = Proxy++-- | A single hex number consuming 32 bits (with leading 0's).+hex32 :: Proxy (Number Hex 32)+hex32 = Proxy++-- | A single hex number consuming `n` bits, which it will try and figure+-- out from context (with leading 0's). Using `TypeApplications` allows you+-- to specify the size directly, `hex @32 == hex32`.+hex :: Proxy (Number Hex n)+hex = Proxy++-- | A single decimal number consuming 4 bits (no leading 0's)+dec4 :: Proxy (Number Dec 4)+dec4 = Proxy++-- | A single decimal number consuming 8 bits (no leading 0's)+dec8 :: Proxy (Number Dec 8)+dec8 = Proxy++-- | A single decimal number consuming 16 bits (no leading 0's)+dec16 :: Proxy (Number Dec 16)+dec16 = Proxy++-- | A single decimal number consuming 32 bits (no leading 0's)+dec32 :: Proxy (Number Dec 32)+dec32 = Proxy++-- | A single decimal number consuming `n` bits, which it will try and figure+-- out from context (no leading 0's). Using `TypeApplications` allows you+-- to specify the size directly, `dec @32 == hex32`.+dec :: Proxy (Number Dec n)+dec = Proxy++---------------------------------------------------------------------+-- MemRender+---------------------------------------------------------------------++-- | The class that implements the main rendering function.+class MemRender a where+    render :: Proxy a -> Coding Get String+    parser :: Proxy a -> ExceptT String (State ([String], Coding PutM ())) ()++addBits :: Coding PutM () -> ExceptT String (State ([String], Coding PutM ())) ()+addBits c = do+    (s,cs) <- get+    put (s,cs >> c)++symbolString :: KnownSymbol a => Proxy a -> String+symbolString = concatMap tr . symbolVal+    where+        tr '-' = "\\_"+        tr '\\' = "\\\\"+        tr c = [c]++stringSymbol :: String -> String+stringSymbol [] = []+stringSymbol ('\\':'\\':rest) = '\\' : stringSymbol rest+stringSymbol ('\\':'_':rest) = '-' : stringSymbol rest+stringSymbol (a:rest) = a : stringSymbol rest++parsePhrase :: MemRender p => Proxy p -> String -> Maybe ByteString+parsePhrase p input =+    let+        tokens = map stringSymbol $ splitOn "-" input+        stm = runExceptT (parser p)+        (e,(_,cdm)) = runState stm (tokens, pure ())+        ptm = runCoding (cdm <* Data.Bytes.Put.flush) (\a _ _ -> pure a) 0 0+    in+        case e of+            Left _ -> Nothing+            Right () -> Just $ runPutL ptm++-- | Turn a memorable string back into a 'Memorable' value.+parseMemorable :: (Memorable a, MemRender p, MemLen a ~ Depth p) => Proxy p -> String -> Maybe a+parseMemorable p input =+    let+        bs = parsePhrase p input+    in runParser <$> bs++-- | Convert a memorable string into a different memorable string.+--+-- Useful for things like taking an existing md5, and converting it+-- into a memorable one.+--+-- >>> :set -XTypeApplications -XDataKinds+-- >>> import Data.Memorable.Theme.Words+-- >>> rerender hex (padHex @128 $ four words10) "2d4fbe4d5db8748c931b85c551d03360"+-- Just "lurk-lash-atop-hole-b8748c931b85c551d03360"+rerender :: (MemRender a, MemRender b, Depth a ~ Depth b) => Proxy a -> Proxy b -> String -> Maybe String+rerender a b input = renderMemorableByteString b <$> parsePhrase a input++instance (KnownSymbol a) => MemRender (a :: Symbol) where+    render = return . symbolString+    parser p = do+        (ss,cs) <- get+        case ss of+            [] -> empty+            s:ss' ->+                if s == symbolVal p+                    then put (ss',cs)+                    else empty+        ++instance (MemRender a, MemRender b) => MemRender (a :- b) where+    render _ = do+        sa <- render (Proxy :: Proxy a)+        sb <- render (Proxy :: Proxy b)+        return $ sa ++ "-" ++ sb+    parser _ = do+        parser (Proxy :: Proxy a)+        parser (Proxy :: Proxy b)+        ++instance (MemRender a, MemRender b) => MemRender (a :| b) where+    render _ = do+        b <- getBit+        if b+            then render (Proxy :: Proxy a)+            else render (Proxy :: Proxy b)+    parser _ = do+        s <- get+        catchError (do+            addBits (putBit True)+            parser (Proxy :: Proxy a) +            ) (\_ -> do+            put s+            addBits (putBit False)+            parser (Proxy :: Proxy b)+            )++instance (NumberRender nt, KnownNat a, KnownNat o) => MemRender (NumberWithOffset nt a o) where+    render _ = do+        let+            o = natVal (Proxy :: Proxy o)+            b = natVal (Proxy :: Proxy a)+        w <- getBitsFrom (fromIntegral (pred b)) 0+        return $ renderNumber (Proxy :: Proxy nt) b (w + o)++    parser _ = do+        let+            o = natVal (Proxy :: Proxy o)+            b = natVal (Proxy :: Proxy a)+        (ss,cs) <- get+        case ss of+            [] -> empty+            (s:ss') -> do+                let+                    n = readNumber (Proxy :: Proxy nt) b s+                case n of+                    Nothing -> empty+                    Just n' -> do+                        let n'' = n' - o+                        when (n'' >= 2^b) empty+                        put (ss',cs >> putBitsFrom (fromIntegral $ pred b) n'')+                ++instance (MemRender a, Depth a <= n, NumberRender nt, KnownNat n, KnownNat (Depth a)) => MemRender (PadTo nt n a) where+    render _ = do+        s1 <- render (Proxy :: Proxy a)+        let+            diff = natVal (Proxy :: Proxy n) - natVal (Proxy :: Proxy (Depth a))+            ntp = Proxy :: Proxy nt+        case diff of+            0 -> return s1+            _ -> do+                d <- getBitsFrom (fromIntegral (pred diff)) 0+                return $ s1 ++ "-" ++ renderNumber ntp diff d++    parser _ = do+        let+            nt = Proxy :: Proxy nt+            diff = natVal (Proxy :: Proxy n) - natVal (Proxy :: Proxy (Depth a))+        parser (Proxy :: Proxy a)+        case diff of+            0 -> return ()+            _ -> do+                (ss,cs) <- get+                when (null ss) empty+                let+                    (s:ss') = ss+                    n = readNumber nt diff s+                n' <- maybe empty return n+                when (n' >= 2^diff) empty+                put (ss', cs >> putBitsFrom (fromIntegral $ pred diff) n')++---------------------------------------------------------------------+-- NumberRender+---------------------------------------------------------------------++-- | Class for capturing how to render numbers.+class NumberRender n where+    renderNumber :: Proxy n -> Integer -> Integer -> String+    readNumber :: Proxy n -> Integer -> String -> Maybe Integer+++-- | Render numbers as decimal numbers. Does not pad.+data Dec++instance NumberRender Dec where+    renderNumber _ _ = show+    readNumber _ _ input = case readDec input of+        [(v,"")] -> Just v+        _ -> Nothing+++-- | Render numbers as hexadecimal numbers. Pads with 0s.+data Hex++instance NumberRender Hex where+    renderNumber _ b = printf "%0*x" hexDigits+        where+            hexDigits = (b - 1) `div` 4 + 1+    readNumber _ _ input = case readHex input of+        [(v,"")] -> Just v+        _ -> Nothing++---------------------------------------------------------------------+-- Rendering functions for users+---------------------------------------------------------------------++-- | Class for all things that can be converted to memorable strings.+-- See `renderMemorable` for how to use.+class Memorable a where+    -- Do not lie. Use @`testMemLen`@.+    type MemLen a :: Nat+    renderMem :: MonadPut m => a -> Coding m ()+    parserMem :: MonadGet m => Coding m a++memBitSize :: forall a. (KnownNat (MemLen a)) => Proxy a -> Int+memBitSize _ = fromIntegral $ natVal (Proxy :: Proxy (MemLen a))++-- | Use this with tasty-quickcheck (or your prefered testing framework) to+-- make sure you aren't lying about `MemLen`.+-- +-- @+-- testProperty "MemLen Word8" $ forAll (arbitrary :: Gen Word8) `testMemLen`+-- @+testMemLen :: forall a. (KnownNat (MemLen a), Memorable a) => a -> Bool+testMemLen a =+    let+        p :: Coding PutM ()+        p = renderMem a+        (x,bs) = runPutM (runCoding p (\a x _ -> return x) 0 0)+        l = fromIntegral $ natVal (Proxy :: Proxy (MemLen a))+        bl = 8 * fromIntegral (BL.length bs) - x+    in+        l == bl++putUnaligned :: (MonadPut m, FiniteBits b) => b -> Coding m ()+putUnaligned b = putBitsFrom (pred $ finiteBitSize b) b++instance Memorable Word8 where+    type MemLen Word8 = 8+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Word8)) 0++instance Memorable Word16 where+    type MemLen Word16 = 16+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Word16)) 0++instance Memorable Word32 where+    type MemLen Word32 = 32+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Word32)) 0++instance Memorable Word64 where+    type MemLen Word64 = 64+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Word64)) 0++instance Memorable Int8 where+    type MemLen Int8 = 8+    renderMem =  putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Int8)) 0++instance Memorable Int16 where+    type MemLen Int16 = 16+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Int16)) 0++instance Memorable Int32 where+    type MemLen Int32 = 32+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Int32)) 0++instance Memorable Int64 where+    type MemLen Int64 = 64+    renderMem = putUnaligned+    parserMem = getBitsFrom (pred $ memBitSize (Proxy :: Proxy Int64)) 0++instance (Memorable a, Memorable b) => Memorable (a,b) where+    type MemLen (a,b) = MemLen a + MemLen b+    renderMem (a,b) = renderMem a >> renderMem b+    parserMem = (,) <$> parserMem <*> parserMem++instance (Memorable a, Memorable b, Memorable c) => Memorable (a,b,c) where+    type MemLen (a,b,c) = MemLen a + MemLen b + MemLen c+    renderMem (a,b,c) = renderMem a >> renderMem b >> renderMem c+    parserMem = (,,) <$> parserMem <*> parserMem <*> parserMem+++instance (Memorable a, Memorable b, Memorable c, Memorable d) => Memorable (a,b,c,d) where+    type MemLen (a,b,c,d) = MemLen a + MemLen b + MemLen c + MemLen d+    renderMem (a,b,c,d) = renderMem a >> renderMem b >> renderMem c >> renderMem d+    parserMem = (,,,) <$> parserMem <*> parserMem <*> parserMem <*> parserMem++instance (Memorable a, Memorable b, Memorable c, Memorable d, Memorable e) => Memorable (a,b,c,d,e) where+    type MemLen (a,b,c,d,e) = MemLen a + MemLen b + MemLen c + MemLen d + MemLen e+    renderMem (a,b,c,d,e) = renderMem a >> renderMem b >> renderMem c >> renderMem d >> renderMem e+    parserMem = (,,,,) <$> parserMem <*> parserMem <*> parserMem <*> parserMem <*> parserMem++#ifdef DATA_DWORD+instance Memorable Word96 where+    type MemLen Word96 = 96+    renderMem (Word96 h l) = renderMem h >> renderMem l+    parserMem = Word96 <$> parserMem <*> parserMem++instance Memorable Word128 where+    type MemLen Word128 = 128+    renderMem (Word128 h l) = renderMem h >> renderMem l+    parserMem = Word128 <$> parserMem <*> parserMem++instance Memorable Word160 where+    type MemLen Word160 = 160+    renderMem (Word160 h l) = renderMem h >> renderMem l+    parserMem = Word160 <$> parserMem <*> parserMem++instance Memorable Word192 where+    type MemLen Word192 = 192+    renderMem (Word192 h l) = renderMem h >> renderMem l+    parserMem = Word192 <$> parserMem <*> parserMem++instance Memorable Word224 where+    type MemLen Word224 = 224+    renderMem (Word224 h l) = renderMem h >> renderMem l+    parserMem = Word224 <$> parserMem <*> parserMem++instance Memorable Word256 where+    type MemLen Word256 = 256+    renderMem (Word256 h l) = renderMem h >> renderMem l+    parserMem = Word256 <$> parserMem <*> parserMem++instance Memorable Int96 where+    type MemLen Int96 = 96+    renderMem (Int96 h l) = renderMem h >> renderMem l+    parserMem = Int96 <$> parserMem <*> parserMem++instance Memorable Int128 where+    type MemLen Int128 = 128+    renderMem (Int128 h l) = renderMem h >> renderMem l+    parserMem = Int128 <$> parserMem <*> parserMem++instance Memorable Int160 where+    type MemLen Int160 = 160+    renderMem (Int160 h l) = renderMem h >> renderMem l+    parserMem = Int160 <$> parserMem <*> parserMem++instance Memorable Int192 where+    type MemLen Int192 = 192+    renderMem (Int192 h l) = renderMem h >> renderMem l+    parserMem = Int192 <$> parserMem <*> parserMem++instance Memorable Int224 where+    type MemLen Int224 = 224+    renderMem (Int224 h l) = renderMem h >> renderMem l+    parserMem = Int224 <$> parserMem <*> parserMem++instance Memorable Int256 where+    type MemLen Int256 = 256+    renderMem (Int256 h l) = renderMem h >> renderMem l+    parserMem = Int256 <$> parserMem <*> parserMem+#endif++#ifdef NETWORK_IP+-- | >>> renderMemorable threeWordsFor32Bits (ip4FromOctets 127 0 0 1)+-- "shore-pick-pets"+instance Memorable IP4 where+    type MemLen IP4 = 32+    renderMem (IP4 w) = renderMem w+    parserMem = IP4 <$> parserMem++instance Memorable IP6 where+    type MemLen IP6 = 128+    renderMem (IP6 w) = renderMem w+    parserMem = IP6 <$> parserMem+#endif++#ifdef CRYPTONITE+#define DIGEST_INST(NAME,BITS) \+instance Memorable (Digest NAME) where {\+    type MemLen (Digest NAME) = BITS; \+    renderMem = mapM_ putUnaligned . B.unpack . convert; \+    parserMem = do { \+        let {b = (BITS) `div` 8;}; \+        Just md <- (digestFromByteString . B.pack) <$> replicateM b (getBitsFrom 7 0); \+        return md;}}++DIGEST_INST(Whirlpool,512)+DIGEST_INST(Blake2s_224,224)+DIGEST_INST(Blake2s_256,256)+DIGEST_INST(Blake2sp_224,224)+DIGEST_INST(Blake2sp_256,256)+DIGEST_INST(Blake2b_512,512)+DIGEST_INST(Blake2bp_512,512)+DIGEST_INST(Tiger,192)+DIGEST_INST(Skein512_512,512)+DIGEST_INST(Skein512_384,384)+DIGEST_INST(Skein512_256,256)+DIGEST_INST(Skein512_224,224)+DIGEST_INST(Skein256_224,224)+DIGEST_INST(Skein256_256,256)+DIGEST_INST(SHA512t_256,256)+DIGEST_INST(SHA512t_224,224)+DIGEST_INST(SHA512,512)+DIGEST_INST(SHA384,384)+DIGEST_INST(SHA3_512,512)+DIGEST_INST(SHA3_384,384)+DIGEST_INST(SHA3_256,256)+DIGEST_INST(SHA3_224,224)+DIGEST_INST(SHA256,256)+DIGEST_INST(SHA224,224)+DIGEST_INST(SHA1,160)+DIGEST_INST(RIPEMD160,160)+-- | +-- >>> :set -XOverloadedStrings+-- >>> import Data.ByteString+-- >>> import Crypto.Hash+-- >>> let myPattern = padHex (four flw10)+-- >>> renderMemorable myPattern (hash ("anExample" :: ByteString) :: Digest MD5)+-- "bark-most-gush-tuft-1b7155ab0dce3ecb4195fc"+DIGEST_INST(MD5,128)+DIGEST_INST(MD4,128)+DIGEST_INST(MD2,128)+DIGEST_INST(Keccak_512,512)+DIGEST_INST(Keccak_384,384)+DIGEST_INST(Keccak_256,256)+DIGEST_INST(Keccak_224,224)+#undef DIGEST_INST+#endif++-- | This is the function to use when you want to turn your values into a+-- memorable strings.+--+-- >>> import Data.Word+-- >>> import Data.Memorable.Theme.Words+-- >>> let myPattern = words8 .- words8+-- >>> renderMemorable myPattern (0x0123 :: Word16)+-- "cats-bulk"+renderMemorable :: (MemRender p, Depth p ~ MemLen a, Memorable a) => Proxy p -> a -> String+renderMemorable p a = renderMemorableByteString p (runRender a)++runRender :: Memorable a => a -> ByteString+runRender c = runPutL (runCoding (renderMem c) (\_ _ _ -> pure ()) 0 0)++runParser :: Memorable a => ByteString -> a+runParser = runGet (runCoding parserMem (\a _ _ -> pure a) 0 0)++-- | Render a `ByteString` as a more memorable `String`.+renderMemorableByteString+    :: MemRender a+    => Proxy a -> ByteString -> String+renderMemorableByteString p =+    runGetL (runCoding (render p) (\a _ _ -> return a) 0 0)+    --runGet (runBitGet . flip evalStateT 0 . unBitPull $ render p)++-- | Generate a random string.+renderRandom+    :: forall a. (MemRender a, KnownNat (Depth a))+    => Proxy a -> IO String+renderRandom p = do+    let+        nBits = getDepth p+        nBytes = fromIntegral $ nBits `div` 8 + 1+    bs <- pack <$> replicateM nBytes randomIO+    return $ renderMemorableByteString p bs+++-- | Render any `Hashable` value as a 32 bit pattern.+renderHashable32 :: (MemRender p, Depth p ~ 32, Hashable a) => Proxy p -> a -> String+renderHashable32 p a = renderMemorable p (fromIntegral $ hash a :: Word32)++-- | Render any `Hashable` value as a 16 bit pattern.+renderHashable16 :: (MemRender p, Depth p ~ 16, Hashable a) => Proxy p -> a -> String+renderHashable16 p a = renderMemorable p (fromIntegral $ hash a :: Word16)++-- | Render any `Hashable` value as a 8 bit pattern.+renderHashable8 :: (MemRender p, Depth p ~ 8, Hashable a) => Proxy p -> a -> String+renderHashable8 p a = renderMemorable p (fromIntegral $ hash a :: Word8)
+ src/Data/Memorable/Theme/Fantasy.hs view
@@ -0,0 +1,287 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+module Data.Memorable.Theme.Fantasy where++import Data.Memorable++type TransitiveVerb = ToTree+    '[ "blending"+     , "breaking"+     , "bullying"+     , "burning"+     , "charming"+     , "dominating"+     , "fighting"+     , "freezing"+     , "growing"+     , "kicking"+     , "killing"+     , "liquefying"+     , "loving"+     , "melting"+     , "paralyzing"+     , "peeling"+     , "petrifying"+     , "piercing"+     , "pinching"+     , "poking"+     , "punching"+     , "singeing"+     , "slicing"+     , "splattering"+     , "squishing"+     , "tearing"+     , "teasing"+     , "throwing"+     , "tormenting"+     , "tossing"+     , "wooing"+     , "zapping"+     ]++type Weapons = ToTree+    '[ "axe"+     , "club"+     , "dagger"+     , "dart"+     , "flail"+     , "halberd"+     , "hammer"+     , "javelin"+     , "lance"+     , "mace"+     , "pick"+     , "sling"+     , "spear"+     , "staff"+     , "sword"+     , "trident"+     ]++type Armour = ToTree+    '[ "helm"+     , "boots"+     , "vest"+     , "gauntlets"+     , "great helm"+     , "cuirasse"+     , "shield"+     , "pants"+     , "shirt"+     , "greaves"+     , "hat"+     , "leggings"+     , "robes"+     , "sandals"+     , "amulet"+     , "ring"+     ]++type Aura = ToTree+    '[ "unholy"+     , "holy"+     , "sacred"+     , "vile"+     , "shimmering"+     , "glowing"+     , "sparkling"+     , "demonic"+     , "angelic"+     , "cold"+     , "terrifying"+     , "ancient"+     , "mouldy"+     , "elvish"+     , "apocryphal"+     , "ghostly"+     ]++type Monster = ToTree+    '[ "batrachian"+     , "afreet"+     , "ankheg"+     , "annis"+     , "assagim"+     , "babau"+     , "babbler"+     , "banshee"+     , "barghest"+     , "basilisk"+     , "behir"+     , "blindheim"+     , "blinkdog"+     , "brownie"+     , "bugbear"+     , "bulette"+     , "carbuncle"+     , "caterwaul"+     , "centaur"+     , "chimaera"+     , "cockatrice"+     , "crabman"+     , "cyclops"+     , "dakon"+     , "demoniac"+     , "devilcat"+     , "disenchanter"+     , "doppelganger"+     , "dracolisk"+     , "dragon"+     , "dretch"+     , "dryad"+     , "elemental"+     , "erinyes"+     , "ettercap"+     , "ettin"+     , "faun"+     , "gargoyle"+     , "genie"+     , "ghast"+     , "ghost"+     , "ghoul"+     , "giant"+     , "gnoll"+     , "goblin"+     , "golem"+     , "gorgon"+     , "griffon"+     , "grimlock"+     , "harpy"+     , "hellhound"+     , "hippogriff"+     , "hobgoblin"+     , "homonculus"+     , "hydra"+     , "imp"+     , "jackalwere"+     , "kobold"+     , "kraken"+     , "lemure"+     , "leprechaun"+     , "lich"+     , "locathah"+     , "manticore"+     , "medusa"+     , "mephit"+     , "merman"+     , "minotaur"+     , "mongrelman"+     , "mould"+     , "mummy"+     , "naga"+     , "necrophidius"+     , "nereid"+     , "nightmare"+     , "nilbog"+     , "nixie"+     , "nymph"+     , "ogre"+     , "orc"+     , "otyugh"+     , "owlbear"+     , "pegasus"+     , "phantom"+     , "phoenix"+     , "piercer"+     , "pixie"+     , "poltergeist"+     , "pseudodragon"+     , "quasit"+     , "quickling"+     , "rakshasa"+     , "remorhaz"+     , "roc"+     , "roper"+     , "sahuagin"+     , "shaitan"+     , "shedu"+     , "shrieker"+     , "shub"+     , "skeleton"+     , "spectre"+     , "sphinx"+     , "sprite"+     , "squealer"+     , "stirge"+     , "stunjelly"+     , "succubus"+     , "sylph"+     , "titan"+     , "treant"+     , "triton"+     , "troglodyte"+     , "troll"+     , "uduk"+     , "unicorn"+     , "vampire"+     , "vilstrak"+     , "volt"+     , "vulchling"+     , "wererat"+     , "werewolf"+     , "wight"+     , "wraith"+     , "wyvern"+     , "xorn"+     , "yeti"+     , "zombie"+     ]++type ArmourMaterial = ToTree+    '[ "bronze"+     , "cloth"+     , "golden"+     , "iron"+     , "leather"+     , "silk"+     , "studded"+     , "wooden"+     , "crystal"+     , "mythril"+     , "bone"+     , "stone"+     , "silver"+     , "glass"+     , "paper"+     , "fur"+     ]++type Buff = ToTree+    '[ "accuracy"+     , "climbing"+     , "constitution"+     , "dexterity"+     , "fear"+     , "fortitude"+     , "hope"+     , "invisibility"+     , "paralysis"+     , "protection"+     , "speed"+     , "swimming"+     , "telekinesis"+     , "warmth"+     , "wisdom"+     , "wizardry"+     ]++type RpgWeapons = Weapons :- "of" :- Monster :- TransitiveVerb++type RpgArmour = ArmourMaterial :- Armour :- "of" :- Buff++{-+rpgThings = aura .- (w .| a)+    where+        w = Proxy :: Proxy RpgWeapons+        a = Proxy :: Proxy RpgArmour+        aura = Proxy :: Proxy Aura+-}++rpgWeapons :: Proxy (Aura :- RpgWeapons)+rpgWeapons = Proxy++rpgArmour :: Proxy (Aura :- RpgArmour)+rpgArmour = Proxy+
+ src/Data/Memorable/Theme/Food.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+module Data.Memorable.Theme.Food where++import Data.Memorable++type DessertFlavours = ToTree+    '[ "strawberry"+     , "chocolate"+     , "vanilla"+     , "blueberry"+     , "raspberry"+     , "apple"+     , "almond"+     , "cherry"+     ]++type DessertTypes = ToTree+    '[ "custard"+     , "parfait"+     , "souffle"+     , "pancakes"+     , "icecream"+     , "tart"+     , "pie"+     , "cupcakes"+     ]++type FoodAdjectives = ToTree+    '[ "delicious"+     , "rancid"+     , "heavenly"+     , "scrumptious"+     , "delightful"+     , "disgusting"+     , "foul"+     , "exotic"+     , "bland"+     , "gourmet"+     , "tasty"+     , "tasteless"+     , "refreshing"+     , "sensational"+     , "crunchy"+     , "creamy"+     ]++type Desserts+    = FoodAdjectives :- DessertFlavours :- DessertTypes++desserts :: Proxy Desserts+desserts = Proxy+
+ src/Data/Memorable/Theme/Metric.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+module Data.Memorable.Theme.Metric where++import Data.Memorable++type UnitsPlural = ToTree+    '[ "meters"+     , "seconds"+     , "grams"+     , "liters"+     , "volts"+     , "ohms"+     , "amps"+     , "henries"+     ]++type Units = ToTree+    '[ "meter"+     , "second"+     , "gram"+     , "liter"+     , "volt"+     , "ohm"+     , "amp"+     , "henry"+     ]++type SIPrefix = ToTree+    '[ "atto"+     , "femto"+     , "pico"+     , "nano"+     , "micro"+     , "milli"+     , "centi"+     , "deci"+     , "deca"+     , "hecto"+     , "kilo"+     , "mega"+     , "giga"+     , "tera"+     , "peta"+     , "exa"+     ]++type Measurements+    = Number Dec 5+    :- "."+    :- Number Dec 3+    :- " "+    :- SIPrefix+    :- UnitsPlural+    :- " per "+    :- SIPrefix+    :- Units+    :- "^"+    :- NumberWithOffset Dec 2 2++measurements :: Proxy Measurements+measurements = Proxy
+ src/Data/Memorable/Theme/Science.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+module Data.Memorable.Theme.Science where++import Data.Memorable+++type NumericPrefix = ToTree '["di", "tri", "4", "5", "9", "mono", "8", "hex"]+type ChemPrefix = ToTree '["propyl", "ethyl", "pyridine", "chloro", "methyl", "benzene", "hydro", "ferro"]+type ChemSuffix = ToTree '["oxide", "carbide", "sulfide", "fluoride"]+type ChemBabble = NumericPrefix :- ChemPrefix :- NumericPrefix :- ChemPrefix :- ChemSuffix++chemBabble :: Proxy ChemBabble+chemBabble = Proxy+
+ src/Data/Memorable/Theme/Words.hs view
@@ -0,0 +1,5233 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PolyKinds #-}++-- | Words is special because GHC can't handle this many Symbols.+-- The `Choose` trick helps, but only to about 1024 words, after+-- which the compiler fails.+module Data.Memorable.Theme.Words where++import Data.Memorable+++type Words12 = Words+type Words11 = LeftSide Words12+type Words10 = LeftSide Words11+type Words9 = LeftSide Words10+type Words8 = LeftSide Words9+type Words7 = LeftSide Words8+type Words6 = LeftSide Words7+type Words5 = LeftSide Words6+type Words4 = LeftSide Words5++-- | A collection of words+words12 :: Proxy Words12+words12 = Proxy++words11 :: Proxy Words11+words11 = leftSide words12++words10 :: Proxy Words10+words10 = leftSide words11++words9 :: Proxy Words9+words9 = leftSide words10++words8 :: Proxy Words8+words8 = leftSide words9++words7 :: Proxy Words7+words7 = leftSide words8++words6 :: Proxy Words6+words6 = leftSide words7++words5 :: Proxy Words5+words5 = leftSide words6++words4 :: Proxy Words4+words4 = leftSide words5++-- | Three words made to fit into 32 bits+threeWordsFor32Bits :: Proxy (Words12 :- Words10 :- Words10)+threeWordsFor32Bits = Proxy++-- | Six words made to fit into 64 bits+sixWordsFor64Bits :: Proxy (Words12 :- Words12 :- Words10 :- Words9 :- Words9 :- Words12)+sixWordsFor64Bits = Proxy++type FLW10 = FourLetterWords+type FLW9 = LeftSide FLW10+type FLW8 = LeftSide FLW9+type FLW7 = LeftSide FLW8+type FLW6 = LeftSide FLW7+type FLW5 = LeftSide FLW6+type FLW4 = LeftSide FLW5++-- | Four letter words (useful for even lengths)+flw10 :: Proxy FLW10+flw10 = Proxy++flw9 :: Proxy FLW9+flw9 = Proxy ++flw8 :: Proxy FLW8+flw8 = leftSide flw9++flw7 :: Proxy FLW7+flw7 = leftSide flw8++flw6 :: Proxy FLW6+flw6 = leftSide flw7++flw5 :: Proxy FLW5+flw5 = leftSide flw6++flw4 :: Proxy FLW4+flw4 = leftSide flw5++-- | Four equal length words made to fit 32 bits+fourEqualWordsFor32Bits :: Proxy (FLW8 :- FLW8 :- FLW8 :- FLW8)+fourEqualWordsFor32Bits = Proxy++-- | Seven equal length words for 64 bits+sevenEqualWordsFor64Bits :: Proxy (FLW10 :- FLW9 :- FLW9 :- FLW9 :- FLW9 :- FLW9 :- FLW9)+sevenEqualWordsFor64Bits = Proxy++-- | Eight equal length words made to fit 64 bits+eightEqualWordsFor64Bits :: Proxy (FLW8 :- FLW8 :- FLW8 :- FLW8 :- FLW8 :- FLW8 :- FLW8 :- FLW8)+eightEqualWordsFor64Bits = Proxy++-- Sorted by word length so that when you `leftSide` it, you keep only the+-- smaller words.+type Words = ToTree+    '[ "act"+     , "ads"+     , "ago"+     , "air"+     , "all"+     , "and"+     , "any"+     , "art"+     , "ash"+     , "ass"+     , "bad"+     , "bag"+     , "big"+     , "bit"+     , "bow"+     , "box"+     , "bus"+     , "but"+     , "cry"+     , "dry"+     , "far"+     , "fat"+     , "few"+     , "fix"+     , "fly"+     , "fog"+     , "for"+     , "fun"+     , "gas"+     , "hey"+     , "him"+     , "his"+     , "hot"+     , "how"+     , "ice"+     , "ill"+     , "its"+     , "joy"+     , "let"+     , "lit"+     , "low"+     , "mad"+     , "man"+     , "may"+     , "mix"+     , "mud"+     , "nor"+     , "not"+     , "now"+     , "off"+     , "old"+     , "one"+     , "out"+     , "per"+     , "raw"+     , "red"+     , "sad"+     , "say"+     , "see"+     , "sex"+     , "she"+     , "shy"+     , "sir"+     , "six"+     , "sky"+     , "spy"+     , "tax"+     , "the"+     , "too"+     , "try"+     , "via"+     , "who"+     , "why"+     , "yes"+     , "yet"+     , "you"+     , "able"+     , "ache"+     , "acid"+     , "acne"+     , "acre"+     , "acts"+     , "adds"+     , "afar"+     , "ages"+     , "ahoy"+     , "aide"+     , "aids"+     , "ails"+     , "aims"+     , "ajar"+     , "akin"+     , "alas"+     , "ally"+     , "also"+     , "amid"+     , "anew"+     , "anti"+     , "apex"+     , "aqua"+     , "arch"+     , "area"+     , "aria"+     , "arms"+     , "army"+     , "arts"+     , "asks"+     , "atom"+     , "atop"+     , "aunt"+     , "aura"+     , "auto"+     , "avid"+     , "avow"+     , "away"+     , "awry"+     , "axis"+     , "axle"+     , "babe"+     , "baby"+     , "back"+     , "bade"+     , "bags"+     , "bail"+     , "bait"+     , "bake"+     , "bald"+     , "bale"+     , "balk"+     , "ball"+     , "balm"+     , "band"+     , "bane"+     , "bang"+     , "bank"+     , "bans"+     , "barb"+     , "bard"+     , "bare"+     , "bark"+     , "barn"+     , "bars"+     , "base"+     , "bash"+     , "bask"+     , "bass"+     , "bath"+     , "bats"+     , "bawl"+     , "bays"+     , "bead"+     , "beak"+     , "beam"+     , "bean"+     , "bear"+     , "beat"+     , "beds"+     , "beef"+     , "been"+     , "beep"+     , "beer"+     , "bees"+     , "begs"+     , "bell"+     , "belt"+     , "bend"+     , "bent"+     , "best"+     , "beta"+     , "bets"+     , "bias"+     , "bids"+     , "bike"+     , "bile"+     , "bill"+     , "bind"+     , "bird"+     , "bite"+     , "bled"+     , "blip"+     , "blob"+     , "blog"+     , "blot"+     , "blow"+     , "blue"+     , "blur"+     , "boar"+     , "boat"+     , "bode"+     , "body"+     , "boil"+     , "bold"+     , "bolt"+     , "bomb"+     , "bond"+     , "bone"+     , "bong"+     , "bonk"+     , "bony"+     , "book"+     , "boom"+     , "boon"+     , "boot"+     , "bore"+     , "born"+     , "boss"+     , "both"+     , "bout"+     , "bowl"+     , "boys"+     , "brag"+     , "bran"+     , "brat"+     , "bred"+     , "brew"+     , "brim"+     , "buck"+     , "buff"+     , "bugs"+     , "bulb"+     , "bulk"+     , "bull"+     , "bump"+     , "bunk"+     , "buoy"+     , "burn"+     , "burp"+     , "bury"+     , "bush"+     , "busk"+     , "bust"+     , "busy"+     , "butt"+     , "buys"+     , "byte"+     , "cabs"+     , "cage"+     , "cake"+     , "call"+     , "calm"+     , "came"+     , "camp"+     , "cane"+     , "cans"+     , "cape"+     , "caps"+     , "card"+     , "care"+     , "cars"+     , "cart"+     , "case"+     , "cash"+     , "cask"+     , "cast"+     , "cats"+     , "cave"+     , "cede"+     , "cell"+     , "cent"+     , "chap"+     , "chat"+     , "chef"+     , "chew"+     , "chin"+     , "chip"+     , "chop"+     , "chug"+     , "cite"+     , "city"+     , "clad"+     , "clam"+     , "clan"+     , "clap"+     , "claw"+     , "clay"+     , "clip"+     , "clog"+     , "clop"+     , "clot"+     , "club"+     , "clue"+     , "coal"+     , "coat"+     , "code"+     , "coil"+     , "coin"+     , "cold"+     , "colt"+     , "coma"+     , "comb"+     , "come"+     , "cone"+     , "cook"+     , "cool"+     , "cope"+     , "cops"+     , "copy"+     , "cord"+     , "core"+     , "cork"+     , "corn"+     , "cost"+     , "cosy"+     , "coup"+     , "cove"+     , "cowl"+     , "cows"+     , "cozy"+     , "crab"+     , "cram"+     , "crew"+     , "crib"+     , "crop"+     , "crow"+     , "crux"+     , "cube"+     , "cues"+     , "cuff"+     , "cull"+     , "cult"+     , "cups"+     , "curb"+     , "curd"+     , "cure"+     , "curl"+     , "cusp"+     , "cuss"+     , "cute"+     , "cuts"+     , "cyan"+     , "cyst"+     , "czar"+     , "dads"+     , "daft"+     , "dame"+     , "damn"+     , "damp"+     , "dams"+     , "dare"+     , "dark"+     , "dart"+     , "dash"+     , "data"+     , "date"+     , "dawn"+     , "days"+     , "daze"+     , "dead"+     , "deaf"+     , "deal"+     , "dean"+     , "dear"+     , "debt"+     , "deck"+     , "deed"+     , "deem"+     , "deep"+     , "deer"+     , "deft"+     , "defy"+     , "deli"+     , "dell"+     , "demo"+     , "dent"+     , "deny"+     , "desk"+     , "dial"+     , "dice"+     , "died"+     , "dies"+     , "diet"+     , "diff"+     , "digs"+     , "dill"+     , "dime"+     , "dine"+     , "dips"+     , "dire"+     , "dirt"+     , "disc"+     , "dish"+     , "disk"+     , "dive"+     , "dock"+     , "does"+     , "dogs"+     , "doll"+     , "dolt"+     , "dome"+     , "done"+     , "doom"+     , "door"+     , "dope"+     , "dork"+     , "dorm"+     , "dose"+     , "dote"+     , "dots"+     , "dove"+     , "down"+     , "drab"+     , "drag"+     , "draw"+     , "drew"+     , "drip"+     , "drop"+     , "drug"+     , "drum"+     , "dual"+     , "duck"+     , "duct"+     , "dude"+     , "duel"+     , "dues"+     , "duet"+     , "duke"+     , "dull"+     , "duly"+     , "dumb"+     , "dump"+     , "dune"+     , "dunk"+     , "dusk"+     , "dust"+     , "duty"+     , "each"+     , "earl"+     , "earn"+     , "ears"+     , "ease"+     , "east"+     , "easy"+     , "eats"+     , "eave"+     , "echo"+     , "edge"+     , "edgy"+     , "edit"+     , "eels"+     , "eggs"+     , "egos"+     , "else"+     , "emit"+     , "ends"+     , "envy"+     , "epic"+     , "eras"+     , "ergo"+     , "etch"+     , "euro"+     , "even"+     , "ever"+     , "eves"+     , "evil"+     , "exam"+     , "exit"+     , "expo"+     , "eyes"+     , "face"+     , "fact"+     , "fade"+     , "fail"+     , "fair"+     , "fake"+     , "fall"+     , "fame"+     , "fang"+     , "fans"+     , "fare"+     , "farm"+     , "fart"+     , "fast"+     , "fate"+     , "faun"+     , "fawn"+     , "fear"+     , "feat"+     , "feed"+     , "feel"+     , "fees"+     , "feet"+     , "fell"+     , "felt"+     , "fend"+     , "fern"+     , "fest"+     , "feud"+     , "file"+     , "fill"+     , "film"+     , "find"+     , "fine"+     , "fire"+     , "firm"+     , "fish"+     , "fist"+     , "fits"+     , "five"+     , "fizz"+     , "flag"+     , "flak"+     , "flap"+     , "flat"+     , "flaw"+     , "flay"+     , "flea"+     , "fled"+     , "flee"+     , "flew"+     , "flex"+     , "flip"+     , "flog"+     , "flow"+     , "flux"+     , "foal"+     , "foam"+     , "foil"+     , "fold"+     , "folk"+     , "fond"+     , "font"+     , "food"+     , "fool"+     , "foot"+     , "ford"+     , "fore"+     , "fork"+     , "form"+     , "fort"+     , "foul"+     , "four"+     , "fowl"+     , "foxy"+     , "frag"+     , "fray"+     , "free"+     , "fret"+     , "frog"+     , "from"+     , "fuel"+     , "full"+     , "fume"+     , "fund"+     , "furs"+     , "fury"+     , "fuse"+     , "fuss"+     , "gain"+     , "gait"+     , "gala"+     , "game"+     , "gang"+     , "gape"+     , "gaps"+     , "garb"+     , "gash"+     , "gasp"+     , "gate"+     , "gave"+     , "gawk"+     , "gaze"+     , "gear"+     , "geek"+     , "gems"+     , "gene"+     , "germ"+     , "gets"+     , "ghee"+     , "gift"+     , "girl"+     , "gist"+     , "give"+     , "glad"+     , "glam"+     , "glee"+     , "glen"+     , "glib"+     , "glow"+     , "glue"+     , "glum"+     , "gnat"+     , "gnaw"+     , "goal"+     , "goat"+     , "goes"+     , "gold"+     , "golf"+     , "gone"+     , "gong"+     , "good"+     , "goof"+     , "gore"+     , "gory"+     , "gosh"+     , "goth"+     , "gout"+     , "gown"+     , "grab"+     , "gram"+     , "gray"+     , "grew"+     , "grid"+     , "grim"+     , "grin"+     , "grip"+     , "grit"+     , "grow"+     , "grub"+     , "gulf"+     , "gull"+     , "gulp"+     , "gunk"+     , "guns"+     , "guru"+     , "gush"+     , "gust"+     , "guts"+     , "guys"+     , "gyms"+     , "hack"+     , "hail"+     , "hair"+     , "half"+     , "hall"+     , "halo"+     , "halt"+     , "hand"+     , "hang"+     , "hard"+     , "hare"+     , "harm"+     , "harp"+     , "hash"+     , "hate"+     , "hats"+     , "haul"+     , "have"+     , "hawk"+     , "hays"+     , "haze"+     , "hazy"+     , "head"+     , "heal"+     , "heap"+     , "hear"+     , "heat"+     , "heed"+     , "heel"+     , "heft"+     , "heir"+     , "held"+     , "hell"+     , "helm"+     , "help"+     , "hemp"+     , "herb"+     , "herd"+     , "here"+     , "hero"+     , "hers"+     , "hide"+     , "high"+     , "hike"+     , "hill"+     , "hilt"+     , "hind"+     , "hint"+     , "hips"+     , "hire"+     , "hiss"+     , "hits"+     , "hive"+     , "hoax"+     , "hold"+     , "hole"+     , "holy"+     , "home"+     , "hone"+     , "honk"+     , "hood"+     , "hoof"+     , "hook"+     , "hoop"+     , "hoot"+     , "hope"+     , "horn"+     , "hose"+     , "host"+     , "hour"+     , "howl"+     , "huge"+     , "hugs"+     , "hulk"+     , "hull"+     , "hump"+     , "hung"+     , "hunt"+     , "hurl"+     , "hurt"+     , "hush"+     , "husk"+     , "hymn"+     , "hype"+     , "icon"+     , "idea"+     , "idle"+     , "idol"+     , "inch"+     , "info"+     , "into"+     , "iris"+     , "iron"+     , "isle"+     , "itch"+     , "item"+     , "jack"+     , "jade"+     , "jail"+     , "jars"+     , "java"+     , "jaws"+     , "jazz"+     , "jean"+     , "jeer"+     , "jest"+     , "jets"+     , "jinx"+     , "jobs"+     , "john"+     , "join"+     , "joke"+     , "jolt"+     , "jump"+     , "junk"+     , "jury"+     , "just"+     , "keen"+     , "keep"+     , "kelp"+     , "kept"+     , "keys"+     , "kick"+     , "kids"+     , "kill"+     , "kiln"+     , "kilt"+     , "kind"+     , "king"+     , "kink"+     , "kiss"+     , "kite"+     , "kits"+     , "knee"+     , "knew"+     , "knit"+     , "knob"+     , "knot"+     , "know"+     , "labs"+     , "lace"+     , "lack"+     , "lady"+     , "laid"+     , "lair"+     , "lake"+     , "lamb"+     , "lame"+     , "lamp"+     , "land"+     , "lane"+     , "laps"+     , "lard"+     , "lash"+     , "last"+     , "late"+     , "lava"+     , "lawn"+     , "laws"+     , "lays"+     , "laze"+     , "lazy"+     , "lead"+     , "leaf"+     , "leak"+     , "lean"+     , "leap"+     , "leek"+     , "leer"+     , "left"+     , "legs"+     , "lend"+     , "lens"+     , "less"+     , "lest"+     , "levy"+     , "lewd"+     , "liar"+     , "lice"+     , "lick"+     , "lids"+     , "lies"+     , "life"+     , "lift"+     , "like"+     , "limb"+     , "lime"+     , "limp"+     , "line"+     , "link"+     , "lint"+     , "lion"+     , "lips"+     , "lisp"+     , "list"+     , "live"+     , "load"+     , "loaf"+     , "loan"+     , "lobe"+     , "lock"+     , "loft"+     , "logo"+     , "logs"+     , "lone"+     , "long"+     , "look"+     , "loom"+     , "loop"+     , "loot"+     , "lord"+     , "lore"+     , "lose"+     , "loss"+     , "lost"+     , "lots"+     , "loud"+     , "love"+     , "luck"+     , "lull"+     , "lump"+     , "lung"+     , "lure"+     , "lurk"+     , "lush"+     , "lust"+     , "lute"+     , "mace"+     , "made"+     , "maid"+     , "mail"+     , "maim"+     , "main"+     , "make"+     , "male"+     , "mall"+     , "malt"+     , "many"+     , "maps"+     , "mare"+     , "mark"+     , "mash"+     , "mask"+     , "mass"+     , "mast"+     , "mate"+     , "math"+     , "matt"+     , "maul"+     , "maze"+     , "mead"+     , "meal"+     , "mean"+     , "meat"+     , "meek"+     , "meet"+     , "meld"+     , "melt"+     , "meme"+     , "memo"+     , "mend"+     , "menu"+     , "meow"+     , "mere"+     , "mesh"+     , "mess"+     , "mice"+     , "mike"+     , "mild"+     , "mile"+     , "milk"+     , "mill"+     , "mime"+     , "mind"+     , "mine"+     , "mini"+     , "mint"+     , "miss"+     , "mist"+     , "mite"+     , "moan"+     , "moat"+     , "mock"+     , "mode"+     , "mold"+     , "mole"+     , "molt"+     , "monk"+     , "mood"+     , "moon"+     , "mope"+     , "more"+     , "moss"+     , "most"+     , "moth"+     , "move"+     , "much"+     , "mule"+     , "mull"+     , "muse"+     , "mush"+     , "musk"+     , "must"+     , "mute"+     , "mutt"+     , "myth"+     , "nail"+     , "name"+     , "navy"+     , "near"+     , "neat"+     , "neck"+     , "need"+     , "neon"+     , "nerd"+     , "nest"+     , "nets"+     , "news"+     , "newt"+     , "next"+     , "nice"+     , "nick"+     , "nine"+     , "node"+     , "nods"+     , "none"+     , "nook"+     , "noon"+     , "nope"+     , "norm"+     , "nose"+     , "note"+     , "noun"+     , "nude"+     , "nuke"+     , "null"+     , "numb"+     , "nuts"+     , "oaks"+     , "oath"+     , "obey"+     , "oboe"+     , "odds"+     , "odor"+     , "oils"+     , "okay"+     , "omen"+     , "omit"+     , "once"+     , "ones"+     , "only"+     , "onto"+     , "onus"+     , "ooze"+     , "opal"+     , "open"+     , "opts"+     , "oral"+     , "ouch"+     , "ours"+     , "oust"+     , "oval"+     , "oven"+     , "over"+     , "owes"+     , "owns"+     , "pace"+     , "pack"+     , "pact"+     , "pads"+     , "page"+     , "paid"+     , "pail"+     , "pain"+     , "pair"+     , "pale"+     , "pall"+     , "palm"+     , "pane"+     , "pans"+     , "pant"+     , "park"+     , "part"+     , "pass"+     , "past"+     , "path"+     , "pats"+     , "pave"+     , "pawn"+     , "pays"+     , "peak"+     , "pear"+     , "peas"+     , "peck"+     , "peek"+     , "peel"+     , "peep"+     , "peer"+     , "pelt"+     , "pens"+     , "peon"+     , "perk"+     , "pest"+     , "pets"+     , "pick"+     , "pics"+     , "pier"+     , "pies"+     , "pigs"+     , "pike"+     , "pile"+     , "pill"+     , "pine"+     , "pink"+     , "pins"+     , "pint"+     , "pipe"+     , "pits"+     , "pity"+     , "plan"+     , "play"+     , "plea"+     , "plot"+     , "plow"+     , "ploy"+     , "plug"+     , "plum"+     , "plus"+     , "poem"+     , "poet"+     , "poke"+     , "pole"+     , "poll"+     , "pomp"+     , "pond"+     , "pony"+     , "pool"+     , "poor"+     , "pops"+     , "pore"+     , "pork"+     , "port"+     , "pose"+     , "posh"+     , "post"+     , "posy"+     , "pots"+     , "pour"+     , "pout"+     , "pray"+     , "prev"+     , "prey"+     , "prod"+     , "prop"+     , "puck"+     , "puff"+     , "pull"+     , "pulp"+     , "pump"+     , "punk"+     , "punt"+     , "puny"+     , "pure"+     , "push"+     , "puts"+     , "pyre"+     , "quay"+     , "quip"+     , "quit"+     , "quiz"+     , "race"+     , "rack"+     , "raft"+     , "rage"+     , "raid"+     , "rail"+     , "rain"+     , "rake"+     , "ramp"+     , "rang"+     , "rank"+     , "rant"+     , "rape"+     , "rare"+     , "rash"+     , "rate"+     , "rats"+     , "rave"+     , "raze"+     , "read"+     , "real"+     , "reap"+     , "rear"+     , "redo"+     , "reed"+     , "reef"+     , "reek"+     , "reel"+     , "rely"+     , "rent"+     , "rest"+     , "ribs"+     , "rice"+     , "rich"+     , "ride"+     , "rids"+     , "riff"+     , "rift"+     , "rims"+     , "rind"+     , "ring"+     , "rink"+     , "riot"+     , "ripe"+     , "rips"+     , "rise"+     , "risk"+     , "road"+     , "roam"+     , "roar"+     , "robe"+     , "rock"+     , "rods"+     , "role"+     , "roll"+     , "romp"+     , "roof"+     , "rook"+     , "room"+     , "root"+     , "rope"+     , "rose"+     , "rows"+     , "rubs"+     , "ruby"+     , "rude"+     , "ruin"+     , "rule"+     , "rump"+     , "rune"+     , "rung"+     , "runs"+     , "runt"+     , "ruse"+     , "rush"+     , "rusk"+     , "rust"+     , "ruts"+     , "sack"+     , "safe"+     , "saga"+     , "sage"+     , "said"+     , "sail"+     , "sake"+     , "sale"+     , "salt"+     , "same"+     , "sand"+     , "sane"+     , "sang"+     , "sank"+     , "sash"+     , "save"+     , "says"+     , "scab"+     , "scam"+     , "scan"+     , "scar"+     , "scum"+     , "seal"+     , "seam"+     , "sear"+     , "seas"+     , "seat"+     , "seed"+     , "seek"+     , "seem"+     , "seen"+     , "seep"+     , "seer"+     , "self"+     , "sell"+     , "semi"+     , "send"+     , "sent"+     , "serf"+     , "sets"+     , "sewn"+     , "sexy"+     , "shed"+     , "shim"+     , "shin"+     , "ship"+     , "shit"+     , "shod"+     , "shoe"+     , "shop"+     , "shot"+     , "show"+     , "shun"+     , "shut"+     , "sick"+     , "side"+     , "sift"+     , "sigh"+     , "sign"+     , "silk"+     , "sill"+     , "silo"+     , "silt"+     , "sing"+     , "sink"+     , "sins"+     , "sire"+     , "site"+     , "sits"+     , "size"+     , "skew"+     , "skid"+     , "skim"+     , "skin"+     , "skip"+     , "skis"+     , "skit"+     , "slab"+     , "slam"+     , "slap"+     , "slat"+     , "slay"+     , "sled"+     , "slew"+     , "slid"+     , "slim"+     , "slip"+     , "slit"+     , "slob"+     , "slot"+     , "slow"+     , "slug"+     , "slum"+     , "slur"+     , "smog"+     , "smug"+     , "snag"+     , "snap"+     , "snip"+     , "snob"+     , "snot"+     , "snow"+     , "snub"+     , "snug"+     , "soak"+     , "soap"+     , "soar"+     , "sock"+     , "soda"+     , "sofa"+     , "soft"+     , "soil"+     , "sold"+     , "sole"+     , "solo"+     , "some"+     , "song"+     , "sons"+     , "soon"+     , "soot"+     , "sore"+     , "sort"+     , "soul"+     , "soup"+     , "sour"+     , "sown"+     , "sows"+     , "spam"+     , "span"+     , "spar"+     , "spat"+     , "spay"+     , "sped"+     , "spew"+     , "spin"+     , "spit"+     , "spot"+     , "spry"+     , "spun"+     , "spur"+     , "stab"+     , "stag"+     , "star"+     , "stay"+     , "stem"+     , "step"+     , "stew"+     , "stir"+     , "stop"+     , "stow"+     , "stub"+     , "stun"+     , "such"+     , "suck"+     , "sues"+     , "suit"+     , "sulk"+     , "sums"+     , "sung"+     , "sunk"+     , "suns"+     , "sure"+     , "surf"+     , "suss"+     , "swab"+     , "swag"+     , "swam"+     , "swan"+     , "swap"+     , "swat"+     , "sway"+     , "swig"+     , "swim"+     , "swum"+     , "sync"+     , "tack"+     , "tact"+     , "tags"+     , "tail"+     , "take"+     , "tale"+     , "talk"+     , "tall"+     , "tame"+     , "tank"+     , "tape"+     , "taps"+     , "tart"+     , "task"+     , "taut"+     , "taxi"+     , "teal"+     , "team"+     , "tear"+     , "teas"+     , "tech"+     , "teen"+     , "tell"+     , "tend"+     , "tens"+     , "tent"+     , "term"+     , "test"+     , "text"+     , "than"+     , "that"+     , "thaw"+     , "them"+     , "then"+     , "they"+     , "thin"+     , "this"+     , "thud"+     , "thug"+     , "thus"+     , "tick"+     , "tide"+     , "tidy"+     , "tied"+     , "tier"+     , "ties"+     , "tile"+     , "till"+     , "tilt"+     , "time"+     , "tint"+     , "tiny"+     , "tips"+     , "tire"+     , "toad"+     , "toes"+     , "toil"+     , "told"+     , "toll"+     , "tomb"+     , "tone"+     , "tony"+     , "took"+     , "tool"+     , "toot"+     , "tops"+     , "tore"+     , "torn"+     , "toss"+     , "tour"+     , "town"+     , "toys"+     , "tram"+     , "trap"+     , "tray"+     , "tree"+     , "trim"+     , "trip"+     , "trod"+     , "trot"+     , "true"+     , "tuba"+     , "tube"+     , "tuck"+     , "tuft"+     , "tuna"+     , "tune"+     , "turf"+     , "turn"+     , "tusk"+     , "twig"+     , "twin"+     , "twos"+     , "type"+     , "tyre"+     , "ugly"+     , "undo"+     , "unit"+     , "unto"+     , "upon"+     , "urge"+     , "used"+     , "user"+     , "uses"+     , "vain"+     , "vale"+     , "vans"+     , "vary"+     , "vase"+     , "vast"+     , "veal"+     , "veer"+     , "veil"+     , "vein"+     , "vend"+     , "vent"+     , "verb"+     , "very"+     , "vest"+     , "vial"+     , "vibe"+     , "vice"+     , "view"+     , "vile"+     , "vine"+     , "visa"+     , "void"+     , "volt"+     , "vote"+     , "wade"+     , "waft"+     , "wage"+     , "wail"+     , "wait"+     , "wake"+     , "walk"+     , "wall"+     , "wand"+     , "wane"+     , "want"+     , "ward"+     , "ware"+     , "warm"+     , "warn"+     , "warp"+     , "wars"+     , "wart"+     , "wary"+     , "wash"+     , "wasp"+     , "wave"+     , "ways"+     , "weak"+     , "wean"+     , "wear"+     , "webs"+     , "weed"+     , "week"+     , "weep"+     , "weld"+     , "well"+     , "welt"+     , "went"+     , "wept"+     , "were"+     , "west"+     , "wets"+     , "what"+     , "when"+     , "whet"+     , "whey"+     , "whim"+     , "whip"+     , "whom"+     , "wick"+     , "wide"+     , "wife"+     , "wild"+     , "will"+     , "wilt"+     , "wimp"+     , "wind"+     , "wine"+     , "wing"+     , "wink"+     , "wins"+     , "wipe"+     , "wire"+     , "wiry"+     , "wise"+     , "wish"+     , "with"+     , "wits"+     , "woke"+     , "wolf"+     , "womb"+     , "wood"+     , "wool"+     , "word"+     , "wore"+     , "work"+     , "worm"+     , "worn"+     , "wrap"+     , "wren"+     , "wuss"+     , "yank"+     , "yard"+     , "yarn"+     , "yawn"+     , "yeah"+     , "year"+     , "yell"+     , "yelp"+     , "yeti"+     , "yoga"+     , "yolk"+     , "york"+     , "your"+     , "yowl"+     , "zeal"+     , "zero"+     , "zest"+     , "zone"+     , "zoom"+     , "about"+     , "above"+     , "abuse"+     , "actor"+     , "adapt"+     , "added"+     , "admit"+     , "adopt"+     , "adult"+     , "after"+     , "again"+     , "agent"+     , "agree"+     , "ahead"+     , "aisle"+     , "alarm"+     , "album"+     , "alien"+     , "alike"+     , "alive"+     , "alley"+     , "allow"+     , "alone"+     , "along"+     , "alter"+     , "amaze"+     , "among"+     , "angel"+     , "anger"+     , "angle"+     , "angry"+     , "ankle"+     , "apart"+     , "apple"+     , "apply"+     , "arena"+     , "argue"+     , "arise"+     , "armed"+     , "array"+     , "arrow"+     , "aside"+     , "asset"+     , "avoid"+     , "await"+     , "awake"+     , "award"+     , "aware"+     , "awful"+     , "badly"+     , "basic"+     , "basis"+     , "beach"+     , "beard"+     , "beast"+     , "begin"+     , "being"+     , "belly"+     , "below"+     , "bench"+     , "birth"+     , "black"+     , "blade"+     , "blame"+     , "blank"+     , "blast"+     , "blend"+     , "bless"+     , "blind"+     , "blink"+     , "block"+     , "blond"+     , "blood"+     , "board"+     , "boast"+     , "bonus"+     , "boost"+     , "booth"+     , "brain"+     , "brake"+     , "brand"+     , "brave"+     , "bread"+     , "break"+     , "brick"+     , "bride"+     , "brief"+     , "bring"+     , "broad"+     , "brown"+     , "brush"+     , "buddy"+     , "build"+     , "bunch"+     , "burst"+     , "buyer"+     , "cabin"+     , "cable"+     , "candy"+     , "cargo"+     , "carry"+     , "carve"+     , "catch"+     , "cause"+     , "cease"+     , "chain"+     , "chair"+     , "chaos"+     , "charm"+     , "chart"+     , "chase"+     , "cheap"+     , "cheat"+     , "check"+     , "cheek"+     , "cheer"+     , "chest"+     , "chief"+     , "child"+     , "chill"+     , "chunk"+     , "civic"+     , "civil"+     , "claim"+     , "class"+     , "clean"+     , "clear"+     , "clerk"+     , "click"+     , "cliff"+     , "climb"+     , "cling"+     , "clock"+     , "close"+     , "cloth"+     , "cloud"+     , "coach"+     , "coast"+     , "color"+     , "couch"+     , "could"+     , "count"+     , "court"+     , "cover"+     , "crack"+     , "craft"+     , "crash"+     , "crawl"+     , "crazy"+     , "cream"+     , "crest"+     , "crime"+     , "cross"+     , "crowd"+     , "cruel"+     , "crush"+     , "curve"+     , "cycle"+     , "daily"+     , "dance"+     , "death"+     , "debut"+     , "delay"+     , "dense"+     , "depth"+     , "devil"+     , "diary"+     , "dirty"+     , "donor"+     , "doubt"+     , "dough"+     , "dozen"+     , "draft"+     , "drain"+     , "drama"+     , "dream"+     , "dress"+     , "dried"+     , "drift"+     , "drill"+     , "drink"+     , "drive"+     , "drown"+     , "drunk"+     , "dying"+     , "eager"+     , "early"+     , "earth"+     , "eight"+     , "elbow"+     , "elder"+     , "elect"+     , "elite"+     , "empty"+     , "enact"+     , "enemy"+     , "enjoy"+     , "enter"+     , "entry"+     , "equal"+     , "equip"+     , "error"+     , "essay"+     , "event"+     , "every"+     , "exact"+     , "exist"+     , "extra"+     , "faint"+     , "faith"+     , "false"+     , "fatal"+     , "fault"+     , "favor"+     , "fence"+     , "fever"+     , "fewer"+     , "fiber"+     , "field"+     , "fifth"+     , "fifty"+     , "fight"+     , "final"+     , "first"+     , "fixed"+     , "flame"+     , "flash"+     , "fleet"+     , "flesh"+     , "float"+     , "flood"+     , "floor"+     , "flour"+     , "fluid"+     , "focus"+     , "force"+     , "forth"+     , "forty"+     , "forum"+     , "found"+     , "frame"+     , "fraud"+     , "fresh"+     , "front"+     , "frown"+     , "fruit"+     , "fully"+     , "funny"+     , "genre"+     , "ghost"+     , "giant"+     , "given"+     , "glass"+     , "globe"+     , "glory"+     , "glove"+     , "grace"+     , "grade"+     , "grain"+     , "grand"+     , "grant"+     , "grape"+     , "grasp"+     , "grass"+     , "grave"+     , "great"+     , "green"+     , "greet"+     , "grief"+     , "gross"+     , "group"+     , "guard"+     , "guess"+     , "guest"+     , "guide"+     , "guilt"+     , "habit"+     , "happy"+     , "harsh"+     , "heart"+     , "heavy"+     , "hello"+     , "hence"+     , "honey"+     , "honor"+     , "horse"+     , "hotel"+     , "house"+     , "human"+     , "humor"+     , "hurry"+     , "ideal"+     , "image"+     , "imply"+     , "index"+     , "inner"+     , "input"+     , "irony"+     , "issue"+     , "jeans"+     , "jewel"+     , "joint"+     , "judge"+     , "juice"+     , "juror"+     , "kneel"+     , "knife"+     , "knock"+     , "known"+     , "label"+     , "labor"+     , "large"+     , "laser"+     , "later"+     , "laugh"+     , "layer"+     , "learn"+     , "least"+     , "leave"+     , "legal"+     , "lemon"+     , "level"+     , "light"+     , "limit"+     , "liver"+     , "lobby"+     , "local"+     , "logic"+     , "loose"+     , "lover"+     , "lower"+     , "loyal"+     , "lucky"+     , "lunch"+     , "magic"+     , "major"+     , "maker"+     , "march"+     , "marry"+     , "match"+     , "maybe"+     , "mayor"+     , "medal"+     , "media"+     , "merit"+     , "metal"+     , "meter"+     , "midst"+     , "might"+     , "minor"+     , "mixed"+     , "model"+     , "money"+     , "month"+     , "moral"+     , "motor"+     , "mount"+     , "mouse"+     , "mouth"+     , "movie"+     , "music"+     , "naked"+     , "nasty"+     , "naval"+     , "nerve"+     , "never"+     , "newly"+     , "night"+     , "noise"+     , "north"+     , "novel"+     , "nurse"+     , "occur"+     , "ocean"+     , "offer"+     , "often"+     , "onion"+     , "opera"+     , "orbit"+     , "order"+     , "organ"+     , "other"+     , "ought"+     , "outer"+     , "owner"+     , "paint"+     , "panel"+     , "panic"+     , "paper"+     , "party"+     , "pasta"+     , "patch"+     , "pause"+     , "peace"+     , "pearl"+     , "phase"+     , "phone"+     , "photo"+     , "piano"+     , "piece"+     , "pilot"+     , "pitch"+     , "pizza"+     , "place"+     , "plain"+     , "plane"+     , "plant"+     , "plate"+     , "plead"+     , "point"+     , "porch"+     , "pound"+     , "power"+     , "press"+     , "price"+     , "pride"+     , "prime"+     , "print"+     , "prior"+     , "prize"+     , "proof"+     , "proud"+     , "prove"+     , "pulse"+     , "punch"+     , "purse"+     , "queen"+     , "quest"+     , "quick"+     , "quiet"+     , "quite"+     , "quote"+     , "radar"+     , "radio"+     , "raise"+     , "rally"+     , "ranch"+     , "range"+     , "rapid"+     , "ratio"+     , "reach"+     , "react"+     , "ready"+     , "realm"+     , "rebel"+     , "refer"+     , "relax"+     , "reply"+     , "rhyme"+     , "rider"+     , "ridge"+     , "rifle"+     , "right"+     , "risky"+     , "rival"+     , "river"+     , "robot"+     , "rough"+     , "round"+     , "route"+     , "royal"+     , "rumor"+     , "rural"+     , "salad"+     , "sales"+     , "sauce"+     , "scale"+     , "scare"+     , "scary"+     , "scene"+     , "scent"+     , "scope"+     , "score"+     , "screw"+     , "seize"+     , "sense"+     , "serve"+     , "seven"+     , "shade"+     , "shake"+     , "shall"+     , "shame"+     , "shape"+     , "share"+     , "shark"+     , "sharp"+     , "sheep"+     , "sheer"+     , "sheet"+     , "shelf"+     , "shell"+     , "shift"+     , "shine"+     , "shirt"+     , "shock"+     , "shoot"+     , "shore"+     , "short"+     , "shout"+     , "shove"+     , "shrug"+     , "sight"+     , "silly"+     , "since"+     , "sixth"+     , "skill"+     , "skirt"+     , "skull"+     , "slave"+     , "sleep"+     , "slice"+     , "slide"+     , "slope"+     , "small"+     , "smart"+     , "smell"+     , "smile"+     , "smoke"+     , "snake"+     , "sneak"+     , "solar"+     , "solid"+     , "solve"+     , "sorry"+     , "sound"+     , "south"+     , "space"+     , "spare"+     , "spark"+     , "speak"+     , "speed"+     , "spell"+     , "spend"+     , "spill"+     , "spine"+     , "spite"+     , "split"+     , "spoon"+     , "sport"+     , "spray"+     , "squad"+     , "stack"+     , "staff"+     , "stage"+     , "stair"+     , "stake"+     , "stand"+     , "stare"+     , "start"+     , "state"+     , "steak"+     , "steal"+     , "steam"+     , "steel"+     , "steep"+     , "steer"+     , "stick"+     , "stiff"+     , "still"+     , "stock"+     , "stone"+     , "stoop"+     , "store"+     , "storm"+     , "story"+     , "stove"+     , "straw"+     , "strip"+     , "study"+     , "stuff"+     , "style"+     , "sugar"+     , "suite"+     , "sunny"+     , "super"+     , "swear"+     , "sweat"+     , "sweep"+     , "sweet"+     , "swell"+     , "swing"+     , "sword"+     , "table"+     , "taste"+     , "teach"+     , "terms"+     , "thank"+     , "their"+     , "theme"+     , "there"+     , "these"+     , "thick"+     , "thigh"+     , "thing"+     , "think"+     , "third"+     , "those"+     , "three"+     , "throw"+     , "thumb"+     , "tight"+     , "tired"+     , "title"+     , "today"+     , "tooth"+     , "topic"+     , "total"+     , "touch"+     , "tough"+     , "towel"+     , "tower"+     , "toxic"+     , "trace"+     , "track"+     , "trade"+     , "trail"+     , "train"+     , "trait"+     , "trash"+     , "treat"+     , "trend"+     , "trial"+     , "tribe"+     , "trick"+     , "troop"+     , "truck"+     , "truly"+     , "trunk"+     , "trust"+     , "truth"+     , "tumor"+     , "twice"+     , "twist"+     , "uncle"+     , "under"+     , "union"+     , "unite"+     , "unity"+     , "until"+     , "upper"+     , "upset"+     , "urban"+     , "usual"+     , "valid"+     , "value"+     , "video"+     , "virus"+     , "visit"+     , "vital"+     , "vocal"+     , "voice"+     , "voter"+     , "wagon"+     , "waist"+     , "waste"+     , "watch"+     , "water"+     , "weave"+     , "weigh"+     , "weird"+     , "whale"+     , "wheat"+     , "wheel"+     , "where"+     , "which"+     , "while"+     , "white"+     , "whole"+     , "whose"+     , "widow"+     , "woman"+     , "works"+     , "world"+     , "worry"+     , "worth"+     , "would"+     , "wound"+     , "wrist"+     , "write"+     , "wrong"+     , "yield"+     , "young"+     , "yours"+     , "youth"+     , "abroad"+     , "absorb"+     , "accent"+     , "accept"+     , "access"+     , "accuse"+     , "across"+     , "action"+     , "active"+     , "actual"+     , "adjust"+     , "admire"+     , "advice"+     , "advise"+     , "affair"+     , "affect"+     , "afford"+     , "afraid"+     , "agency"+     , "agenda"+     , "almost"+     , "always"+     , "amount"+     , "animal"+     , "annual"+     , "answer"+     , "anyone"+     , "anyway"+     , "appeal"+     , "appear"+     , "around"+     , "arrest"+     , "arrive"+     , "artist"+     , "asleep"+     , "aspect"+     , "assert"+     , "assess"+     , "assign"+     , "assist"+     , "assume"+     , "assure"+     , "attach"+     , "attack"+     , "attend"+     , "author"+     , "ballot"+     , "banana"+     , "banker"+     , "barely"+     , "barrel"+     , "basket"+     , "battle"+     , "beauty"+     , "become"+     , "before"+     , "behalf"+     , "behave"+     , "behind"+     , "belief"+     , "belong"+     , "beside"+     , "better"+     , "beyond"+     , "bishop"+     , "bitter"+     , "bloody"+     , "border"+     , "boring"+     , "borrow"+     , "bother"+     , "bottle"+     , "bottom"+     , "bounce"+     , "branch"+     , "breast"+     , "breath"+     , "breeze"+     , "bridge"+     , "bright"+     , "broken"+     , "broker"+     , "bronze"+     , "brutal"+     , "bubble"+     , "bucket"+     , "budget"+     , "bullet"+     , "burden"+     , "bureau"+     , "butter"+     , "button"+     , "camera"+     , "campus"+     , "cancel"+     , "cancer"+     , "candle"+     , "canvas"+     , "carbon"+     , "career"+     , "carpet"+     , "carrot"+     , "casino"+     , "casual"+     , "cattle"+     , "center"+     , "chance"+     , "change"+     , "charge"+     , "cheese"+     , "choice"+     , "choose"+     , "church"+     , "circle"+     , "client"+     , "clinic"+     , "closed"+     , "closer"+     , "closet"+     , "coffee"+     , "collar"+     , "colony"+     , "column"+     , "combat"+     , "comedy"+     , "coming"+     , "commit"+     , "common"+     , "compel"+     , "comply"+     , "convey"+     , "cookie"+     , "corner"+     , "costly"+     , "cotton"+     , "county"+     , "couple"+     , "course"+     , "cousin"+     , "create"+     , "credit"+     , "crisis"+     , "critic"+     , "cruise"+     , "custom"+     , "damage"+     , "dancer"+     , "danger"+     , "deadly"+     , "dealer"+     , "debate"+     , "debris"+     , "decade"+     , "decent"+     , "decide"+     , "deeply"+     , "defeat"+     , "defend"+     , "define"+     , "degree"+     , "demand"+     , "denial"+     , "depart"+     , "depend"+     , "depict"+     , "deploy"+     , "deputy"+     , "derive"+     , "desert"+     , "design"+     , "desire"+     , "detail"+     , "detect"+     , "device"+     , "devote"+     , "differ"+     , "dining"+     , "dinner"+     , "direct"+     , "divide"+     , "divine"+     , "doctor"+     , "domain"+     , "donate"+     , "double"+     , "drawer"+     , "driver"+     , "during"+     , "easily"+     , "eating"+     , "editor"+     , "effect"+     , "effort"+     , "eighth"+     , "either"+     , "emerge"+     , "empire"+     , "employ"+     , "enable"+     , "endure"+     , "energy"+     , "engage"+     , "engine"+     , "enough"+     , "enroll"+     , "ensure"+     , "entire"+     , "entity"+     , "equity"+     , "escape"+     , "estate"+     , "ethics"+     , "ethnic"+     , "evolve"+     , "exceed"+     , "except"+     , "excuse"+     , "exotic"+     , "expand"+     , "expect"+     , "expert"+     , "export"+     , "expose"+     , "extend"+     , "extent"+     , "fabric"+     , "factor"+     , "fairly"+     , "family"+     , "famous"+     , "farmer"+     , "faster"+     , "father"+     , "fellow"+     , "female"+     , "fierce"+     , "figure"+     , "filter"+     , "finger"+     , "finish"+     , "firmly"+     , "fiscal"+     , "flavor"+     , "flight"+     , "flower"+     , "flying"+     , "follow"+     , "forbid"+     , "forest"+     , "forget"+     , "formal"+     , "format"+     , "former"+     , "foster"+     , "fourth"+     , "freely"+     , "freeze"+     , "friend"+     , "frozen"+     , "future"+     , "galaxy"+     , "garage"+     , "garden"+     , "garlic"+     , "gather"+     , "gender"+     , "genius"+     , "gentle"+     , "gently"+     , "gifted"+     , "glance"+     , "global"+     , "golden"+     , "govern"+     , "ground"+     , "growth"+     , "guilty"+     , "guitar"+     , "handle"+     , "happen"+     , "hardly"+     , "hazard"+     , "health"+     , "heaven"+     , "height"+     , "helmet"+     , "hidden"+     , "highly"+     , "hockey"+     , "honest"+     , "horror"+     , "hunger"+     , "hungry"+     , "hunter"+     , "ignore"+     , "immune"+     , "impact"+     , "import"+     , "impose"+     , "income"+     , "indeed"+     , "infant"+     , "inform"+     , "injure"+     , "injury"+     , "inmate"+     , "insect"+     , "insert"+     , "inside"+     , "insist"+     , "intact"+     , "intend"+     , "intent"+     , "invade"+     , "invent"+     , "invest"+     , "invite"+     , "island"+     , "itself"+     , "jacket"+     , "jungle"+     , "junior"+     , "killer"+     , "ladder"+     , "lately"+     , "latter"+     , "launch"+     , "lawyer"+     , "leader"+     , "league"+     , "legacy"+     , "legend"+     , "length"+     , "lesson"+     , "letter"+     , "likely"+     , "liquid"+     , "listen"+     , "little"+     , "living"+     , "locate"+     , "lonely"+     , "lovely"+     , "mainly"+     , "makeup"+     , "manage"+     , "manner"+     , "manual"+     , "marble"+     , "margin"+     , "marine"+     , "marker"+     , "market"+     , "master"+     , "matter"+     , "medium"+     , "member"+     , "memory"+     , "mental"+     , "mentor"+     , "merely"+     , "method"+     , "middle"+     , "minute"+     , "mirror"+     , "mobile"+     , "modern"+     , "modest"+     , "modify"+     , "moment"+     , "monkey"+     , "mostly"+     , "mother"+     , "motion"+     , "motive"+     , "murder"+     , "muscle"+     , "museum"+     , "mutter"+     , "mutual"+     , "myself"+     , "narrow"+     , "nation"+     , "native"+     , "nature"+     , "nearby"+     , "nearly"+     , "needle"+     , "nobody"+     , "normal"+     , "notice"+     , "notion"+     , "number"+     , "object"+     , "obtain"+     , "occupy"+     , "office"+     , "online"+     , "openly"+     , "oppose"+     , "option"+     , "orange"+     , "origin"+     , "others"+     , "outfit"+     , "outlet"+     , "output"+     , "oxygen"+     , "palace"+     , "parade"+     , "parent"+     , "parish"+     , "partly"+     , "pastor"+     , "patent"+     , "patrol"+     , "patron"+     , "peanut"+     , "pencil"+     , "people"+     , "pepper"+     , "period"+     , "permit"+     , "person"+     , "phrase"+     , "pickup"+     , "pillow"+     , "pistol"+     , "planet"+     , "player"+     , "please"+     , "plenty"+     , "plunge"+     , "pocket"+     , "poetry"+     , "police"+     , "policy"+     , "poster"+     , "potato"+     , "powder"+     , "praise"+     , "prayer"+     , "preach"+     , "prefer"+     , "pretty"+     , "priest"+     , "prison"+     , "profit"+     , "prompt"+     , "proper"+     , "public"+     , "punish"+     , "purple"+     , "pursue"+     , "puzzle"+     , "rabbit"+     , "racial"+     , "racism"+     , "random"+     , "rarely"+     , "rather"+     , "rating"+     , "reader"+     , "really"+     , "reason"+     , "recall"+     , "recent"+     , "recipe"+     , "record"+     , "reduce"+     , "reform"+     , "refuge"+     , "refuse"+     , "regain"+     , "regard"+     , "regime"+     , "region"+     , "regret"+     , "reject"+     , "relate"+     , "relief"+     , "remain"+     , "remark"+     , "remind"+     , "remote"+     , "remove"+     , "render"+     , "rental"+     , "repair"+     , "repeat"+     , "report"+     , "rescue"+     , "resign"+     , "resist"+     , "resort"+     , "result"+     , "resume"+     , "retail"+     , "retain"+     , "retire"+     , "return"+     , "reveal"+     , "review"+     , "reward"+     , "rhythm"+     , "ribbon"+     , "ritual"+     , "rocket"+     , "rubber"+     , "ruling"+     , "runner"+     , "sacred"+     , "safely"+     , "safety"+     , "salary"+     , "salmon"+     , "sample"+     , "saving"+     , "scared"+     , "scheme"+     , "school"+     , "scream"+     , "screen"+     , "script"+     , "search"+     , "season"+     , "second"+     , "secret"+     , "sector"+     , "secure"+     , "seldom"+     , "select"+     , "seller"+     , "senior"+     , "sensor"+     , "series"+     , "settle"+     , "severe"+     , "sexual"+     , "shadow"+     , "shared"+     , "shorts"+     , "should"+     , "shower"+     , "shrimp"+     , "shrink"+     , "signal"+     , "silent"+     , "silver"+     , "simple"+     , "simply"+     , "singer"+     , "single"+     , "sister"+     , "sleeve"+     , "slight"+     , "slowly"+     , "smooth"+     , "soccer"+     , "social"+     , "sodium"+     , "soften"+     , "softly"+     , "solely"+     , "source"+     , "speech"+     , "sphere"+     , "spirit"+     , "spouse"+     , "spread"+     , "spring"+     , "square"+     , "stable"+     , "stance"+     , "statue"+     , "status"+     , "steady"+     , "strain"+     , "streak"+     , "stream"+     , "street"+     , "stress"+     , "strict"+     , "strike"+     , "string"+     , "stroke"+     , "strong"+     , "studio"+     , "stupid"+     , "submit"+     , "subtle"+     , "suburb"+     , "sudden"+     , "suffer"+     , "summer"+     , "summit"+     , "supply"+     , "surely"+     , "survey"+     , "switch"+     , "symbol"+     , "system"+     , "tackle"+     , "tactic"+     , "talent"+     , "target"+     , "temple"+     , "tender"+     , "tennis"+     , "terror"+     , "thanks"+     , "theory"+     , "thirty"+     , "though"+     , "thread"+     , "threat"+     , "thrive"+     , "throat"+     , "ticket"+     , "timber"+     , "timing"+     , "tissue"+     , "toilet"+     , "tomato"+     , "tongue"+     , "toward"+     , "tragic"+     , "trauma"+     , "travel"+     , "treaty"+     , "tribal"+     , "tunnel"+     , "turkey"+     , "twelve"+     , "twenty"+     , "unable"+     , "unfair"+     , "unfold"+     , "unique"+     , "unless"+     , "unlike"+     , "update"+     , "useful"+     , "vacuum"+     , "valley"+     , "vanish"+     , "vendor"+     , "verbal"+     , "versus"+     , "vessel"+     , "victim"+     , "viewer"+     , "virtue"+     , "vision"+     , "visual"+     , "volume"+     , "voting"+     , "wander"+     , "warmth"+     , "weaken"+     , "wealth"+     , "weapon"+     , "weekly"+     , "weight"+     , "widely"+     , "window"+     , "winner"+     , "winter"+     , "wisdom"+     , "within"+     , "wonder"+     , "wooden"+     , "worker"+     , "writer"+     , "yellow"+     , "abandon"+     , "ability"+     , "absence"+     , "account"+     , "achieve"+     , "acquire"+     , "actress"+     , "address"+     , "advance"+     , "adviser"+     , "against"+     , "airline"+     , "airport"+     , "alcohol"+     , "alleged"+     , "already"+     , "analyst"+     , "analyze"+     , "ancient"+     , "another"+     , "anxiety"+     , "anxious"+     , "anybody"+     , "anymore"+     , "apology"+     , "appoint"+     , "approve"+     , "arrange"+     , "arrival"+     , "article"+     , "assault"+     , "athlete"+     , "attempt"+     , "attract"+     , "auction"+     , "average"+     , "balance"+     , "balloon"+     , "banking"+     , "barrier"+     , "battery"+     , "because"+     , "bedroom"+     , "believe"+     , "beneath"+     , "benefit"+     , "besides"+     , "between"+     , "bicycle"+     , "billion"+     , "biology"+     , "blanket"+     , "bombing"+     , "breathe"+     , "briefly"+     , "brother"+     , "builder"+     , "burning"+     , "cabinet"+     , "capable"+     , "capital"+     , "captain"+     , "capture"+     , "careful"+     , "carrier"+     , "cartoon"+     , "catalog"+     , "ceiling"+     , "central"+     , "century"+     , "certain"+     , "chamber"+     , "channel"+     , "chapter"+     , "charity"+     , "charter"+     , "chicken"+     , "chronic"+     , "circuit"+     , "citizen"+     , "classic"+     , "clearly"+     , "climate"+     , "closely"+     , "closest"+     , "clothes"+     , "cluster"+     , "coastal"+     , "cocaine"+     , "collect"+     , "college"+     , "combine"+     , "comfort"+     , "command"+     , "comment"+     , "company"+     , "compare"+     , "compete"+     , "complex"+     , "compose"+     , "concede"+     , "concept"+     , "concern"+     , "concert"+     , "condemn"+     , "conduct"+     , "confess"+     , "confirm"+     , "confuse"+     , "connect"+     , "consent"+     , "consist"+     , "consult"+     , "consume"+     , "contact"+     , "contain"+     , "contend"+     , "content"+     , "contest"+     , "context"+     , "control"+     , "convert"+     , "convict"+     , "cooking"+     , "correct"+     , "costume"+     , "cottage"+     , "council"+     , "counsel"+     , "counter"+     , "country"+     , "courage"+     , "crowded"+     , "crucial"+     , "crystal"+     , "culture"+     , "curious"+     , "current"+     , "curtain"+     , "custody"+     , "dancing"+     , "declare"+     , "decline"+     , "defense"+     , "deficit"+     , "delight"+     , "deliver"+     , "density"+     , "deposit"+     , "descend"+     , "deserve"+     , "despite"+     , "dessert"+     , "destroy"+     , "develop"+     , "diamond"+     , "dictate"+     , "digital"+     , "dignity"+     , "dilemma"+     , "discuss"+     , "disease"+     , "dismiss"+     , "display"+     , "dispute"+     , "distant"+     , "disturb"+     , "diverse"+     , "divorce"+     , "doorway"+     , "drawing"+     , "driving"+     , "dynamic"+     , "eastern"+     , "economy"+     , "edition"+     , "educate"+     , "elderly"+     , "elegant"+     , "element"+     , "embrace"+     , "emotion"+     , "endless"+     , "endorse"+     , "enforce"+     , "enhance"+     , "entitle"+     , "episode"+     , "equally"+     , "essence"+     , "ethical"+     , "evening"+     , "evident"+     , "exactly"+     , "examine"+     , "example"+     , "excited"+     , "exclude"+     , "execute"+     , "exhaust"+     , "exhibit"+     , "expense"+     , "explain"+     , "explode"+     , "exploit"+     , "explore"+     , "express"+     , "extreme"+     , "eyebrow"+     , "factory"+     , "faculty"+     , "failure"+     , "fantasy"+     , "fashion"+     , "fatigue"+     , "feather"+     , "feature"+     , "federal"+     , "feeling"+     , "fiction"+     , "fifteen"+     , "fighter"+     , "finally"+     , "finance"+     , "finding"+     , "fishing"+     , "fitness"+     , "foreign"+     , "forever"+     , "forgive"+     , "formula"+     , "fortune"+     , "forward"+     , "founder"+     , "fragile"+     , "frankly"+     , "freedom"+     , "fuchsia"+     , "fucking"+     , "funding"+     , "funeral"+     , "gallery"+     , "garbage"+     , "general"+     , "genetic"+     , "genuine"+     , "gesture"+     , "glimpse"+     , "gravity"+     , "greatly"+     , "grimace"+     , "grocery"+     , "growing"+     , "habitat"+     , "halfway"+     , "hallway"+     , "handful"+     , "happily"+     , "harmony"+     , "harvest"+     , "healthy"+     , "hearing"+     , "heavily"+     , "helpful"+     , "herself"+     , "highway"+     , "himself"+     , "history"+     , "holiday"+     , "horizon"+     , "hormone"+     , "hostage"+     , "hostile"+     , "housing"+     , "however"+     , "hundred"+     , "hunting"+     , "husband"+     , "illegal"+     , "illness"+     , "imagine"+     , "impress"+     , "improve"+     , "impulse"+     , "include"+     , "inherit"+     , "initial"+     , "inquiry"+     , "insight"+     , "inspire"+     , "install"+     , "instant"+     , "instead"+     , "intense"+     , "involve"+     , "isolate"+     , "jewelry"+     , "journal"+     , "journey"+     , "justice"+     , "justify"+     , "killing"+     , "kingdom"+     , "kitchen"+     , "landing"+     , "largely"+     , "laundry"+     , "lawsuit"+     , "leading"+     , "leather"+     , "lecture"+     , "legally"+     , "liberal"+     , "liberty"+     , "library"+     , "license"+     , "lightly"+     , "limited"+     , "logical"+     , "loyalty"+     , "machine"+     , "manager"+     , "mandate"+     , "mansion"+     , "married"+     , "massive"+     , "maximum"+     , "meaning"+     , "measure"+     , "medical"+     , "meeting"+     , "mention"+     , "message"+     , "million"+     , "mineral"+     , "minimal"+     , "minimum"+     , "miracle"+     , "missile"+     , "missing"+     , "mission"+     , "mistake"+     , "mixture"+     , "monitor"+     , "monster"+     , "monthly"+     , "morning"+     , "musical"+     , "mystery"+     , "natural"+     , "neither"+     , "nervous"+     , "network"+     , "neutral"+     , "nominee"+     , "nothing"+     , "nowhere"+     , "nuclear"+     , "observe"+     , "obvious"+     , "offense"+     , "officer"+     , "ongoing"+     , "opening"+     , "operate"+     , "opinion"+     , "opposed"+     , "organic"+     , "outcome"+     , "outdoor"+     , "outline"+     , "outside"+     , "overall"+     , "oversee"+     , "package"+     , "painful"+     , "painter"+     , "parking"+     , "partial"+     , "partner"+     , "passage"+     , "passing"+     , "passion"+     , "patient"+     , "pattern"+     , "payment"+     , "peasant"+     , "penalty"+     , "pension"+     , "perfect"+     , "perform"+     , "perhaps"+     , "persist"+     , "physics"+     , "picture"+     , "pioneer"+     , "pitcher"+     , "planner"+     , "plastic"+     , "playoff"+     , "pleased"+     , "popular"+     , "portion"+     , "portray"+     , "possess"+     , "poverty"+     , "precise"+     , "predict"+     , "premise"+     , "premium"+     , "prepare"+     , "present"+     , "pretend"+     , "prevail"+     , "prevent"+     , "primary"+     , "privacy"+     , "private"+     , "problem"+     , "proceed"+     , "process"+     , "produce"+     , "product"+     , "profile"+     , "program"+     , "project"+     , "promise"+     , "promote"+     , "propose"+     , "protect"+     , "protein"+     , "protest"+     , "provide"+     , "provoke"+     , "publish"+     , "purpose"+     , "pursuit"+     , "qualify"+     , "quality"+     , "quarter"+     , "quickly"+     , "quietly"+     , "radical"+     , "rapidly"+     , "readily"+     , "reading"+     , "reality"+     , "realize"+     , "rebuild"+     , "receive"+     , "recover"+     , "recruit"+     , "reflect"+     , "refugee"+     , "regular"+     , "related"+     , "release"+     , "relieve"+     , "removal"+     , "replace"+     , "request"+     , "require"+     , "reserve"+     , "resolve"+     , "respect"+     , "respond"+     , "restore"+     , "retired"+     , "retreat"+     , "revenue"+     , "reverse"+     , "rolling"+     , "romance"+     , "roughly"+     , "routine"+     , "running"+     , "satisfy"+     , "scandal"+     , "scatter"+     , "scholar"+     , "science"+     , "scratch"+     , "section"+     , "secular"+     , "segment"+     , "seminar"+     , "senator"+     , "serious"+     , "servant"+     , "service"+     , "serving"+     , "session"+     , "setting"+     , "seventh"+     , "several"+     , "shallow"+     , "sharply"+     , "shelter"+     , "shortly"+     , "shuttle"+     , "sibling"+     , "silence"+     , "similar"+     , "skilled"+     , "slavery"+     , "society"+     , "soldier"+     , "someday"+     , "somehow"+     , "someone"+     , "speaker"+     , "special"+     , "species"+     , "specify"+     , "sponsor"+     , "squeeze"+     , "stadium"+     , "starter"+     , "station"+     , "statute"+     , "stomach"+     , "storage"+     , "strange"+     , "stretch"+     , "student"+     , "stumble"+     , "subject"+     , "subsidy"+     , "succeed"+     , "success"+     , "suggest"+     , "suicide"+     , "summary"+     , "support"+     , "suppose"+     , "surface"+     , "surgeon"+     , "surgery"+     , "survive"+     , "suspect"+     , "suspend"+     , "sustain"+     , "swallow"+     , "sweater"+     , "symptom"+     , "teacher"+     , "teenage"+     , "tension"+     , "terrain"+     , "testify"+     , "testing"+     , "texture"+     , "theater"+     , "therapy"+     , "thereby"+     , "thought"+     , "through"+     , "tighten"+     , "tightly"+     , "tobacco"+     , "tonight"+     , "totally"+     , "tourism"+     , "tourist"+     , "towards"+     , "trading"+     , "traffic"+     , "tragedy"+     , "trailer"+     , "trainer"+     , "transit"+     , "trigger"+     , "triumph"+     , "trouble"+     , "typical"+     , "uncover"+     , "undergo"+     , "unhappy"+     , "uniform"+     , "unknown"+     , "unusual"+     , "usually"+     , "utility"+     , "utilize"+     , "vaccine"+     , "variety"+     , "various"+     , "vehicle"+     , "venture"+     , "verdict"+     , "version"+     , "veteran"+     , "victory"+     , "village"+     , "violate"+     , "violent"+     , "virtual"+     , "visible"+     , "visitor"+     , "vitamin"+     , "walking"+     , "warning"+     , "warrior"+     , "wealthy"+     , "weather"+     , "wedding"+     , "weekend"+     , "welcome"+     , "welfare"+     , "western"+     , "whereas"+     , "whether"+     , "whisper"+     , "whoever"+     , "willing"+     , "without"+     , "witness"+     , "working"+     , "workout"+     , "worried"+     , "writing"+     , "written"+     , "abortion"+     , "absolute"+     , "abstract"+     , "academic"+     , "accident"+     , "accuracy"+     , "accurate"+     , "actively"+     , "activist"+     , "activity"+     , "actually"+     , "addition"+     , "adequate"+     , "adoption"+     , "advanced"+     , "advocate"+     , "aircraft"+     , "airplane"+     , "alliance"+     , "although"+     , "aluminum"+     , "ambition"+     , "analysis"+     , "ancestor"+     , "announce"+     , "annually"+     , "anything"+     , "anywhere"+     , "apparent"+     , "approach"+     , "approval"+     , "argument"+     , "artifact"+     , "artistic"+     , "assemble"+     , "assembly"+     , "athletic"+     , "attitude"+     , "attorney"+     , "audience"+     , "autonomy"+     , "backyard"+     , "bacteria"+     , "balanced"+     , "baseball"+     , "basement"+     , "bathroom"+     , "behavior"+     , "birthday"+     , "blessing"+     , "boundary"+     , "building"+     , "business"+     , "calendar"+     , "campaign"+     , "capacity"+     , "casualty"+     , "category"+     , "cemetery"+     , "ceremony"+     , "chairman"+     , "champion"+     , "changing"+     , "chemical"+     , "civilian"+     , "classify"+     , "clinical"+     , "clothing"+     , "collapse"+     , "colonial"+     , "colorful"+     , "combined"+     , "commonly"+     , "complain"+     , "complete"+     , "compound"+     , "comprise"+     , "computer"+     , "conceive"+     , "conclude"+     , "concrete"+     , "conflict"+     , "confront"+     , "consider"+     , "constant"+     , "consumer"+     , "continue"+     , "contract"+     , "contrast"+     , "convince"+     , "corridor"+     , "coverage"+     , "creation"+     , "creative"+     , "creature"+     , "criminal"+     , "criteria"+     , "critical"+     , "cultural"+     , "currency"+     , "customer"+     , "darkness"+     , "database"+     , "daughter"+     , "deadline"+     , "decision"+     , "decorate"+     , "decrease"+     , "dedicate"+     , "defender"+     , "delicate"+     , "delivery"+     , "describe"+     , "designer"+     , "detailed"+     , "diabetes"+     , "diagnose"+     , "dialogue"+     , "diminish"+     , "diplomat"+     , "directly"+     , "director"+     , "disabled"+     , "disagree"+     , "disaster"+     , "disclose"+     , "discount"+     , "discover"+     , "disorder"+     , "dissolve"+     , "distance"+     , "distinct"+     , "distract"+     , "district"+     , "division"+     , "doctrine"+     , "document"+     , "domestic"+     , "dominant"+     , "dominate"+     , "donation"+     , "downtown"+     , "dramatic"+     , "drinking"+     , "driveway"+     , "dynamics"+     , "earnings"+     , "economic"+     , "educator"+     , "election"+     , "electric"+     , "elephant"+     , "elevator"+     , "eligible"+     , "emerging"+     , "emission"+     , "emphasis"+     , "employee"+     , "employer"+     , "engineer"+     , "enormous"+     , "entirely"+     , "entrance"+     , "envelope"+     , "envision"+     , "epidemic"+     , "equality"+     , "equation"+     , "estimate"+     , "evaluate"+     , "everyday"+     , "everyone"+     , "evidence"+     , "exchange"+     , "exciting"+     , "exercise"+     , "existing"+     , "expected"+     , "explicit"+     , "exposure"+     , "extended"+     , "external"+     , "facility"+     , "familiar"+     , "favorite"+     , "feedback"+     , "feminist"+     , "festival"+     , "fighting"+     , "flexible"+     , "football"+     , "forehead"+     , "formerly"+     , "fraction"+     , "fragment"+     , "frequent"+     , "freshman"+     , "friendly"+     , "frontier"+     , "function"+     , "gasoline"+     , "generate"+     , "generous"+     , "governor"+     , "graduate"+     , "grateful"+     , "greatest"+     , "guidance"+     , "handsome"+     , "hardware"+     , "headache"+     , "headline"+     , "heritage"+     , "hesitate"+     , "historic"+     , "homeland"+     , "homeless"+     , "homework"+     , "honestly"+     , "horrible"+     , "hospital"+     , "humanity"+     , "identify"+     , "identity"+     , "ideology"+     , "illusion"+     , "improved"+     , "incident"+     , "increase"+     , "indicate"+     , "industry"+     , "informal"+     , "inherent"+     , "initiate"+     , "innocent"+     , "instance"+     , "instinct"+     , "instruct"+     , "interact"+     , "interest"+     , "interior"+     , "internal"+     , "interval"+     , "intimate"+     , "invasion"+     , "investor"+     , "involved"+     , "isolated"+     , "judgment"+     , "judicial"+     , "landmark"+     , "language"+     , "laughter"+     , "lawmaker"+     , "learning"+     , "lifetime"+     , "lighting"+     , "likewise"+     , "listener"+     , "literary"+     , "location"+     , "longtime"+     , "magazine"+     , "magnetic"+     , "maintain"+     , "majority"+     , "managing"+     , "marriage"+     , "material"+     , "meantime"+     , "mechanic"+     , "medicine"+     , "mentally"+     , "merchant"+     , "metaphor"+     , "midnight"+     , "military"+     , "minimize"+     , "minister"+     , "ministry"+     , "minority"+     , "moderate"+     , "molecule"+     , "momentum"+     , "monument"+     , "moreover"+     , "mortgage"+     , "motivate"+     , "mountain"+     , "movement"+     , "multiple"+     , "mushroom"+     , "musician"+     , "national"+     , "negative"+     , "neighbor"+     , "normally"+     , "northern"+     , "notebook"+     , "numerous"+     , "nutrient"+     , "observer"+     , "obstacle"+     , "occasion"+     , "offender"+     , "offering"+     , "official"+     , "operator"+     , "opponent"+     , "opposite"+     , "ordinary"+     , "organism"+     , "organize"+     , "original"+     , "outsider"+     , "overcome"+     , "overlook"+     , "painting"+     , "parental"+     , "particle"+     , "patience"+     , "peaceful"+     , "perceive"+     , "personal"+     , "persuade"+     , "physical"+     , "planning"+     , "platform"+     , "pleasant"+     , "pleasure"+     , "politics"+     , "portrait"+     , "position"+     , "positive"+     , "possible"+     , "possibly"+     , "powerful"+     , "practice"+     , "precious"+     , "predator"+     , "pregnant"+     , "presence"+     , "preserve"+     , "pressure"+     , "previous"+     , "priority"+     , "prisoner"+     , "probably"+     , "proclaim"+     , "producer"+     , "profound"+     , "progress"+     , "prohibit"+     , "properly"+     , "property"+     , "proposal"+     , "proposed"+     , "prospect"+     , "protocol"+     , "provided"+     , "provider"+     , "province"+     , "publicly"+     , "purchase"+     , "quantity"+     , "question"+     , "railroad"+     , "rational"+     , "reaction"+     , "receiver"+     , "recently"+     , "recovery"+     , "regional"+     , "register"+     , "regulate"+     , "relation"+     , "relative"+     , "relevant"+     , "reliable"+     , "religion"+     , "remember"+     , "reminder"+     , "reporter"+     , "republic"+     , "required"+     , "research"+     , "resemble"+     , "resident"+     , "resource"+     , "response"+     , "restrict"+     , "retailer"+     , "rhetoric"+     , "romantic"+     , "sanction"+     , "sandwich"+     , "scenario"+     , "schedule"+     , "scramble"+     , "security"+     , "selected"+     , "sentence"+     , "separate"+     , "sequence"+     , "severely"+     , "sexually"+     , "shooting"+     , "shopping"+     , "shortage"+     , "shoulder"+     , "sidewalk"+     , "slightly"+     , "socially"+     , "software"+     , "solution"+     , "somebody"+     , "sometime"+     , "somewhat"+     , "southern"+     , "specific"+     , "spectrum"+     , "spending"+     , "sprinkle"+     , "standard"+     , "standing"+     , "starting"+     , "steadily"+     , "stimulus"+     , "straight"+     , "stranger"+     , "strategy"+     , "strength"+     , "strictly"+     , "striking"+     , "strongly"+     , "struggle"+     , "suburban"+     , "suddenly"+     , "suitable"+     , "sunlight"+     , "superior"+     , "supplier"+     , "supposed"+     , "surprise"+     , "surround"+     , "survival"+     , "survivor"+     , "swimming"+     , "symbolic"+     , "sympathy"+     , "syndrome"+     , "talented"+     , "taxpayer"+     , "teaching"+     , "teammate"+     , "teaspoon"+     , "teenager"+     , "tendency"+     , "terrible"+     , "terribly"+     , "terrific"+     , "textbook"+     , "theology"+     , "thinking"+     , "thousand"+     , "threaten"+     , "together"+     , "tolerate"+     , "tomorrow"+     , "training"+     , "transfer"+     , "transmit"+     , "traveler"+     , "treasure"+     , "tropical"+     , "troubled"+     , "ultimate"+     , "universe"+     , "unlikely"+     , "upstairs"+     , "vacation"+     , "validity"+     , "valuable"+     , "variable"+     , "vertical"+     , "violence"+     , "weakness"+     , "whatever"+     , "whenever"+     , "wherever"+     , "wildlife"+     , "withdraw"+     , "workshop"+     , "yourself"+     , "accompany"+     , "according"+     , "admission"+     , "advantage"+     , "adventure"+     , "advertise"+     , "aesthetic"+     , "afternoon"+     , "afterward"+     , "agreement"+     , "allegedly"+     , "alongside"+     , "ambitious"+     , "amendment"+     , "anonymous"+     , "apartment"+     , "apologize"+     , "architect"+     , "assistant"+     , "associate"+     , "attention"+     , "attribute"+     , "authority"+     , "authorize"+     , "automatic"+     , "available"+     , "awareness"+     , "basically"+     , "beautiful"+     , "beginning"+     , "biography"+     , "boyfriend"+     , "breakfast"+     , "breathing"+     , "brilliant"+     , "broadcast"+     , "butterfly"+     , "calculate"+     , "candidate"+     , "carefully"+     , "celebrate"+     , "celebrity"+     , "certainly"+     , "challenge"+     , "character"+     , "chemistry"+     , "childhood"+     , "chocolate"+     , "cigarette"+     , "classical"+     , "classroom"+     , "coalition"+     , "cognitive"+     , "colleague"+    ]++-- Shuffled so that you get an even distribution of words+-- when taking `leftSide`+type FourLetterWords = ToTree+    '[ "ergo"+     , "blur"+     , "gaze"+     , "said"+     , "cone"+     , "thaw"+     , "undo"+     , "than"+     , "vile"+     , "rods"+     , "thug"+     , "hurt"+     , "matt"+     , "spun"+     , "eggs"+     , "tail"+     , "copy"+     , "says"+     , "gust"+     , "rims"+     , "mild"+     , "feed"+     , "hawk"+     , "pelt"+     , "pawn"+     , "suit"+     , "rain"+     , "trap"+     , "bulk"+     , "slug"+     , "spay"+     , "ring"+     , "hate"+     , "time"+     , "mole"+     , "skip"+     , "cart"+     , "pall"+     , "full"+     , "glue"+     , "fate"+     , "fast"+     , "keen"+     , "mead"+     , "flux"+     , "jets"+     , "part"+     , "bone"+     , "wept"+     , "apex"+     , "lace"+     , "very"+     , "dawn"+     , "lies"+     , "ware"+     , "bead"+     , "anew"+     , "lint"+     , "deal"+     , "slur"+     , "lung"+     , "came"+     , "mace"+     , "bonk"+     , "text"+     , "save"+     , "dell"+     , "left"+     , "clip"+     , "mute"+     , "glib"+     , "gala"+     , "arts"+     , "rush"+     , "raid"+     , "asks"+     , "pray"+     , "amid"+     , "cram"+     , "leap"+     , "best"+     , "prey"+     , "laws"+     , "jazz"+     , "redo"+     , "eave"+     , "fore"+     , "shoe"+     , "nice"+     , "grit"+     , "paid"+     , "boom"+     , "whim"+     , "used"+     , "puck"+     , "smog"+     , "sack"+     , "year"+     , "pens"+     , "flex"+     , "hash"+     , "suss"+     , "sums"+     , "bait"+     , "cyst"+     , "hilt"+     , "warp"+     , "cave"+     , "fuss"+     , "spry"+     , "bunk"+     , "firm"+     , "lend"+     , "pulp"+     , "port"+     , "lick"+     , "aria"+     , "body"+     , "sway"+     , "edgy"+     , "lewd"+     , "wuss"+     , "horn"+     , "yowl"+     , "link"+     , "chop"+     , "kiln"+     , "moss"+     , "dome"+     , "halo"+     , "lull"+     , "fits"+     , "foul"+     , "cask"+     , "bred"+     , "wink"+     , "grow"+     , "helm"+     , "cage"+     , "lime"+     , "lump"+     , "take"+     , "wall"+     , "hoop"+     , "pore"+     , "lazy"+     , "poet"+     , "sure"+     , "root"+     , "nods"+     , "buck"+     , "vine"+     , "seam"+     , "died"+     , "toss"+     , "oven"+     , "palm"+     , "rusk"+     , "bomb"+     , "tore"+     , "gash"+     , "glad"+     , "maim"+     , "hips"+     , "arch"+     , "skit"+     , "tree"+     , "snot"+     , "bees"+     , "toll"+     , "step"+     , "prod"+     , "food"+     , "pits"+     , "gape"+     , "welt"+     , "diet"+     , "saga"+     , "limb"+     , "bent"+     , "bark"+     , "tuck"+     , "gasp"+     , "heat"+     , "hind"+     , "tune"+     , "buoy"+     , "buff"+     , "gaps"+     , "womb"+     , "ball"+     , "hats"+     , "clog"+     , "ages"+     , "cuff"+     , "lips"+     , "were"+     , "pump"+     , "rate"+     , "cues"+     , "damn"+     , "knit"+     , "lust"+     , "seer"+     , "ford"+     , "swat"+     , "dunk"+     , "iris"+     , "hack"+     , "grid"+     , "tied"+     , "rats"+     , "brag"+     , "mesh"+     , "lock"+     , "stir"+     , "snub"+     , "bake"+     , "baby"+     , "call"+     , "club"+     , "cyan"+     , "boar"+     , "high"+     , "tear"+     , "safe"+     , "wood"+     , "hemp"+     , "play"+     , "pool"+     , "rest"+     , "sail"+     , "tire"+     , "corn"+     , "sold"+     , "plum"+     , "webs"+     , "talk"+     , "pale"+     , "plea"+     , "luck"+     , "into"+     , "thud"+     , "drew"+     , "seem"+     , "hump"+     , "belt"+     , "kiss"+     , "each"+     , "cash"+     , "jean"+     , "hill"+     , "digs"+     , "null"+     , "when"+     , "chap"+     , "wake"+     , "rear"+     , "pike"+     , "farm"+     , "mold"+     , "lean"+     , "pins"+     , "gulf"+     , "buys"+     , "tool"+     , "demo"+     , "made"+     , "plug"+     , "bard"+     , "ears"+     , "oaks"+     , "lest"+     , "plus"+     , "nuke"+     , "legs"+     , "plan"+     , "dash"+     , "hair"+     , "lute"+     , "army"+     , "evil"+     , "look"+     , "wipe"+     , "gist"+     , "meow"+     , "bail"+     , "knew"+     , "nail"+     , "deck"+     , "ever"+     , "snag"+     , "desk"+     , "heel"+     , "some"+     , "pans"+     , "germ"+     , "mind"+     , "will"+     , "vans"+     , "guys"+     , "dorm"+     , "bids"+     , "lays"+     , "code"+     , "pays"+     , "shod"+     , "lose"+     , "sash"+     , "clap"+     , "grab"+     , "oral"+     , "taut"+     , "cull"+     , "toys"+     , "slow"+     , "molt"+     , "ties"+     , "bane"+     , "salt"+     , "hike"+     , "idol"+     , "uses"+     , "maid"+     , "pear"+     , "gray"+     , "melt"+     , "most"+     , "news"+     , "cope"+     , "bawl"+     , "rank"+     , "mock"+     , "clay"+     , "lard"+     , "crib"+     , "noun"+     , "peel"+     , "sore"+     , "nick"+     , "meld"+     , "main"+     , "jars"+     , "mist"+     , "skim"+     , "easy"+     , "road"+     , "gosh"+     , "kite"+     , "wise"+     , "foot"+     , "puff"+     , "cusp"+     , "lush"+     , "boon"+     , "fray"+     , "gunk"+     , "cult"+     , "garb"+     , "cent"+     , "flee"+     , "rung"+     , "math"+     , "dire"+     , "yard"+     , "loom"+     , "kill"+     , "dice"+     , "ajar"+     , "vial"+     , "rise"+     , "hone"+     , "whet"+     , "skin"+     , "vase"+     , "silo"+     , "flap"+     , "fork"+     , "pick"+     , "meme"+     , "boat"+     , "jump"+     , "sewn"+     , "memo"+     , "page"+     , "chin"+     , "zeal"+     , "fees"+     , "peek"+     , "furs"+     , "guts"+     , "euro"+     , "tidy"+     , "site"+     , "veil"+     , "labs"+     , "wilt"+     , "busk"+     , "stag"+     , "ploy"+     , "tart"+     , "beer"+     , "data"+     , "heap"+     , "beds"+     , "tusk"+     , "heed"+     , "deem"+     , "shut"+     , "clan"+     , "burn"+     , "stay"+     , "size"+     , "hers"+     , "glow"+     , "dope"+     , "gong"+     , "pour"+     , "wave"+     , "here"+     , "hiss"+     , "dial"+     , "bled"+     , "prev"+     , "diff"+     , "rave"+     , "loud"+     , "king"+     , "fume"+     , "bare"+     , "comb"+     , "claw"+     , "bars"+     , "mush"+     , "dads"+     , "idea"+     , "twos"+     , "glam"+     , "song"+     , "tend"+     , "rust"+     , "tray"+     , "over"+     , "ours"+     , "caps"+     , "duet"+     , "what"+     , "dote"+     , "dock"+     , "case"+     , "dude"+     , "bull"+     , "tuna"+     , "opts"+     , "weld"+     , "west"+     , "skid"+     , "upon"+     , "raft"+     , "dine"+     , "stew"+     , "wren"+     , "lady"+     , "slip"+     , "wine"+     , "prop"+     , "fall"+     , "rule"+     , "junk"+     , "pain"+     , "newt"+     , "tank"+     , "game"+     , "must"+     , "jaws"+     , "riot"+     , "room"+     , "cook"+     , "risk"+     , "race"+     , "wrap"+     , "soap"+     , "dump"+     , "spur"+     , "thin"+     , "duke"+     , "scan"+     , "etch"+     , "mime"+     , "seen"+     , "rook"+     , "gets"+     , "care"+     , "neck"+     , "teen"+     , "punk"+     , "post"+     , "dove"+     , "brim"+     , "sole"+     , "silk"+     , "bold"+     , "bale"+     , "vary"+     , "lash"+     , "vein"+     , "down"+     , "pave"+     , "burp"+     , "does"+     , "dead"+     , "also"+     , "veal"+     , "snap"+     , "punt"+     , "gate"+     , "john"+     , "toad"+     , "swig"+     , "teas"+     , "soot"+     , "drag"+     , "real"+     , "fake"+     , "vale"+     , "tame"+     , "swim"+     , "cake"+     , "fine"+     , "balk"+     , "gone"+     , "mend"+     , "oboe"+     , "grew"+     , "pond"+     , "peer"+     , "view"+     , "bird"+     , "gull"+     , "meet"+     , "flip"+     , "seal"+     , "fail"+     , "mass"+     , "team"+     , "pole"+     , "hope"+     , "path"+     , "yelp"+     , "mull"+     , "eels"+     , "coal"+     , "wire"+     , "wade"+     , "send"+     , "aids"+     , "cord"+     , "bang"+     , "life"+     , "push"+     , "rang"+     , "slot"+     , "jail"+     , "exam"+     , "raze"+     , "ramp"+     , "name"+     , "rail"+     , "card"+     , "boil"+     , "find"+     , "pact"+     , "duck"+     , "host"+     , "dogs"+     , "self"+     , "tyre"+     , "sunk"+     , "butt"+     , "home"+     , "bald"+     , "chat"+     , "kink"+     , "show"+     , "skis"+     , "jinx"+     , "tick"+     , "leaf"+     , "aqua"+     , "roar"+     , "mail"+     , "your"+     , "kits"+     , "fire"+     , "them"+     , "colt"+     , "foxy"+     , "list"+     , "serf"+     , "loft"+     , "joke"+     , "type"+     , "park"+     , "pail"+     , "wide"+     , "male"+     , "cans"+     , "ones"+     , "veer"+     , "deft"+     , "cuts"+     , "tens"+     , "icon"+     , "hear"+     , "pyre"+     , "wing"+     , "wets"+     , "core"+     , "well"+     , "perk"+     , "brat"+     , "deaf"+     , "tale"+     , "mint"+     , "sang"+     , "soda"+     , "girl"+     , "cops"+     , "tram"+     , "pack"+     , "fuse"+     , "rent"+     , "camp"+     , "acid"+     , "suck"+     , "hero"+     , "kick"+     , "fowl"+     , "loss"+     , "peep"+     , "week"+     , "yank"+     , "feet"+     , "onto"+     , "free"+     , "runs"+     , "runt"+     , "mike"+     , "only"+     , "hulk"+     , "turn"+     , "rude"+     , "wart"+     , "faun"+     , "balm"+     , "obey"+     , "czar"+     , "dams"+     , "fold"+     , "slid"+     , "rich"+     , "snip"+     , "scum"+     , "mean"+     , "meek"+     , "ruse"+     , "hugs"+     , "term"+     , "fish"+     , "tier"+     , "gyms"+     , "tony"+     , "atop"+     , "ally"+     , "crow"+     , "jest"+     , "ahoy"+     , "gram"+     , "hoax"+     , "dies"+     , "pout"+     , "yolk"+     , "iron"+     , "nose"+     , "jade"+     , "feud"+     , "bean"+     , "poor"+     , "area"+     , "shot"+     , "ooze"+     , "file"+     , "less"+     , "sake"+     , "weep"+     , "such"+     , "york"+     , "puny"+     , "hole"+     , "spam"+     , "fort"+     , "once"+     , "foam"+     , "hazy"+     , "tall"+     , "rake"+     , "lids"+     , "sane"+     , "mess"+     , "nest"+     , "omit"+     , "dolt"+     , "cane"+     , "warn"+     , "rage"+     , "seed"+     , "lead"+     , "told"+     , "knob"+     , "drop"+     , "gush"+     , "riff"+     , "wear"+     , "folk"+     , "dots"+     , "scab"+     , "ruby"+     , "tape"+     , "gift"+     , "soak"+     , "laps"+     , "quip"+     , "side"+     , "vibe"+     , "rips"+     , "lots"+     , "boot"+     , "ugly"+     , "hood"+     , "clue"+     , "wage"+     , "sick"+     , "ride"+     , "cosy"+     , "numb"+     , "pats"+     , "bans"+     , "glum"+     , "read"+     , "haze"+     , "taps"+     , "cold"+     , "vote"+     , "mice"+     , "coin"+     , "gait"+     , "idle"+     , "fend"+     , "onus"+     , "born"+     , "ends"+     , "help"+     , "gain"+     , "spar"+     , "doom"+     , "opal"+     , "tube"+     , "band"+     , "tuft"+     , "rubs"+     , "sofa"+     , "soup"+     , "robe"+     , "peas"+     , "spot"+     , "hush"+     , "wins"+     , "hare"+     , "zoom"+     , "harm"+     , "hull"+     , "come"+     , "verb"+     , "lost"+     , "sigh"+     , "rump"+     , "bowl"+     , "fond"+     , "warm"+     , "scam"+     , "dear"+     , "jolt"+     , "swab"+     , "seek"+     , "bony"+     , "laze"+     , "late"+     , "aims"+     , "ouch"+     , "lava"+     , "zone"+     , "whey"+     , "land"+     , "nerd"+     , "eras"+     , "chef"+     , "heft"+     , "turf"+     , "past"+     , "mark"+     , "yeti"+     , "clop"+     , "dent"+     , "epic"+     , "whom"+     , "mast"+     , "dust"+     , "mall"+     , "from"+     , "deer"+     , "drip"+     , "deep"+     , "rows"+     , "owns"+     , "base"+     , "duct"+     , "woke"+     , "bays"+     , "bask"+     , "tags"+     , "feel"+     , "maul"+     , "logs"+     , "silt"+     , "feat"+     , "herb"+     , "next"+     , "sues"+     , "mite"+     , "with"+     , "blob"+     , "roof"+     , "geek"+     , "dull"+     , "meat"+     , "pant"+     , "dual"+     , "dive"+     , "open"+     , "flog"+     , "spit"+     , "unto"+     , "pile"+     , "acre"+     , "dusk"+     , "sank"+     , "rape"+     , "pics"+     , "rink"+     , "guns"+     , "oath"+     , "slam"+     , "sled"+     , "okay"+     , "mope"+     , "bend"+     , "trot"+     , "shim"+     , "town"+     , "guru"+     , "weak"+     , "fair"+     , "shit"+     , "gave"+     , "deny"+     , "deed"+     , "fans"+     , "hymn"+     , "soil"+     , "lice"+     , "nets"+     , "zest"+     , "stop"+     , "yoga"+     , "rely"+     , "mode"+     , "pots"+     , "form"+     , "kelp"+     , "half"+     , "coil"+     , "swap"+     , "city"+     , "fern"+     , "edit"+     , "fill"+     , "blip"+     , "hoof"+     , "rift"+     , "spat"+     , "coup"+     , "cats"+     , "yawn"+     , "leer"+     , "shed"+     , "pies"+     , "malt"+     , "pint"+     , "bags"+     , "gulp"+     , "leek"+     , "glen"+     , "wind"+     , "stun"+     , "clam"+     , "cube"+     , "bass"+     , "nope"+     , "know"+     , "holy"+     , "chug"+     , "herd"+     , "spin"+     , "cool"+     , "word"+     , "ways"+     , "that"+     , "hide"+     , "eats"+     , "drab"+     , "tech"+     , "took"+     , "grim"+     , "book"+     , "work"+     , "akin"+     , "pass"+     , "bank"+     , "rare"+     , "ripe"+     , "itch"+     , "mule"+     , "poke"+     , "bolt"+     , "snug"+     , "wimp"+     , "loot"+     , "ward"+     , "ghee"+     , "beta"+     , "dean"+     , "dime"+     , "film"+     , "wane"+     , "tile"+     , "muse"+     , "went"+     , "fool"+     , "navy"+     , "huge"+     , "even"+     , "heal"+     , "visa"+     , "earn"+     , "fart"+     , "pull"+     , "dart"+     , "howl"+     , "debt"+     , "awry"+     , "mare"+     , "auto"+     , "dark"+     , "crux"+     , "isle"+     , "cozy"+     , "bump"+     , "vend"+     , "font"+     , "wand"+     , "sour"+     , "babe"+     , "bran"+     , "bode"+     , "goal"+     , "foil"+     , "knee"+     , "bade"+     , "bash"+     , "aide"+     , "neat"+     , "swag"+     , "hurl"+     , "rope"+     , "quit"+     , "reef"+     , "bong"+     , "note"+     , "fund"+     , "thus"+     , "hose"+     , "sexy"+     , "grin"+     , "beep"+     , "duty"+     , "fled"+     , "mill"+     , "near"+     , "quay"+     , "fare"+     , "lack"+     , "earl"+     , "gory"+     , "fuel"+     , "tuba"+     , "sand"+     , "fear"+     , "sign"+     , "live"+     , "hype"+     , "door"+     , "much"+     , "node"+     , "snow"+    ]
+ test/Main.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+module Main where++import Data.Memorable.Internal+import Data.Proxy+import Data.Memorable.Theme.Words+import Data.Word+import Data.Int+import Data.List+import GHC.TypeLits+import Text.Printf+import Test.Tasty+import Test.Tasty.QuickCheck as QC+import qualified Test.Tasty.SmallCheck as SC+import Test.Tasty.HUnit+import Test.DocTest++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Test"+    [ properties+    , unitTests+    ]++type TestType = (Int8, Int32, Word8, Word64)+testPat = padHex @112 (words8 .- words10)+testPat2 = padDec @112 (hex8 .- words4 .- dec4)++properties :: TestTree+properties = testGroup "Properties"+    [ QC.testProperty "is show" (\x -> show (x :: Word8) == renderMemorable dec8 x)+    , QC.testProperty "is show tuple" (\(a,b) -> show (a :: Word8) ++ "-" ++ show (b :: Word8) == renderMemorable (two dec8) (a,b))+    , QC.testProperty "is %02x" (\x -> printf "%02x" (x :: Word8) == renderMemorable hex8 x)+    , QC.testProperty "is %04x" (\x -> printf "%04x" (x :: Word16) == renderMemorable hex16 x)+    , QC.testProperty "is %08x" (\x -> printf "%08x" (x :: Word32) == renderMemorable hex32 x)+    , QC.testProperty "tuples" (\(a,b) -> printf "%02x-%02x" (a :: Word8) (b :: Word8) == renderMemorable (hex8 .- hex8) (a,b))+    , QC.testProperty "render/parse" (\a -> runParser (runRender (a :: TestType)) == a)+    , QC.testProperty "rMem/pMem" (\a -> parseMemorable testPat (renderMemorable testPat a) == Just (a :: TestType))+    , QC.testProperty "rMem/pMem" (\a -> parseMemorable testPat2 (renderMemorable testPat2 a) == Just (a :: TestType))+    , QC.testProperty "rerender" (\a -> let x = renderMemorable testPat (a :: TestType) in Just x == (rerender testPat2 testPat =<< rerender testPat testPat2 x))+    ]++scProps = testGroup "(checked by SmallCheck)"+    [+    ]++unitTests :: TestTree+unitTests = testGroup "Unit tests"+    [ testCase "DocTest" $ doctest ["./src/"]+    , testGroup "Unique"+        [ testCase "padHex words4" $ assert (uniqueRenderings (padHex words4))+        , testCase "padDec words4" $ assert (uniqueRenderings (padDec words4))+        , testCase "words4 .- words4" $ assert (uniqueRenderings (words4 .- words4))+        , testCase "Hex" $ assert (uniqueRenderings (Proxy :: Proxy (Number Dec 8)))+        , testCase "Dec" $ assert (uniqueRenderings (Proxy :: Proxy (Number Hex 8)))+        ]+    ]++uniqueRenderings :: (Depth p ~ 8, MemRender p) => Proxy p -> Bool+uniqueRenderings p =+    let+        ls = map (renderMemorable p) [minBound .. maxBound :: Word8]+    in+        ls == nub ls