packages feed

crypto-enigma 0.0.2.5 → 0.0.2.6

raw patch · 8 files changed

+282/−75 lines, 8 filesdep +QuickCheckdep +mtldep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: QuickCheck, mtl

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Crypto.Enigma: instance GHC.Show.Show Crypto.Enigma.EnigmaError
+ Crypto.Enigma: message :: String -> String
- Crypto.Enigma.Display: showEnigmaEncoding :: EnigmaConfig -> String -> String
+ Crypto.Enigma.Display: showEnigmaEncoding :: EnigmaConfig -> Message -> String

Files

CHANGELOG.md view
@@ -1,5 +1,14 @@ See also: the list of [code releases] and [closed milestones]. +### [0.0.2.6]++* Add [QuickCheck](https://hackage.haskell.org/package/QuickCheck) tests.+* Remove (disabled) assertions from [`configEnigma`](https://hackage.haskell.org/package/crypto-enigma/docs/Crypto-Enigma.html#v:configEnigma)+  and fail with an error when bad arguments are given.+* Convert all strings provided as `Message` arguments to valid machine input+  (see [`message`](https://hackage.haskell.org/package/crypto-enigma/docs/Crypto-Enigma.html#v:message)).++ ### [0.0.2.5]  * Test and abandon [Travis CI Hackage deployment](http://docs.travis-ci.com/user/deployment/hackage/).@@ -56,6 +65,7 @@ [build checks]: https://travis-ci.org/orome/crypto-enigma-hs/branches [code releases]: https://github.com/orome/crypto-enigma-hs/releases [closed milestones]: https://github.com/orome/crypto-enigma-hs/milestones?state=closed+[0.0.2.6]: https://github.com/orome/crypto-enigma-hs/releases/tag/0.0.2.6 [0.0.2.5]: https://github.com/orome/crypto-enigma-hs/releases/tag/0.0.2.5 [0.0.2.4]: https://github.com/orome/crypto-enigma-hs/releases/tag/0.0.2.4 [0.0.2.3]: https://github.com/orome/crypto-enigma-hs/releases/tag/0.0.2.3
Crypto/Enigma.hs view
@@ -50,18 +50,24 @@         -- * Encoding         -- | Encoding messages.         Message,+        message,         enigmaEncoding ) where  import           Control.Arrow import           Control.Exception      (assert)+import           Control.Monad          (unless)+import           Control.Monad.Except import           Control.Applicative import           Data.Monoid import           Data.List import           Data.List.Split        (splitOn)+--import           Data.Ix                (inRange) import qualified Data.Map               as M import           Data.Maybe import           Text.Printf            (printf)+import           Data.Char              (toUpper)+import           Data.String.Utils       (replace)  import           Crypto.Enigma.Utils --import Data.Map (Map)@@ -81,9 +87,12 @@ -- TBD - Note how this implementation differs by preserving all letters and full mappings so they can be examined. <<< -- REV - Have lists and display omit plugboard stage if not used or present; distinguishing non-use and absence? -- REV - Show degenerate case in list and display examples?+-- REV - Add and use _make_valid... and _is_valid... from Python version? <<<+-- ASK - Retina figures? <<<   + -- Enigma mechanics ==========================================================  @@ -160,7 +169,7 @@     where         c = find ((== n).name) comps         plug [p1,p2] = map (\ch -> if ch == p1 then p2 else if ch == p2 then p1 else ch)-        plug _       = id       -- Anything but a .-separated pair will have no effect+        plug _       = id       -- Anything but a .-separated pair will have no effect (configEnigma assertion)   -- Machine configurations and transitions ------------------------------------@@ -290,10 +299,10 @@ windows :: EnigmaConfig -> String windows ec = reverse $ tail.init $ windowLetter ec <$> (stages ec) --- REV - Add assertion that last components' is in reflectors; all of head.tail components' are in rotors?  <<<+-- REV - Add check that last components' is in reflectors; all of head.tail components' are in rotors?  <<< -- REV - Add checks for historical combinations of machine elements?--- | A (safe public, <https://wiki.haskell.org/Smart_constructors "smart">) constructor that does validation and takes a conventional specification as input, in the---   form of four strings:+-- | A (safe public, <https://wiki.haskell.org/Smart_constructors "smart">) constructor that does validation and takes+--   a conventional specification as input, in the form of four strings: -- --   * The rotor 'Name's, separated by dashes (e.g. @\"C-V-I-II\"@); see 'Name'. --   * The letters visible at the windows (e.g. @\"MQR\"@); see 'windows'.@@ -306,16 +315,25 @@ --   Validation is permissive, allowing for ahistorical collections and numbers of rotors (including reflectors --   at the rotor stage, and trivial degenerate machines; --   e.g., @configEnigma "-" \"A\" "" "01"@), and any number of (non-contradictory) plugboard wirings (including none).+--   Invalid arguments result in an error. configEnigma :: String -> String -> String -> String -> EnigmaConfig-configEnigma rots winds plug rngs = assert ((and $ (==(length components')) <$> [length winds', length rngs']) &&-                                            (and $ [(>=1),(<=26)] <*> rngs') &&-                                            (and $ (`elem` letters) <$> winds') &&-                                            (plug == "" || ((and $ (==2).length <$> splitOn "." plug) &&-                                                            (and $ (`elem` letters) <$> filter (/='.') plug) &&-                                                            ((\s -> s == nub s) $ filter (/='.') plug))-                                            ) &&-                                            (and $ (`M.member` comps) <$> tail components'))-        EnigmaConfig {+configEnigma rots winds plug rngs = case runExcept (configEnigmaExcept rots winds plug rngs) of+        Right cfg  -> cfg+        Left err -> error (show err)++-- A safe (total) constructor; not currently exposed+configEnigmaExcept :: String -> String -> String -> String -> Except EnigmaError EnigmaConfig+configEnigmaExcept rots winds plug rngs = do+        unless (and $ (==(length components')) <$> [length winds', length rngs']) (throwError BadNumbers)+        unless (and $ [(>=1),(<=26)] <*> rngs') (throwError (BadRotors rngs))+        unless (and $ (`elem` letters) <$> winds') (throwError (BadWindows winds))+        unless (plug `elem` ["~",""," "] ||+                       ((and $ (==2).length <$> splitOn "." plug) &&+                        (and $ (`elem` letters) <$> filter (/='.') plug) &&+                        ((\s -> s == nub s) $ filter (/='.') plug))+               ) (throwError (BadPlugs plug))+        unless (and $ (`M.member` comps) <$> tail components') (throwError (BadRotors rots))+        return EnigmaConfig {                 components = components',                 positions = zipWith (\w r -> (mod (numA0 w - r + 1) 26) + 1) winds' rngs',                 rings = rngs'@@ -327,7 +345,22 @@         winds' = "A" ++ reverse winds ++ "A"         components' = reverse $ splitOn "-" $ rots ++ "-" ++ plug +-- Errors for use in configEnigmaExcept+data EnigmaError = BadNumbers+                 | BadRings String+                 | BadWindows String+                 | BadPlugs String+                 | BadRotors String+                 | MiscError String +instance Show EnigmaError where+        show BadNumbers = "Numbers of windows, ring settings, and components don't match"+        show (BadRings s) = "Bad ring settings: " ++ s+        show (BadWindows s) = "Bad windows: " ++ s+        show (BadPlugs s) = "Bad plugboard : " ++ s+        show (BadRotors s) = "Bad rotors : " ++ s+        show (MiscError s) = s+ -- TBD - Some checking, e.g. that four "words" have been provided? -- | Read the elements of a conventional specification (see 'configEnigma') joined by spaces into a single string. --@@ -336,7 +369,11 @@ --   >>> cfg == cfg' --   True instance Read EnigmaConfig where-        readsPrec _ i = [(configEnigma c w s r, "")] where [c, w, s, r] = words i+        readsPrec _ i = case runExcept (configEnigmaExcept c w s r) of+            Right cfg  -> [(cfg, "")]+            Left err -> [] -- Looses error information, but conforms to specification of 'readsPrec' in 'Read'+          where [c, w, s, r] = words i+--        readsPrec _ i = [(configEnigma c w s r, "")] where [c, w, s, r] = words i  -- | Show the elements of a conventional specification (see 'configEnigma') joined by spaces into a single string. --@@ -472,12 +509,6 @@ enigmaMapping :: EnigmaConfig -> Mapping enigmaMapping ec = last $ enigmaMappingList ec  -- The final stage's progressive encoding --- TBD - Keep? Not enforced except by assertion in 'enigmaEncoding', and by substitution of ' ' in showEnigmaConfigS--- | A valid keyboard entry into an Enigma machine: a string of uppercase characters.-type Message = String---- TBD - Either keep assertion enabled or replace with if/else or other enforcement or check <<<--- REV - Move preprocessing back; move assertion out? -- | Encode a 'Message' using a given (starting) machine configuration, by 'step'ping the configuration prior to --   processing each character of the message. This produces a new configuration (with new 'positions' only) --   for encoding each character, which serves as the "starting" configuration for subsequent@@ -489,11 +520,46 @@ --   The details of this encoding and its relationship to stepping from one configuration to another are --   illustrated in "Crypto.Enigma.Display#showEnigmaOperationEG". -----   Note that because of the way the Enigma machine is designed, it is always the case that+--   Note that because of the way the Enigma machine is designed, it is always the case (provided that 'msg' is+--   all uppercase letters) that -- --   prop> enigmaEncoding cfg (enigmaEncoding cfg msg) == msg enigmaEncoding :: EnigmaConfig -> Message -> String-enigmaEncoding ec msg = assert (all (`elem` letters) msg) $+enigmaEncoding ec str =         -- The encoding of a string is the sequence encodings of each character         -- performed by sequentially stepped configurations (preceded by a step)-        zipWith encode (enigmaMapping <$> cfgs) msg where cfgs = iterate step (step ec)+        zipWith encode (enigmaMapping <$> cfgs) (message str) where cfgs = iterate step (step ec)++++-- REV - Decide what (if any) functions should force String->Message and which (if any) should requre as arg <<<+--       (Currently all force internally).+-- TBD - Fold message into message' and rename message; require Message for some funcs (list); check docs <<<+++-- Message entry -------------------------------------------------------------++-- | A 'String', to which 'message' is applied by functions taking it as an argument.+type Message = String++-- | Convert a 'String' to valid Enigma machine input: replace any symbols for which there are standard Kriegsmarine+--   substitutions, remove any remaining non letter characters, and convert to uppercase. This function is applied+--   automatically to 'Message' arguments for functions defined here.+message :: String -> String+message s = filter (`elem` letters) $ foldl1 fmap (uncurry replace <$> subs) $ toUpper <$> s+    where+        subs = [(" ",""),(".","X"),(",","Y"),("'","J"),(">","J"),("<","J"),("!","X"),+                ("?","UD"),("-","YY"),(":","XX"),("(","KK"),(")","KK"),+                ("1","YQ"),("2","YW"),("3","YE"),("4","YR"),("5","YT"),+                ("6","YZ"),("7","YU"),("8","YI"),("9","YO"),("0","YP")]++-- REV - Possible future version in which 'Message' is at type (and caller is responsible for making Message).+-- data Message = Message String deriving Show+--+-- message :: String -> Message+-- message s = Message (filter (`elem` letters) $ foldl1 fmap (uncurry replace <$> subs) $ toUpper <$> s)+--     where+--         subs = [(" ",""),(".","X"),(",","Y"),("'","J"),(">","J"),("<","J"),("!","X"),+--                 ("?","UD"),("-","YY"),(":","XX"),("(","KK"),(")","KK"),+--                 ("1","YQ"),("2","YW"),("3","YE"),("4","YR"),("5","YT"),+--                 ("6","YZ"),("7","YU"),("8","YI"),("9","YO"),("0","YP")]
Crypto/Enigma/Display.hs view
@@ -45,19 +45,6 @@  -- Helpers =================================================================== ---- Message entry ----------------------------------------------------------------- Some standard substitutions performed by (Kriegsmarine) operators-preproc :: String -> Message-preproc s = filter (`elem` ['A'..'Z']) $ foldl1 fmap (uncurry replace <$> subs) $ toUpper <$> s-    where-        subs = [(" ",""),(".","X"),(",","Y"),("'","J"),(">","J"),("<","J"),("!","X"),-                ("?","UD"),("-","YY"),(":","XX"),("(","KK"),(")","KK"),-                ("1","YQ"),("2","YW"),("3","YE"),("4","YR"),("5","YT"),-                ("6","YZ"),("7","YU"),("8","YI"),("9","YO"),("0","YP")]-- -- Message display -----------------------------------------------------------  -- TBD - Don't remove spaces (at least in showEnigmaOperation and instead put a blank line?)@@ -88,8 +75,8 @@  -- Preprocess a message and produce a configuration display for the starting configuration -- and for each character of the message, using the provided configuration display function.-showEnigmaOperation_ :: (EnigmaConfig -> Char -> String) -> EnigmaConfig -> String -> String-showEnigmaOperation_ df ec msg = unlines $ zipWith df (iterate step ec) (' ':(preproc msg))+showEnigmaOperation_ :: (EnigmaConfig -> Char -> String) -> EnigmaConfig -> Message -> String+showEnigmaOperation_ df ec str = unlines $ zipWith df (iterate step ec) (' ':(message str))   -- Configuration display -----------------------------------------------------@@ -106,11 +93,11 @@ -- --   shows the process of encoding of the letter __@\'K\'@__ to __@\'G\'@__. showEnigmaConfig :: EnigmaConfig -> Char -> String-showEnigmaConfig ec ch = fmt ch' (markedMapping (locCar ch' enc enc) enc)+showEnigmaConfig ec ch = fmt mch (markedMapping (locCar mch enc enc) enc)                                  (windows ec)                                  (reverse $ tail.init $ positions ec)     where-        ch' = if ch `elem` letters then ch else ' '+        mch = messageChar ch         enc = enigmaMapping ec         fmt ch e ws ps = printf "%s %s  %s  %s" lbl e ws ps'             where@@ -179,18 +166,18 @@ --   <<figs/configinternal.jpg>> showEnigmaConfigInternal :: EnigmaConfig -> Char -> String showEnigmaConfigInternal ec ch =-        unlines $ [fmt (if ch' == ' ' then "" else ch':" >") (markedMapping (head charLocs) letters) ' ' 0 ""] +++        unlines $ [fmt (if mch == ' ' then "" else mch:" >") (markedMapping (head charLocs) letters) ' ' 0 ""] ++                   (zipWith5 fmt (init <> reverse $ ["P"] ++ (show <$> (tail.init $ stages ec)) ++ ["R"])                                 (zipWith markedMapping (tail.init $ charLocs) (stageMappingList ec))                                 (" " ++ (reverse $ windows ec) ++ replicate (length $ positions ec) ' ')                                 ([0] ++ ((tail.init $ positions ec)) ++ replicate (length $ positions ec) 0 )                                 (components ec ++ (tail $ reverse $ components ec))                   ) ++-                  [fmt (if ch' == ' ' then "" else (encode (enigmaMapping ec) ch'):" <")+                  [fmt (if mch == ' ' then "" else (encode (enigmaMapping ec) mch):" <")                        (markedMapping (last charLocs) (enigmaMapping ec)) ' ' 0 ""]     where-        ch' = if ch `elem` letters then ch else ' '-        charLocs = zipWith (locCar ch')+        mch = messageChar ch+        charLocs = zipWith (locCar mch)                            ([letters] ++ stageMappingList ec ++ [enigmaMapping ec])                            ([letters] ++ enigmaMappingList ec ++ [enigmaMapping ec])         fmt l e w p n = printf "%3.3s %s  %s  %s  %s" l e (w:[]) p' n@@ -215,7 +202,7 @@ --   perform any encoding (as explained in 'step'). --   Note also that the second line of this display is the same as one displayed in the example for 'showEnigmaConfig'. showEnigmaOperation :: EnigmaConfig -> String -> String-showEnigmaOperation ec msg = showEnigmaOperation_ showEnigmaConfig ec msg+showEnigmaOperation ec str = showEnigmaOperation_ showEnigmaConfig ec str  -- | Show a schematic of an Enigma machine's internal configuration (see 'showEnigmaConfigInternal' for details) --   and for each subsequent configuration as it processes each letter of a message.@@ -267,17 +254,17 @@ --   perform any encoding (as explained in 'step'). Note also that the second block of this display is the same --   as one displayed in the example for 'showEnigmaConfigInternal', where it is explained in more detail. showEnigmaOperationInternal :: EnigmaConfig -> String -> String-showEnigmaOperationInternal ec msg = showEnigmaOperation_ showEnigmaConfigInternal ec msg+showEnigmaOperationInternal ec str = showEnigmaOperation_ showEnigmaConfigInternal ec str    -- Encoding display ========================================================== --- | Show the conventionally formatted encoding of a message by an (initial) Enigma machine configuration.+-- | Show the conventionally formatted encoding of a 'Message' by an (initial) Enigma machine configuration. -- --   >>> let cfg = configEnigma "c-β-V-VI-VIII" "CDTJ" "AE.BF.CM.DQ.HU.JN.LX.PR.SZ.VW" "05.16.05.12" --   >>> putStr $ showEnigmaEncoding cfg "FOLGENDES IST SOFORT BEKANNTZUGEBEN" --   RBBF PMHP HGCZ XTDY GAHG UFXG EWKB LKGJ-showEnigmaEncoding :: EnigmaConfig -> String -> String-showEnigmaEncoding ec msg = postproc $ enigmaEncoding ec (preproc msg)+showEnigmaEncoding :: EnigmaConfig -> Message -> String+showEnigmaEncoding ec str = postproc $ enigmaEncoding ec (message str) 
Crypto/Enigma/Utils.hs view
@@ -12,7 +12,7 @@  -- REV - Could make this [MsgChar] letters :: String-letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+letters = ['A'..'Z']  numA0 :: Char -> Int numA0 ch = ord ch - ord 'A'@@ -34,3 +34,10 @@ -- standard simple-substitution cypher encoding encode' :: String -> String -> String encode' e s = (encode e) <$> s+++-- Character restriction ----------------------------------------------------++-- If the character isn't in 'letters', treat it as blank (a special case for 'encode' and other functions)+messageChar :: Char -> Char+messageChar ch = if ch `elem` letters then ch else ' '
README.md view
@@ -11,16 +11,63 @@ Currently support is only provided for those [machine models] in most widespread general use during the war years: the [I], [M3], and [M4]. -This is adapted, as an exerecise in learning Haskell, from an earlier learning project written in Mathematica.-It is my first Haskell program.+This is adapted, as an exercise in learning Haskell, from an earlier learning project written in Mathematica.+It is my first Haskell program. A [Python version] with substantially the same API, plus a command line interface, is+also available. +### Functionality++Perform [message encoding]:++    >>> enigmaEncoding (configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11") "KRIEG"+    "GOWNW"++    >>> let cfg = configEnigma "c-β-V-VI-VIII" "CDTJ" "AE.BF.CM.DQ.HU.JN.LX.PR.SZ.VW" "05.16.05.12"+    >>> putStr $ showEnigmaEncoding cfg "FOLGENDES IST SOFORT BEKANNTZUGEBEN"+    RBBF PMHP HGCZ XTDY GAHG UFXG EWKB LKGJ++Show [configuration details]:++    >>> let cfg = configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11"+    >>> putStr $ showEnigmaConfigInternal cfg 'K'+    K > ABCDEFGHIJK̲̅LMNOPQRSTUVWXYZ+      P YBCDFEGHIJZ̲̅PONMLQRSTXVWUAK         UX.MO.KZ.AY.EF.PL+      1 LORVFBQNGWKATHJSZPIYUDXEMC̲̅  Q  07  II+      2 BJY̲̅INTKWOARFEMVSGCUDPHZQLX  A  24  VIII+      3 ILHXUBZQPNVGKMCRTEJFADOYS̲̅W  F  16  V+      4 YDSKZPTNCHGQOMXAUWJ̲̅FBRELVI  L  10  γ+      R ENKQAUYWJI̲̅COPBLMDXZVFTHRGS         b+      4 PUIBWTKJZ̲̅SDXNHMFLVCGQYROAE         γ+      3 UFOVRTLCASMBNJWIHPYQEKZDXG̲̅         V+      2 JARTMLQ̲̅VDBGYNEIUXKPFSOHZCW         VIII+      1 LFZVXEINSOKAYHBRG̲̅CPMUDJWTQ         II+      P YBCDFEG̲̅HIJZPONMLQRSTXVWUAK         UX.MO.KZ.AY.EF.PL+    G < CMAWFEKLNVG̲̅HBIUYTXZQOJDRPS++Simulate [machine operation]:++    >>> let cfg = configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11"+    >>> putStr $ showEnigmaOperation cfg "KRIEG"+        OHNKJYSBTEDMLCARWPGIXZQUFV  LFAP  10 16 24 06+    K > CMAWFEKLNVG̲̅HBIUYTXZQOJDRPS  LFAQ  10 16 24 07+    R > HXETCUMASQNZGKRYJO̲̅IDFWVBPL  LFAR  10 16 24 08+    I > FGRJUABYW̲̅DZSXVQTOCLPENIMHK  LFAS  10 16 24 09+    E > SJWYN̲̅UZPQBVXRETHIMAOFKCLDG  LFAT  10 16 24 10+    G > EOKPAQW̲̅JLHCISTBDFVMNXRGUZY  LFAU  10 16 24 11++### Limitations+ Note that the correct display of some characters used to represent components (thin Naval rotors) assumes support for Unicode, while some aspects of the display of machine state depend on support for combining Unicode. This is a [known limitation](https://github.com/orome/crypto-enigma-hs/issues/10) that will be addressed in a future release. +### Documentation+ Full [documentation] — for the latest [release version] — is available on Hackage. +### Alternatives+ For other Haskell Enigma machines see:  * [enigma-hs](https://github.com/kc1212/enigma-hs)@@ -28,8 +75,6 @@ * [enigma.lhs](https://gist.github.com/erantapaa/f071bc3f58d017f9280a) * [henigma](https://github.com/erantapaa/henigma) ----- ### Development status  [![Build Status](https://travis-ci.org/orome/crypto-enigma-hs.svg?branch=develop)](https://travis-ci.org/orome/crypto-enigma-hs/branches)@@ -38,11 +83,16 @@ [development version] will work. More detail about planned releases and activities can be found the list of scheduled [milestones] and in the list of [open issues]. +[Python version]: https://pypi.python.org/pypi/crypto-enigma [documentation]: https://hackage.haskell.org/package/crypto-enigma [release version]: https://github.com/orome/crypto-enigma-hs/tree/hackage [development version]: https://github.com/orome/crypto-enigma-hs/tree/develop [milestones]: https://github.com/orome/crypto-enigma-hs/milestones [open issues]: https://github.com/orome/crypto-enigma-hs/issues++[message encoding]: https://hackage.haskell.org/package/crypto-enigma/docs/Crypto-Enigma.html#v:enigmaEncoding+[configuration details]: https://hackage.haskell.org/package/crypto-enigma/docs/Crypto-Enigma-Display.html#v:showEnigmaConfigInternal+[machine operation]: https://hackage.haskell.org/package/crypto-enigma/docs/Crypto-Enigma-Display.html#v:showEnigmaOperation  [machine models]: http://www.cryptomuseum.com/crypto/enigma/tree.htm [I]: http://www.cryptomuseum.com/crypto/enigma/i/index.htm
crypto-enigma.cabal view
@@ -3,7 +3,7 @@ -- PVP summary:         +-+------- breaking API changes --                      | | +----- non-breaking API additions --                      | | | +--- code changes with no API change-version:                0.0.2.5+version:                0.0.2.6 synopsis:               An Enigma machine simulator with display. description:            The crypto-enigma package is an Enigma machine simulator                         with rich display and machine state details.@@ -41,11 +41,11 @@         location:       git://github.com/orome/crypto-enigma-hs.git         branch:         develop -source-repository this-        type:           git-        location:       git://github.com/orome/crypto-enigma-hs.git-        branch:         hackage-        tag:            0.0.2.5+--source-repository this+--        type:           git+--        location:       git://github.com/orome/crypto-enigma-hs.git+--        branch:         hackage+--        tag:            0.0.2.6  library     -- default-extensions: Trustworthy@@ -56,8 +56,19 @@     build-depends:      base >=4.8.1.0 && <4.9,                         containers >=0.5.5.1,                         split >=0.2.2,+                        mtl >=2.2,                         MissingH >=1.3.0.1     -- hs-source-dirs:+    default-language:   Haskell2010++test-suite crypto-enigma-check+    type:               exitcode-stdio-1.0+    hs-source-dirs:     test+    main-is:            Check.hs+    build-depends:      base >=4.8.1.0 && <4.9,+                        QuickCheck >=2.8,+                        crypto-enigma+    -- ghc-options:        -threaded -rtsopts -with-rtsopts=-N     default-language:   Haskell2010  test-suite crypto-enigma-test
+ test/Check.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+module Main where++-- import Test.HUnit+import Test.QuickCheck+import Test.QuickCheck.Test (isSuccess)+import Control.Monad (unless, replicateM)+import System.Exit+import Data.List (sort,intercalate)+import Text.Printf (printf)++import Crypto.Enigma+import Crypto.Enigma.Display++{-# ANN module ("HLint: ignore Use mappend"::String) #-}++capitals = elements ['A'..'Z']+anychars = elements (['A'..'Z'] ++ " .,'><!?-:()1234567890" ++ ['a'..'z'] ++  "$@&^=|~")++-- ASK - Can I provide the number of stages as an argument (parameterize) ? <<<+-- TBD - Consistent formatting and labeling of tests <<<+instance Arbitrary EnigmaConfig where+        arbitrary = do+                nc <- choose (3,4)  -- This could cover a wider range+                ws <- replicateM nc capitals+                cs <- replicateM nc (elements rotors)+                uk <- elements reflectors+                rs <- replicateM nc (choose (1,26))+--                 Positive x <- arbitrary+--                 Positive y <- arbitrary+                return $ configEnigma (intercalate "-" (uk:cs))+                                      ws+                                      "UX.MO.KZ.AY.EF.PL"  -- TBD - Generate plugboard and test <<<+                                      (intercalate "." $ (printf "%02d") <$> (rs :: [Int]))++-- REV - Requires TypeSynonymInstances, FlexibleInstances; find a better way <<<+instance Arbitrary String where+        arbitrary = do+          l <- choose (1,200)+          replicateM l anychars+++prop_ReadShowIsNoOp :: EnigmaConfig -> Bool+prop_ReadShowIsNoOp cfg = cfg == (read (show cfg) :: EnigmaConfig)++prop_EncodeEncodeIsMessage :: EnigmaConfig -> String -> Bool+prop_EncodeEncodeIsMessage cfg str = enigmaEncoding cfg (enigmaEncoding cfg str) == message str++prop_NoEncodeIsMessage :: String -> Bool+prop_NoEncodeIsMessage str = enigmaEncoding (configEnigma "----" "AAAA" "" "01.01.01.01") str == message str++main :: IO ()+main = do+        putStrLn "\n==== QuickCheck Tests"+        putStrLn "\nExample EnigmaConfig test values:"+        sample (arbitrary :: Gen EnigmaConfig)+        sample (arbitrary :: Gen EnigmaConfig)+        putStrLn "\nExample Message test values:"+        sample (arbitrary :: Gen Message)+        putStrLn "\nQuickCheck - read.show is id:"+        result <- verboseCheckWithResult stdArgs { maxSuccess = 10, chatty = True } prop_ReadShowIsNoOp+        unless (isSuccess result) exitFailure+        result <- quickCheckWithResult stdArgs { maxSuccess = 200, chatty = True } prop_ReadShowIsNoOp+        unless (isSuccess result) exitFailure+        putStrLn "\nQuickCheck - encoding of encoding is message:"+        result <- verboseCheckWithResult stdArgs { maxSuccess = 5, chatty = True } prop_EncodeEncodeIsMessage+        unless (isSuccess result) exitFailure+        result <- quickCheckWithResult stdArgs { maxSuccess = 100, chatty = True } prop_EncodeEncodeIsMessage+        unless (isSuccess result) exitFailure+        putStrLn "\nQuickCheck - no-op incoding is message:"+        result <- verboseCheckWithResult stdArgs { maxSuccess = 5, chatty = True } prop_NoEncodeIsMessage+        unless (isSuccess result) exitFailure+        result <- quickCheckWithResult stdArgs { maxSuccess = 100, chatty = True } prop_NoEncodeIsMessage+        unless (isSuccess result) exitFailure+        putStrLn "\n"
test/Test.hs view
@@ -3,13 +3,12 @@ import Test.HUnit import System.Exit import Data.List (sort)+ import Crypto.Enigma import Crypto.Enigma.Display  {-# ANN module ("HLint: ignore Use mappend"::String) #-} -- testRotorNames :: Test testRotorNames = TestCase $ assertEqual "Invalid rotor list"         (sort ["I","II","III","IV","V","VI","VII","VIII","\946","\947"])@@ -36,7 +35,7 @@         cfg         (read (show cfg) :: EnigmaConfig) -testPlugboardIsOwnInverse :: Name -> Message -> Test+testPlugboardIsOwnInverse :: Name -> String -> Test testPlugboardIsOwnInverse plug msg = TestCase $ assertEqual ("Plugboard is not self-inverse for " ++ plug)         msg         (enigmaEncoding (configEnigma "----" "AAAA" plug "01.01.01.01") msg)@@ -44,7 +43,7 @@ testWindowsStepping :: EnigmaConfig -> [String] -> Test testWindowsStepping cfg windss = TestCase $ assertEqual ("Incorrect series of window letters for " ++ show cfg)         windss-        (take 500 $ map windows $ iterate step cfg)+        (take 500 . map windows $ iterate step cfg)  testStageMappings :: EnigmaConfig -> [Mapping] -> Test testStageMappings cfg mps = TestCase $ assertEqual ("Incorrect mappings for " ++ show cfg)@@ -52,7 +51,7 @@         (stageMappingList cfg)  -- See: http://www.enigma.hoerenberg.com/index.php?cat=The%20U534%20messages-testHistoricalMessage :: String -> EnigmaConfig -> Message -> String -> Test+testHistoricalMessage :: String -> EnigmaConfig -> String -> String -> Test testHistoricalMessage hmn cfg msg enc = TestCase $ assertEqual ("Error processing historical message " ++ hmn)         enc         (enigmaEncoding cfg msg)@@ -77,7 +76,7 @@         sop         (lines $ showEnigmaOperationInternal cfg msg) -testShowEncoding :: EnigmaConfig -> Message -> [String] -> Test+testShowEncoding :: EnigmaConfig -> String -> [String] -> Test testShowEncoding cfg msg senc = TestCase $ assertEqual ("Incorrect encoding display for " ++ show cfg)         senc         (lines $ showEnigmaEncoding cfg msg)@@ -88,15 +87,16 @@  main :: IO () main = do+        putStrLn "\n==== HUnit Tests"         putStrLn "\nComponent names:"         putStrLn $ " Rotors:\t" ++ (show rotors)         putStrLn $ " Reflectors:\t" ++ (show reflectors)         putStrLn "\nInternals display test:"-        putStrLn $ showEnigmaConfigInternal (configEnigma "----" "AAAA" "~" "01.01.01.01") ' '+        putStrLn $ showEnigmaConfigInternal (configEnigma "----" "AAAA" "" "01.01.01.01") ' '         putStrLn "Operation display test:"         putStrLn $ showEnigmaOperation (configEnigma "----" "AAAA" "~" "01.01.10.01") ['A'..'Z']         putStrLn "Encoding display test:"-        putStrLn $ showEnigmaEncoding (configEnigma "----" "AAAA" "~" "01.01.10.01") (concatMap (replicate 8) ['A'..'Z'])+        putStrLn $ showEnigmaEncoding (configEnigma "----" "AAAA" " " "01.01.10.01") (concatMap (replicate 8) ['A'..'Z'])         results <- runTestTT $ TestList [                 testRotorNames,                 testReflectorNames,@@ -111,7 +111,7 @@                 testPlugboardIsOwnInverse "EL.MU.XZ.PW.HY.OF" "THESAMETHESAMETHESAMETHESAMETHESAMETHESAMETHESAMETHESAME",                 testStageMappings (configEnigma "c-γ-V-I-II" "LIAQ" "AI.JF.CM.DQ.HU.LX.PR.SZ" "03.01.04.22")                         ["IBMQEJGUAFKXCNORDPZTHVWLYS","DKATJFOIPXNWZCGQMBYRHVLESU","UFMHNPIOJGTYCQWRZBKAXVSDLE","MHKVFZDPSOEBIGXWUCNRTJYALQ","YDSKZPTNCHGQOMXAUWJFBRELVI","RDOBJNTKVEHMLFCWZAXGYIPSUQ","PUIBWTKJZSDXNHMFLVCGQYROAE","XLRGKENBMVCYASJHZTIUQDPOWF","TRMXZBJDGISYCEHFNPWKAVOULQ","CRNAXFOUHEBWQKGIPTYDZVLJSM","IBMQEJGUAFKXCNORDPZTHVWLYS"],-                testWindowsStepping (configEnigma "B-III-I-II" "AAA" "" "01.01.01.01")+                testWindowsStepping (configEnigma "B-III-I-II" "AAA" "~" "01.01.01")                         ["AAA","AAB","AAC","AAD","AAE","ABF","ABG","ABH","ABI","ABJ","ABK","ABL","ABM","ABN","ABO","ABP","ABQ","ABR","ABS","ABT","ABU","ABV","ABW","ABX","ABY","ABZ","ABA","ABB","ABC","ABD","ABE","ACF","ACG","ACH","ACI","ACJ","ACK","ACL","ACM","ACN","ACO","ACP","ACQ","ACR","ACS","ACT","ACU","ACV","ACW","ACX","ACY","ACZ","ACA","ACB","ACC","ACD","ACE","ADF","ADG","ADH","ADI","ADJ","ADK","ADL","ADM","ADN","ADO","ADP","ADQ","ADR","ADS","ADT","ADU","ADV","ADW","ADX","ADY","ADZ","ADA","ADB","ADC","ADD","ADE","AEF","AEG","AEH","AEI","AEJ","AEK","AEL","AEM","AEN","AEO","AEP","AEQ","AER","AES","AET","AEU","AEV","AEW","AEX","AEY","AEZ","AEA","AEB","AEC","AED","AEE","AFF","AFG","AFH","AFI","AFJ","AFK","AFL","AFM","AFN","AFO","AFP","AFQ","AFR","AFS","AFT","AFU","AFV","AFW","AFX","AFY","AFZ","AFA","AFB","AFC","AFD","AFE","AGF","AGG","AGH","AGI","AGJ","AGK","AGL","AGM","AGN","AGO","AGP","AGQ","AGR","AGS","AGT","AGU","AGV","AGW","AGX","AGY","AGZ","AGA","AGB","AGC","AGD","AGE","AHF","AHG","AHH","AHI","AHJ","AHK","AHL","AHM","AHN","AHO","AHP","AHQ","AHR","AHS","AHT","AHU","AHV","AHW","AHX","AHY","AHZ","AHA","AHB","AHC","AHD","AHE","AIF","AIG","AIH","AII","AIJ","AIK","AIL","AIM","AIN","AIO","AIP","AIQ","AIR","AIS","AIT","AIU","AIV","AIW","AIX","AIY","AIZ","AIA","AIB","AIC","AID","AIE","AJF","AJG","AJH","AJI","AJJ","AJK","AJL","AJM","AJN","AJO","AJP","AJQ","AJR","AJS","AJT","AJU","AJV","AJW","AJX","AJY","AJZ","AJA","AJB","AJC","AJD","AJE","AKF","AKG","AKH","AKI","AKJ","AKK","AKL","AKM","AKN","AKO","AKP","AKQ","AKR","AKS","AKT","AKU","AKV","AKW","AKX","AKY","AKZ","AKA","AKB","AKC","AKD","AKE","ALF","ALG","ALH","ALI","ALJ","ALK","ALL","ALM","ALN","ALO","ALP","ALQ","ALR","ALS","ALT","ALU","ALV","ALW","ALX","ALY","ALZ","ALA","ALB","ALC","ALD","ALE","AMF","AMG","AMH","AMI","AMJ","AMK","AML","AMM","AMN","AMO","AMP","AMQ","AMR","AMS","AMT","AMU","AMV","AMW","AMX","AMY","AMZ","AMA","AMB","AMC","AMD","AME","ANF","ANG","ANH","ANI","ANJ","ANK","ANL","ANM","ANN","ANO","ANP","ANQ","ANR","ANS","ANT","ANU","ANV","ANW","ANX","ANY","ANZ","ANA","ANB","ANC","AND","ANE","AOF","AOG","AOH","AOI","AOJ","AOK","AOL","AOM","AON","AOO","AOP","AOQ","AOR","AOS","AOT","AOU","AOV","AOW","AOX","AOY","AOZ","AOA","AOB","AOC","AOD","AOE","APF","APG","APH","API","APJ","APK","APL","APM","APN","APO","APP","APQ","APR","APS","APT","APU","APV","APW","APX","APY","APZ","APA","APB","APC","APD","APE","AQF","BRG","BRH","BRI","BRJ","BRK","BRL","BRM","BRN","BRO","BRP","BRQ","BRR","BRS","BRT","BRU","BRV","BRW","BRX","BRY","BRZ","BRA","BRB","BRC","BRD","BRE","BSF","BSG","BSH","BSI","BSJ","BSK","BSL","BSM","BSN","BSO","BSP","BSQ","BSR","BSS","BST","BSU","BSV","BSW","BSX","BSY","BSZ","BSA","BSB","BSC","BSD","BSE","BTF","BTG","BTH","BTI","BTJ","BTK","BTL","BTM","BTN","BTO","BTP","BTQ","BTR","BTS","BTT","BTU","BTV","BTW","BTX","BTY","BTZ","BTA","BTB","BTC","BTD","BTE","BUF","BUG","BUH","BUI","BUJ","BUK","BUL","BUM","BUN","BUO","BUP","BUQ","BUR","BUS","BUT","BUU","BUV","BUW","BUX","BUY","BUZ","BUA","BUB","BUC","BUD","BUE","BVF"],                 testWindowsStepping (configEnigma "c-γ-V-I-II" "LIAQ" "AI.JF.CM.DQ.HU.LX.PR.SZ" "03.01.04.22")                         ["LIAQ","LIAR","LIAS","LIAT","LIAU","LIAV","LIAW","LIAX","LIAY","LIAZ","LIAA","LIAB","LIAC","LIAD","LIAE","LIBF","LIBG","LIBH","LIBI","LIBJ","LIBK","LIBL","LIBM","LIBN","LIBO","LIBP","LIBQ","LIBR","LIBS","LIBT","LIBU","LIBV","LIBW","LIBX","LIBY","LIBZ","LIBA","LIBB","LIBC","LIBD","LIBE","LICF","LICG","LICH","LICI","LICJ","LICK","LICL","LICM","LICN","LICO","LICP","LICQ","LICR","LICS","LICT","LICU","LICV","LICW","LICX","LICY","LICZ","LICA","LICB","LICC","LICD","LICE","LIDF","LIDG","LIDH","LIDI","LIDJ","LIDK","LIDL","LIDM","LIDN","LIDO","LIDP","LIDQ","LIDR","LIDS","LIDT","LIDU","LIDV","LIDW","LIDX","LIDY","LIDZ","LIDA","LIDB","LIDC","LIDD","LIDE","LIEF","LIEG","LIEH","LIEI","LIEJ","LIEK","LIEL","LIEM","LIEN","LIEO","LIEP","LIEQ","LIER","LIES","LIET","LIEU","LIEV","LIEW","LIEX","LIEY","LIEZ","LIEA","LIEB","LIEC","LIED","LIEE","LIFF","LIFG","LIFH","LIFI","LIFJ","LIFK","LIFL","LIFM","LIFN","LIFO","LIFP","LIFQ","LIFR","LIFS","LIFT","LIFU","LIFV","LIFW","LIFX","LIFY","LIFZ","LIFA","LIFB","LIFC","LIFD","LIFE","LIGF","LIGG","LIGH","LIGI","LIGJ","LIGK","LIGL","LIGM","LIGN","LIGO","LIGP","LIGQ","LIGR","LIGS","LIGT","LIGU","LIGV","LIGW","LIGX","LIGY","LIGZ","LIGA","LIGB","LIGC","LIGD","LIGE","LIHF","LIHG","LIHH","LIHI","LIHJ","LIHK","LIHL","LIHM","LIHN","LIHO","LIHP","LIHQ","LIHR","LIHS","LIHT","LIHU","LIHV","LIHW","LIHX","LIHY","LIHZ","LIHA","LIHB","LIHC","LIHD","LIHE","LIIF","LIIG","LIIH","LIII","LIIJ","LIIK","LIIL","LIIM","LIIN","LIIO","LIIP","LIIQ","LIIR","LIIS","LIIT","LIIU","LIIV","LIIW","LIIX","LIIY","LIIZ","LIIA","LIIB","LIIC","LIID","LIIE","LIJF","LIJG","LIJH","LIJI","LIJJ","LIJK","LIJL","LIJM","LIJN","LIJO","LIJP","LIJQ","LIJR","LIJS","LIJT","LIJU","LIJV","LIJW","LIJX","LIJY","LIJZ","LIJA","LIJB","LIJC","LIJD","LIJE","LIKF","LIKG","LIKH","LIKI","LIKJ","LIKK","LIKL","LIKM","LIKN","LIKO","LIKP","LIKQ","LIKR","LIKS","LIKT","LIKU","LIKV","LIKW","LIKX","LIKY","LIKZ","LIKA","LIKB","LIKC","LIKD","LIKE","LILF","LILG","LILH","LILI","LILJ","LILK","LILL","LILM","LILN","LILO","LILP","LILQ","LILR","LILS","LILT","LILU","LILV","LILW","LILX","LILY","LILZ","LILA","LILB","LILC","LILD","LILE","LIMF","LIMG","LIMH","LIMI","LIMJ","LIMK","LIML","LIMM","LIMN","LIMO","LIMP","LIMQ","LIMR","LIMS","LIMT","LIMU","LIMV","LIMW","LIMX","LIMY","LIMZ","LIMA","LIMB","LIMC","LIMD","LIME","LINF","LING","LINH","LINI","LINJ","LINK","LINL","LINM","LINN","LINO","LINP","LINQ","LINR","LINS","LINT","LINU","LINV","LINW","LINX","LINY","LINZ","LINA","LINB","LINC","LIND","LINE","LIOF","LIOG","LIOH","LIOI","LIOJ","LIOK","LIOL","LIOM","LION","LIOO","LIOP","LIOQ","LIOR","LIOS","LIOT","LIOU","LIOV","LIOW","LIOX","LIOY","LIOZ","LIOA","LIOB","LIOC","LIOD","LIOE","LIPF","LIPG","LIPH","LIPI","LIPJ","LIPK","LIPL","LIPM","LIPN","LIPO","LIPP","LIPQ","LIPR","LIPS","LIPT","LIPU","LIPV","LIPW","LIPX","LIPY","LIPZ","LIPA","LIPB","LIPC","LIPD","LIPE","LIQF","LJRG","LJRH","LJRI","LJRJ","LJRK","LJRL","LJRM","LJRN","LJRO","LJRP","LJRQ","LJRR","LJRS","LJRT","LJRU","LJRV","LJRW","LJRX","LJRY","LJRZ","LJRA","LJRB","LJRC","LJRD","LJRE","LJSF","LJSG","LJSH","LJSI","LJSJ","LJSK","LJSL","LJSM","LJSN","LJSO","LJSP","LJSQ","LJSR","LJSS","LJST","LJSU","LJSV","LJSW","LJSX","LJSY","LJSZ","LJSA","LJSB","LJSC","LJSD","LJSE","LJTF","LJTG","LJTH","LJTI","LJTJ","LJTK","LJTL","LJTM","LJTN","LJTO","LJTP","LJTQ","LJTR","LJTS","LJTT","LJTU","LJTV","LJTW","LJTX","LJTY","LJTZ","LJTA","LJTB","LJTC","LJTD","LJTE","LJUF","LJUG","LJUH","LJUI","LJUJ","LJUK","LJUL","LJUM","LJUN","LJUO","LJUP","LJUQ","LJUR","LJUS","LJUT","LJUU","LJUV"],@@ -127,14 +127,14 @@                         (configEnigma "c-β-V-VI-VIII" (enigmaEncoding (configEnigma "c-β-V-VI-VIII" "NAEM" "AE.BF.CM.DQ.HU.JN.LX.PR.SZ.VW" "05.16.05.12") "QEOB") "AE.BF.CM.DQ.HU.JN.LX.PR.SZ.VW" "05.16.05.12")                         "KRKRALLEXXFOLGENDESISTSOFORTBEKANNTZUGEBENXXICHHABEFOLGELNBEBEFEHLERHALTENXXJANSTERLEDESBISHERIGXNREICHSMARSCHALLSJGOERINGJSETZTDERFUEHRERSIEYHVRRGRZSSADMIRALYALSSEINENNACHFOLGEREINXSCHRIFTLSCHEVOLLMACHTUNTERWEGSXABSOFORTSOLLENSIESAEMTLICHEMASSNAHMENVERFUEGENYDIESICHAUSDERGEGENWAERTIGENLAGEERGEBENXGEZXREICHSLEITEIKKTULPEKKJBORMANNJXXOBXDXMMMDURNHFKSTXKOMXADMXUUUBOOIEXKP"                         "LANOTCTOUARBBFPMHPHGCZXTDYGAHGUFXGEWKBLKGJWLQXXTGPJJAVTOCKZFSLPPQIHZFXOEBWIIEKFZLCLOAQJULJOYHSSMBBGWHZANVOIIPYRBRTDJQDJJOQKCXWDNBBTYVXLYTAPGVEATXSONPNYNQFUDBBHHVWEPYEYDOHNLXKZDNWRHDUWUJUMWWVIIWZXIVIUQDRHYMNCYEFUAPNHOTKHKGDNPSAKNUAGHJZSMJBMHVTREQEDGXHLZWIFUSKDQVELNMIMITHBHDBWVHDFYHJOQIHORTDJDBWXEMEAYXGYQXOHFDMYUXXNOJAZRSGHPLWMLRECWWUTLRTTVLBHYOORGLGOWUXNXHMHYFAACQEKTHSJW",-                testShowConfig (configEnigma "C-III-II-I" "XYZ" "MJ.NH.RF.PL.ZS.DC" "09.22.25.19") 'E'-                        "E > LEDCB\818\773ROYZTUAVWGQPFXJKMNSHI  XYZ  03 01 08",-                testShowConfig (configEnigma "A-I-II-III" "ABC" "OI.XC.QA.PL.FG.ER.TY" "02.02.25.25") ' '-                        "    ORWGIZDJEHQNVLASKBPXYMCTUF  ABC  26 04 05",+                testShowConfig (configEnigma "C-III-II-I" "XYZ" "MJ.NH.RF.PL.ZS.DC" "09.25.19") 'E'+                        "E > HEMVB\818\773GFAKZIWCQSXNTORYDLPUJ  XYZ  16 01 08",+                testShowConfig (configEnigma "A-I-II-III" "ABC" "OI.XC.QA.PL.FG.ER.TY" "02.07.25") ' '+                        "    YPLOUWXRMQTCIZDBJHVKESFGAN  ABC  26 22 05",                 testShowConfigIntenral (configEnigma "b-γ-V-VIII-II" "LFAQ" "UX.MO.KZ.AY.EF.PL" "03.17.04.11") 'K'                         ["K > ABCDEFGHIJK\818\773LMNOPQRSTUVWXYZ         ","  P YBCDFEGHIJZ\818\773PONMLQRSTXVWUAK         UX.MO.KZ.AY.EF.PL","  1 LORVFBQNGWKATHJSZPIYUDXEMC\818\773  Q  07  II","  2 BJY\818\773INTKWOARFEMVSGCUDPHZQLX  A  24  VIII","  3 ILHXUBZQPNVGKMCRTEJFADOYS\818\773W  F  16  V","  4 YDSKZPTNCHGQOMXAUWJ\818\773FBRELVI  L  10  \947","  R ENKQAUYWJI\818\773COPBLMDXZVFTHRGS         b","  4 PUIBWTKJZ\818\773SDXNHMFLVCGQYROAE         \947","  3 UFOVRTLCASMBNJWIHPYQEKZDXG\818\773         V","  2 JARTMLQ\818\773VDBGYNEIUXKPFSOHZCW         VIII","  1 LFZVXEINSOKAYHBRG\818\773CPMUDJWTQ         II","  P YBCDFEG\818\773HIJZPONMLQRSTXVWUAK         UX.MO.KZ.AY.EF.PL","G < CMAWFEKLNVG\818\773HBIUYTXZQOJDRPS         "],-                testShowConfigIntenral (configEnigma "A-I-II-III" "ABC" "OI.XC.QA.PL.FG.ER.TY" "02.02.25.25") ' '-                        ["    ABCDEFGHIJKLMNOPQRSTUVWXYZ         ","  P QBXDRGFHOJKPMNILAESYUVWCTZ         OI.XC.QA.PL.FG.ER.TY","  1 FHYLNPTRVJUAESCWGIQOMKXZBD  C  05  III","  2 HPFORUYIETQJZNDWKMVCSLBXGA  B  04  II","  3 KFLNGMHERWAOUPXZIYVTQBJCSD  A  26  I","  R EFKNABMZYWCXGDSRVPOUTQJLIH         A","  3 KVXZHBEGQWACFDLNUIYTMSJORP         I","  2 ZWTOICYAHLQVRNDBKEUJFSPXGM         II","  1 LYOZMAQBRJVDUETFSHNGKIPWCX         III","  P QBXDRGFHOJKPMNILAESYUVWCTZ         OI.XC.QA.PL.FG.ER.TY","    ORWGIZDJEHQNVLASKBPXYMCTUF         "],+                testShowConfigIntenral (configEnigma "A-I-II-III" "LZR" "OI.XC.QA.PL.FG.ER.TY" "09.02.16") ' '+                        ["    ABCDEFGHIJKLMNOPQRSTUVWXYZ         ","  P QBXDRGFHOJKPMNILAESYUVWCTZ         OI.XC.QA.PL.FG.ER.TY","  1 DFHJANPRVTXLWCGUEYIKSQOMZB  R  03  III","  2 QGCLFMUKTWZDNJYVOESIBPRAHX  Z  25  II","  3 CIDANSWKQLTVEURPMXFYOZGBHJ  L  04  I","  R EJMZALYXVBWFCRQUONTSPIKHGD         A","  3 DXACMSWYBZHJQEUPIOFKNLGRTV         I","  2 XUCLREBYTNHDFMQVAWSIGPJZOK         II","  1 EZNAQBOCSDTLXFWGVHUJPIMKRY         III","  P QBXDRGFHOJKPMNILAESYUVWCTZ         OI.XC.QA.PL.FG.ER.TY","    XPOSZNUVJIRQWFCBLKDYGHMATE         "],                 testShowOperation (configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11") "KRIEG"                         ["    OHNKJYSBTEDMLCARWPGIXZQUFV  LFAP  10 16 24 06","K > CMAWFEKLNVG\818\773HBIUYTXZQOJDRPS  LFAQ  10 16 24 07","R > HXETCUMASQNZGKRYJO\818\773IDFWVBPL  LFAR  10 16 24 08","I > FGRJUABYW\818\773DZSXVQTOCLPENIMHK  LFAS  10 16 24 09","E > SJWYN\818\773UZPQBVXRETHIMAOFKCLDG  LFAT  10 16 24 10","G > EOKPAQW\818\773JLHCISTBDFVMNXRGUZY  LFAU  10 16 24 11"],                 testShowOperation (configEnigma "c-β-VI-VIII-V" "OMLQ" "AI.JF.CM.DQ.HU.LX.PR.SZ" "03.16.04.11") "UBOOT"@@ -145,6 +145,7 @@                         "KRKR ALLE XX FOLGENDES IST SOFORT BEKANNTZUGEBEN XX ICH HABE FOLGELNBE BEFEHL ERHALTEN XX J ANSTERLE DES BISHERIGXN REICHSMARSCHALLS J GOERING J SETZT DER FUEHRER SIE Y HVRR GRZSSADMIRAL Y ALS SEINEN NACHFOLGER EIN X SCHRIFTLSCHE VOLLMACHT UNTERWEGS X ABSOFORT SOLLEN SIE SAEMTLICHE MASSNAHMEN VERFUEGEN Y DIE SICH AUS DER GEGENWAERTIGEN LAGE ERGEBEN X GEZ X REICHSLEITEI KK TULPE KK J BORMANN J XX OB.D.MMM DURNH FKST.KOM.ADM.UUU BOOIE.KP"                         ["LANO TCTO UARB BFPM HPHG CZXT DYGA HGUF XGEW KBLK GJWL QXXT ","GPJJ AVTO CKZF SLPP QIHZ FXOE BWII EKFZ LCLO AQJU LJOY HSSM ","BBGW HZAN VOII PYRB RTDJ QDJJ OQKC XWDN BBTY VXLY TAPG VEAT ","XSON PNYN QFUD BBHH VWEP YEYD OHNL XKZD NWRH DUWU JUMW WVII ","WZXI VIUQ DRHY MNCY EFUA PNHO TKHK GDNP SAKN UAGH JZSM JBMH ","VTRE QEDG XHLZ WIFU SKDQ VELN MIMI THBH DBWV HDFY HJOQ IHOR ","TDJD BWXE MEAY XGYQ XOHF DMYU XXNO JAZR SGHP LWML RECW WUTL ","RTTV LBHY OORG LGOW UXNX HMHY FAAC QEKT HSJW"],                 testTest]+        putStrLn "\n"         if (errors results + failures results == 0) then                 exitWith ExitSuccess         else