packages feed

pgp-wordlist (empty) → 0.1

raw patch · 8 files changed

+688/−0 lines, 8 filesdep +HUnitdep +basedep +bimapsetup-changed

Dependencies added: HUnit, base, bimap, bytestring, pgp-wordlist, tasty, tasty-hunit, tasty-quickcheck, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, David Luposchainsky++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of David Luposchainsky nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+[![Build Status](https://secure.travis-ci.org/quchen/pgp-wordlist.png?branch=master)](http://travis-ci.org/quchen/pgp-wordlist)++Translate between binary data and a human-readable collection of words.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ pgp-wordlist.cabal view
@@ -0,0 +1,70 @@+-- Initial pgp-wordlist.cabal generated by cabal init.  For further+-- documentation, see http://haskell.org/cabal/users-guide/++name:                pgp-wordlist+version:             0.1+synopsis:            Translate between binary data and a human-readable+                     collection of words.+description:         Translate between binary data and a human-readable+                     collection of words.+                     .+                     The PGP Word List consists of two phonetic alphabets, each+                     with one word per possible byte value. A string of bytes+                     is translated with these alphabets, alternating between+                     them at each byte.+                     .+                     The PGP words corresponding to the bytes @5B 1D CA 6E@+                     are "erase breakaway spellbind headwaters", for example.+                     .+                     For further information, see+                     <http://en.wikipedia.org/wiki/PGP_word_list Wikipedia>.+license:             BSD3+license-file:        LICENSE+author:              David Luposchainsky <dluposchainsky(λ)gmail.com>+maintainer:          David Luposchainsky <dluposchainsky(λ)gmail.com>+copyright:           David Luposchainsky <dluposchainsky(λ)gmail.com>+homepage:            https://github.com/quchen/pgp-wordlist+bug-reports:         https://github.com/quchen/pgp-wordlist/issues+category:            Data, Serialization+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  README.md+tested-with:         GHC == 7.6.3+                   , GHC == 7.8.4+                   , GHC == 7.10.1++++source-repository head+  type:     git+  location: https://github.com/quchen/pgp-wordlist++++library+    exposed-modules:     Data.Text.PgpWordlist+                       , Data.Text.PgpWordlist.Internal.AltList+                       , Data.Text.PgpWordlist.Internal.Tables+    build-depends:       base >=4.6 && < 5+                       , bimap >=0.3 && <0.4+                       , bytestring >=0.10 && <0.11+                       , text >=1.2 && <1.3+    hs-source-dirs:      src+    ghc-options:         -Wall+    default-language:    Haskell2010++++test-suite tasty+    main-is: test/Main.hs+    ghc-options: -Wall+    type: exitcode-stdio-1.0+    default-language: Haskell2010+    build-depends: pgp-wordlist+                 , base+                 , bytestring+                 , HUnit+                 , tasty >= 0.10+                 , tasty-hunit >= 0.9+                 , tasty-quickcheck+                 , text
+ src/Data/Text/PgpWordlist.hs view
@@ -0,0 +1,8 @@+-- | Translate between binary data and a human-readable collection of words.+module Data.Text.PgpWordlist (+      toText+    , fromText+    , TranslationError(..)+) where++import Data.Text.PgpWordlist.Internal.Tables
+ src/Data/Text/PgpWordlist/Internal/AltList.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE CPP #-}++-- | Defines a type of list that containins two alternating element types.+module Data.Text.PgpWordlist.Internal.AltList (+      AltList(..)++    -- * Construction, deconstruction+    , fromList+    , toList++    -- * Bifunctor+    , first+    , second+    , bimap++    -- * Bitraversable+    , bisequence+    , bitraverse+) where++++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif++++-- | List of elements of alternating element types.+data AltList a b = Nil | Cons a (AltList b a)+    deriving (Eq, Ord, Show)++++-- | Simple conversion function. Inverse of 'toList'.+fromList :: [a] -> AltList a a+fromList = foldr Cons Nil++-- | Simple conversion function. Inverse of 'fromList'.+toList :: AltList a a -> [a]+toList Nil = []+toList (Cons x xs) = x : toList xs++++-- | Map over the first component of an 'AltList'.+--+-- @+-- 'first' f ≡ 'bimap' f 'id'+-- @+first :: (a -> a') -> AltList a b -> AltList a' b+first _ Nil = Nil+first f (Cons x Nil) = Cons (f x) Nil+first f (Cons x (Cons y ys)) = Cons (f x) (Cons y (first f ys))++++-- | Map over the second component of an 'AltList'.+--+-- @+-- 'second' g ≡ 'bimap' 'id' g+-- @+second :: (b -> b') -> AltList a b -> AltList a b'+second _ Nil = Nil+second _ (Cons x Nil) = Cons x Nil+second g (Cons x (Cons y ys)) = Cons x (Cons (g y) (second g ys))++++-- | Map over the both components of an 'AltList'.+--+-- @+-- 'bimap' f g ≡ 'second' g . 'first' f+-- @+bimap :: (a -> a') -> (b -> b') -> AltList a b -> AltList a' b'+bimap _ _ Nil = Nil+bimap f g (Cons x xs) = Cons (f x) (bimap g f xs)++++-- | Monomorphic bisequence.+bisequence :: Applicative f => AltList (f a) (f b) -> f (AltList a b)+bisequence Nil = pure Nil+bisequence (Cons x xs) = Cons <$> x <*> bisequence xs++-- | Monomorphic bitraverse.+bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> AltList a b -> f (AltList c d)+bitraverse _ _ Nil = pure Nil+bitraverse f g (Cons x xs) = Cons <$> f x <*> bitraverse g f xs
+ src/Data/Text/PgpWordlist/Internal/Tables.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Core functionality for conversion between binary formats and PGP word+--   lists.+module Data.Text.PgpWordlist.Internal.Tables where++++import           Data.Text.PgpWordlist.Internal.AltList (AltList)+import qualified Data.Text.PgpWordlist.Internal.AltList as Alt++import           Data.Bimap                             (Bimap, (!))+import qualified Data.Bimap                             as BM++import qualified Data.ByteString.Lazy                   as BSL+import           Data.Text                              (Text)+import qualified Data.Text                              as T+import           Data.Word++++-- | Abstract representation of a PGP word list.+newtype PgpWordlist = PgpWordlist (AltList EvenWord OddWord)+    deriving (Eq, Ord, Show)++++-- | Possible translation errors from a list of PGP words to binary format.+data TranslationError =+      BadWord Text         -- ^ Word is not recognized+    | BadParity Text Word8 -- ^ Word is recognized, but from the wrong+                           --   alphabet. Duplicates, omissions, and neighbour+                           --   transpositions are often cause for this.+    deriving (Eq, Ord, Show)++++-- | Inverse of 'fromText', modulo whitespace count.+toText :: BSL.ByteString -> Text+toText = T.intercalate " "+       . Alt.toList+       . Alt.bimap (unEvenWord . toEvenWord) (unOddWord . toOddWord)+       . Alt.fromList+       . BSL.unpack++++-- | Convert a text of whitespace-separated words to their binary+--   representation. The whitespace splitting behaviour is given by 'T.words'.+fromText :: Text -> Either TranslationError BSL.ByteString+fromText = fmap (BSL.pack . Alt.toList)+         . Alt.bitraverse fromEvenWord fromOddWord+         . Alt.fromList+         . T.words++++-- | Word that is supposed to occur in an even position+newtype EvenWord = EvenWord { unEvenWord :: Text }+    deriving (Eq, Ord, Show)++-- | Look up the word corresponding to a byte.+toEvenWord :: Word8 -> EvenWord+toEvenWord = (evenMap !) -- evenMap is total, so the lookup is safe++-- | Simple conversion, taking into account invalid words.+fromEvenWord :: Text -> Either TranslationError Word8+fromEvenWord word = case BM.lookupR (EvenWord word) evenMap of+    Just i  -> Right i+    Nothing -> Left (case BM.lookupR (OddWord word) oddMap of+        Just j  -> BadParity word j+        Nothing -> BadWord word)++-- | Mapping from and to 'EvenWord's+evenMap :: Bimap Word8 EvenWord+evenMap = BM.fromList (map pick12 wordList)+  where+    pick12 :: (a,b,c) -> (a,b)+    pick12 (i,e,_) = (i,e)++++-- | Word that is supposed to occur in an odd position+newtype OddWord = OddWord { unOddWord :: Text }+    deriving (Eq, Ord, Show)++-- | Look up the word corresponding to a byte.+toOddWord :: Word8 -> OddWord+toOddWord = (oddMap !) -- oddMap is total, so the lookup is safe++-- | Simple conversion, taking into account invalid words.+fromOddWord :: Text -> Either TranslationError Word8+fromOddWord word = case BM.lookupR (OddWord word) oddMap of+    Just i  -> Right i+    Nothing -> Left (case BM.lookupR (EvenWord word) evenMap of+        Just j  -> BadParity word j+        Nothing -> BadWord word)++-- | Mapping from and to 'OddWord's+oddMap :: Bimap Word8 OddWord+oddMap = BM.fromList (map pick13 wordList)+  where+    pick13 :: (a,b,c) -> (a,c)+    pick13 (i,_,o) = (i,o)++++wordList :: [(Word8, EvenWord, OddWord)]+wordList = map (\(i,e,o) -> (i, EvenWord e, OddWord o)) table+  where+    table = [ (0x00, "aardvark" , "adroitness" )+            , (0x01, "absurd"   , "adviser"    )+            , (0x02, "accrue"   , "aftermath"  )+            , (0x03, "acme"     , "aggregate"  )+            , (0x04, "adrift"   , "alkali"     )+            , (0x05, "adult"    , "almighty"   )+            , (0x06, "afflict"  , "amulet"     )+            , (0x07, "ahead"    , "amusement"  )+            , (0x08, "aimless"  , "antenna"    )+            , (0x09, "algol"    , "applicant"  )+            , (0x0a, "allow"    , "apollo"     )+            , (0x0b, "alone"    , "armistice"  )+            , (0x0c, "ammo"     , "article"    )+            , (0x0d, "ancient"  , "asteroid"   )+            , (0x0e, "apple"    , "atlantic"   )+            , (0x0f, "artist"   , "atmosphere" )+            , (0x10, "assume"   , "autopsy"    )+            , (0x11, "athens"   , "babylon"    )+            , (0x12, "atlas"    , "backwater"  )+            , (0x13, "aztec"    , "barbecue"   )+            , (0x14, "baboon"   , "belowground")+            , (0x15, "backfield", "bifocals"   )+            , (0x16, "backward" , "bodyguard"  )+            , (0x17, "banjo"    , "bookseller" )+            , (0x18, "beaming"  , "borderline" )+            , (0x19, "bedlamp"  , "bottomless" )+            , (0x1a, "beehive"  , "bradbury"   )+            , (0x1b, "beeswax"  , "bravado"    )+            , (0x1c, "befriend" , "brazilian"  )+            , (0x1d, "belfast"  , "breakaway"  )+            , (0x1e, "berserk"  , "burlington" )+            , (0x1f, "billiard" , "businessman")+            , (0x20, "bison"    , "butterfat"  )+            , (0x21, "blackjack", "camelot"    )+            , (0x22, "blockade" , "candidate"  )+            , (0x23, "blowtorch", "cannonball" )+            , (0x24, "bluebird" , "capricorn"  )+            , (0x25, "bombast"  , "caravan"    )+            , (0x26, "bookshelf", "caretaker"  )+            , (0x27, "brackish" , "celebrate"  )+            , (0x28, "breadline", "cellulose"  )+            , (0x29, "breakup"  , "certify"    )+            , (0x2a, "brickyard", "chambermaid")+            , (0x2b, "briefcase", "cherokee"   )+            , (0x2c, "burbank"  , "chicago"    )+            , (0x2d, "button"   , "clergyman"  )+            , (0x2e, "buzzard"  , "coherence"  )+            , (0x2f, "cement"   , "combustion" )+            , (0x30, "chairlift", "commando"   )+            , (0x31, "chatter"  , "company"    )+            , (0x32, "checkup"  , "component"  )+            , (0x33, "chisel"   , "concurrent" )+            , (0x34, "choking"  , "confidence" )+            , (0x35, "chopper"  , "conformist" )+            , (0x36, "christmas", "congregate" )+            , (0x37, "clamshell", "consensus"  )+            , (0x38, "classic"  , "consulting" )+            , (0x39, "classroom", "corporate"  )+            , (0x3a, "cleanup"  , "corrosion"  )+            , (0x3b, "clockwork", "councilman" )+            , (0x3c, "cobra"    , "crossover"  )+            , (0x3d, "commence" , "crucifix"   )+            , (0x3e, "concert"  , "cumbersome" )+            , (0x3f, "cowbell"  , "customer"   )+            , (0x40, "crackdown", "dakota"     )+            , (0x41, "cranky"   , "decadence"  )+            , (0x42, "crowfoot" , "december"   )+            , (0x43, "crucial"  , "decimal"    )+            , (0x44, "crumpled" , "designing"  )+            , (0x45, "crusade"  , "detector"   )+            , (0x46, "cubic"    , "detergent"  )+            , (0x47, "dashboard", "determine"  )+            , (0x48, "deadbolt" , "dictator"   )+            , (0x49, "deckhand" , "dinosaur"   )+            , (0x4a, "dogsled"  , "direction"  )+            , (0x4b, "dragnet"  , "disable"    )+            , (0x4c, "drainage" , "disbelief"  )+            , (0x4d, "dreadful" , "disruptive" )+            , (0x4e, "drifter"  , "distortion" )+            , (0x4f, "dropper"  , "document"   )+            , (0x50, "drumbeat" , "embezzle"   )+            , (0x51, "drunken"  , "enchanting" )+            , (0x52, "dupont"   , "enrollment" )+            , (0x53, "dwelling" , "enterprise" )+            , (0x54, "eating"   , "equation"   )+            , (0x55, "edict"    , "equipment"  )+            , (0x56, "egghead"  , "escapade"   )+            , (0x57, "eightball", "eskimo"     )+            , (0x58, "endorse"  , "everyday"   )+            , (0x59, "endow"    , "examine"    )+            , (0x5a, "enlist"   , "existence"  )+            , (0x5b, "erase"    , "exodus"     )+            , (0x5c, "escape"   , "fascinate"  )+            , (0x5d, "exceed"   , "filament"   )+            , (0x5e, "eyeglass" , "finicky"    )+            , (0x5f, "eyetooth" , "forever"    )+            , (0x60, "facial"   , "fortitude"  )+            , (0x61, "fallout"  , "frequency"  )+            , (0x62, "flagpole" , "gadgetry"   )+            , (0x63, "flatfoot" , "galveston"  )+            , (0x64, "flytrap"  , "getaway"    )+            , (0x65, "fracture" , "glossary"   )+            , (0x66, "framework", "gossamer"   )+            , (0x67, "freedom"  , "graduate"   )+            , (0x68, "frighten" , "gravity"    )+            , (0x69, "gazelle"  , "guitarist"  )+            , (0x6a, "geiger"   , "hamburger"  )+            , (0x6b, "glitter"  , "hamilton"   )+            , (0x6c, "glucose"  , "handiwork"  )+            , (0x6d, "goggles"  , "hazardous"  )+            , (0x6e, "goldfish" , "headwaters" )+            , (0x6f, "gremlin"  , "hemisphere" )+            , (0x70, "guidance" , "hesitate"   )+            , (0x71, "hamlet"   , "hideaway"   )+            , (0x72, "highchair", "holiness"   )+            , (0x73, "hockey"   , "hurricane"  )+            , (0x74, "indoors"  , "hydraulic"  )+            , (0x75, "indulge"  , "impartial"  )+            , (0x76, "inverse"  , "impetus"    )+            , (0x77, "involve"  , "inception"  )+            , (0x78, "island"   , "indigo"     )+            , (0x79, "jawbone"  , "inertia"    )+            , (0x7a, "keyboard" , "infancy"    )+            , (0x7b, "kickoff"  , "inferno"    )+            , (0x7c, "kiwi"     , "informant"  )+            , (0x7d, "klaxon"   , "insincere"  )+            , (0x7e, "locale"   , "insurgent"  )+            , (0x7f, "lockup"   , "integrate"  )+            , (0x80, "merit"    , "intention"  )+            , (0x81, "minnow"   , "inventive"  )+            , (0x82, "miser"    , "istanbul"   )+            , (0x83, "mohawk"   , "jamaica"    )+            , (0x84, "mural"    , "jupiter"    )+            , (0x85, "music"    , "leprosy"    )+            , (0x86, "necklace" , "letterhead" )+            , (0x87, "neptune"  , "liberty"    )+            , (0x88, "newborn"  , "maritime"   )+            , (0x89, "nightbird", "matchmaker" )+            , (0x8a, "oakland"  , "maverick"   )+            , (0x8b, "obtuse"   , "medusa"     )+            , (0x8c, "offload"  , "megaton"    )+            , (0x8d, "optic"    , "microscope" )+            , (0x8e, "orca"     , "microwave"  )+            , (0x8f, "payday"   , "midsummer"  )+            , (0x90, "peachy"   , "millionaire")+            , (0x91, "pheasant" , "miracle"    )+            , (0x92, "physique" , "misnomer"   )+            , (0x93, "playhouse", "molasses"   )+            , (0x94, "pluto"    , "molecule"   )+            , (0x95, "preclude" , "montana"    )+            , (0x96, "prefer"   , "monument"   )+            , (0x97, "preshrunk", "mosquito"   )+            , (0x98, "printer"  , "narrative"  )+            , (0x99, "prowler"  , "nebula"     )+            , (0x9a, "pupil"    , "newsletter" )+            , (0x9b, "puppy"    , "norwegian"  )+            , (0x9c, "python"   , "october"    )+            , (0x9d, "quadrant" , "ohio"       )+            , (0x9e, "quiver"   , "onlooker"   )+            , (0x9f, "quota"    , "opulent"    )+            , (0xa0, "ragtime"  , "orlando"    )+            , (0xa1, "ratchet"  , "outfielder" )+            , (0xa2, "rebirth"  , "pacific"    )+            , (0xa3, "reform"   , "pandemic"   )+            , (0xa4, "regain"   , "pandora"    )+            , (0xa5, "reindeer" , "paperweight")+            , (0xa6, "rematch"  , "paragon"    )+            , (0xa7, "repay"    , "paragraph"  )+            , (0xa8, "retouch"  , "paramount"  )+            , (0xa9, "revenge"  , "passenger"  )+            , (0xaa, "reward"   , "pedigree"   )+            , (0xab, "rhythm"   , "pegasus"    )+            , (0xac, "ribcage"  , "penetrate"  )+            , (0xad, "ringbolt" , "perceptive" )+            , (0xae, "robust"   , "performance")+            , (0xaf, "rocker"   , "pharmacy"   )+            , (0xb0, "ruffled"  , "phonetic"   )+            , (0xb1, "sailboat" , "photograph" )+            , (0xb2, "sawdust"  , "pioneer"    )+            , (0xb3, "scallion" , "pocketful"  )+            , (0xb4, "scenic"   , "politeness" )+            , (0xb5, "scorecard", "positive"   )+            , (0xb6, "scotland" , "potato"     )+            , (0xb7, "seabird"  , "processor"  )+            , (0xb8, "select"   , "provincial" )+            , (0xb9, "sentence" , "proximate"  )+            , (0xba, "shadow"   , "puberty"    )+            , (0xbb, "shamrock" , "publisher"  )+            , (0xbc, "showgirl" , "pyramid"    )+            , (0xbd, "skullcap" , "quantity"   )+            , (0xbe, "skydive"  , "racketeer"  )+            , (0xbf, "slingshot", "rebellion"  )+            , (0xc0, "slowdown" , "recipe"     )+            , (0xc1, "snapline" , "recover"    )+            , (0xc2, "snapshot" , "repellent"  )+            , (0xc3, "snowcap"  , "replica"    )+            , (0xc4, "snowslide", "reproduce"  )+            , (0xc5, "solo"     , "resistor"   )+            , (0xc6, "southward", "responsive" )+            , (0xc7, "soybean"  , "retraction" )+            , (0xc8, "spaniel"  , "retrieval"  )+            , (0xc9, "spearhead", "retrospect" )+            , (0xca, "spellbind", "revenue"    )+            , (0xcb, "spheroid" , "revival"    )+            , (0xcc, "spigot"   , "revolver"   )+            , (0xcd, "spindle"  , "sandalwood" )+            , (0xce, "spyglass" , "sardonic"   )+            , (0xcf, "stagehand", "saturday"   )+            , (0xd0, "stagnate" , "savagery"   )+            , (0xd1, "stairway" , "scavenger"  )+            , (0xd2, "standard" , "sensation"  )+            , (0xd3, "stapler"  , "sociable"   )+            , (0xd4, "steamship", "souvenir"   )+            , (0xd5, "sterling" , "specialist" )+            , (0xd6, "stockman" , "speculate"  )+            , (0xd7, "stopwatch", "stethoscope")+            , (0xd8, "stormy"   , "stupendous" )+            , (0xd9, "sugar"    , "supportive" )+            , (0xda, "surmount" , "surrender"  )+            , (0xdb, "suspense" , "suspicious" )+            , (0xdc, "sweatband", "sympathy"   )+            , (0xdd, "swelter"  , "tambourine" )+            , (0xde, "tactics"  , "telephone"  )+            , (0xdf, "talon"    , "therapist"  )+            , (0xe0, "tapeworm" , "tobacco"    )+            , (0xe1, "tempest"  , "tolerance"  )+            , (0xe2, "tiger"    , "tomorrow"   )+            , (0xe3, "tissue"   , "torpedo"    )+            , (0xe4, "tonic"    , "tradition"  )+            , (0xe5, "topmost"  , "travesty"   )+            , (0xe6, "tracker"  , "trombonist" )+            , (0xe7, "transit"  , "truncated"  )+            , (0xe8, "trauma"   , "typewriter" )+            , (0xe9, "treadmill", "ultimate"   )+            , (0xea, "trojan"   , "undaunted"  )+            , (0xeb, "trouble"  , "underfoot"  )+            , (0xec, "tumor"    , "unicorn"    )+            , (0xed, "tunnel"   , "unify"      )+            , (0xee, "tycoon"   , "universe"   )+            , (0xef, "uncut"    , "unravel"    )+            , (0xf0, "unearth"  , "upcoming"   )+            , (0xf1, "unwind"   , "vacancy"    )+            , (0xf2, "uproot"   , "vagabond"   )+            , (0xf3, "upset"    , "vertigo"    )+            , (0xf4, "upshot"   , "virginia"   )+            , (0xf5, "vapor"    , "visitor"    )+            , (0xf6, "village"  , "vocalist"   )+            , (0xf7, "virus"    , "voyager"    )+            , (0xf8, "vulcan"   , "warranty"   )+            , (0xf9, "waffle"   , "waterloo"   )+            , (0xfa, "wallet"   , "whimsical"  )+            , (0xfb, "watchword", "wichita"    )+            , (0xfc, "wayside"  , "wilmington" )+            , (0xfd, "willow"   , "wyoming"    )+            , (0xfe, "woodlark" , "yesteryear" )+            , (0xff, "zulu"     , "yucatan"    )+            ]
+ test/Main.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ParallelListComp #-}++module Main where++++import           Data.Text.PgpWordlist.Internal.Tables++import qualified Data.ByteString.Lazy                  as BSL+import           Test.Tasty+import qualified Test.Tasty.HUnit                      as HU+import qualified Test.Tasty.QuickCheck                 as QC+import           Text.Printf++++main :: IO ()+main = defaultMain tree++++tree :: TestTree+tree = testGroup "Tests"+    [ allBytesConvertible+    , examplesBackAndForth+    , randomRoundtrips+    ]++++allBytesConvertible :: TestTree+allBytesConvertible = testGroup "All bytes have a corresponding word" tests+  where+    tests = map evenTest [0..] ++ map oddTest [0..]+    evenTest i = HU.testCase (printf "%02x is convertible to an even word" i)+                             (evenTestAssertion i)+    oddTest  i = HU.testCase (printf "%02x is convertible to an odd word" i)+                             (oddTestAssertion i)+    evenTestAssertion i = HU.assertBool "" (toEvenWord i `seq` True)+    oddTestAssertion  i = HU.assertBool "" (toOddWord  i `seq` True)++++examplesBackAndForth :: TestTree+examplesBackAndForth = testGroup "Conversion beween various examples" tests+  where+    tests = concat+        [ [ HU.testCase (printf "#%2d: %s -> PGP words" i (bsToHex bytes))+                        (HU.assertEqual "" (toText bytes) pgpWords)+          , HU.testCase (printf "#%2d: PGP words -> %s" i (bsToHex bytes))+                        (HU.assertEqual "" (Right bytes) (fromText pgpWords))+          ]+        | (bytes, pgpWords) <- testCases+        | i <- [0..] :: [Int]+        ]+    bsToHex :: BSL.ByteString -> String+    bsToHex = concatMap (printf "%02x") . BSL.unpack++    testCases = map (\(x,y) -> (BSL.pack x, y))+        [ ( [0xce, 0xe7, 0x1c, 0xbf, 0xe6, 0x59, 0x49, 0x48]+          , "spyglass truncated befriend rebellion tracker examine deckhand dictator"+          )+        , ( [0x06, 0x7a, 0xd2, 0xfc, 0xb7, 0x74, 0x48, 0x18]+          , "afflict infancy standard wilmington seabird hydraulic deadbolt borderline"+          )+        , ( [0xb3, 0xc6, 0x75, 0x64, 0xa7, 0x9e, 0x0a, 0xbb]+          , "scallion responsive indulge getaway repay onlooker allow publisher"+          )+        , ( [0x84, 0x98, 0xd3, 0x33, 0xf1, 0xe4, 0x2a, 0x92]+          , "mural narrative stapler concurrent unwind tradition brickyard misnomer"+          )+        , ( [0xb2, 0x8d, 0x47, 0xf8, 0x8c, 0x57, 0xb5, 0x09]+          , "sawdust microscope dashboard warranty offload eskimo scorecard applicant"+          )+        , ( [0x2e, 0x26, 0xf1, 0x24, 0x65, 0xb9, 0xae, 0xd7]+          , "buzzard caretaker unwind capricorn fracture proximate robust stethoscope"+          )+        , ( [0x72, 0x19, 0x07, 0xf2, 0xca, 0x6f, 0xe4, 0xee]+          , "highchair bottomless ahead vagabond spellbind hemisphere tonic universe"+          )+        , ( [0x52, 0x8f, 0x47, 0x3a, 0xbd, 0xf3, 0x64, 0x5b]+          , "dupont midsummer dashboard corrosion skullcap vertigo flytrap exodus"+          )+        , ( [0xe9, 0x75, 0x73, 0x8c, 0x03, 0xc1, 0x49, 0x7f]+          , "treadmill impartial hockey megaton acme recover deckhand integrate"+          )+        , ( [0xfd, 0xce, 0xd3, 0x8f, 0xfa, 0x3d, 0xd8, 0xf2]+          , "willow sardonic stapler midsummer wallet crucifix stormy vagabond"+          )+        , ( [0x2d, 0x56, 0x8c, 0x11, 0x95, 0x47, 0x4f, 0x6f]+          , "button escapade offload babylon preclude determine dropper hemisphere"+          )+        , ( [0xe4, 0x05, 0x46, 0xfe, 0xc9, 0xf4, 0x41, 0x9a]+          , "tonic almighty cubic yesteryear spearhead virginia cranky newsletter"+          )+        , ( [0xb2, 0x68, 0xfc, 0xe4, 0x2c, 0xf5, 0xe5, 0x04]+          , "sawdust gravity wayside tradition burbank visitor topmost alkali"+          )+        , ( [0xb4, 0xfb, 0xec, 0xe3, 0xc8, 0xe4, 0x29, 0xa5]+          , "scenic wichita tumor torpedo spaniel tradition breakup paperweight"+          )+        , ( [0xbb, 0x96, 0x68, 0xc6, 0x33, 0x11, 0x4f, 0x7c]+          , "shamrock monument frighten responsive chisel babylon dropper informant"+          )+        , ( [0x51, 0x5e, 0x7f, 0xe3, 0x1b, 0xc0, 0x6f, 0xc7]+          , "drunken finicky lockup torpedo beeswax recipe gremlin retraction"+          )+        ]++randomRoundtrips :: TestTree+randomRoundtrips = makeGroup tests+  where+    makeGroup = localOption (QC.QuickCheckMaxSize 1024)+              . testGroup "Random roundtrips"+    tests = [ QC.testProperty "Bytes -> PGP words -> Bytes" $+                \bytes -> let bs = BSL.pack bytes+                          in  fromText (toText bs) == Right bs+            ]