packages feed

crypto-enigma 0.0.1.7 → 0.0.2.0

raw patch · 6 files changed

+344/−135 lines, 6 filesdep +HUnitdep +crypto-enigmaPVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, crypto-enigma

API changes (from Hackage documentation)

+ Crypto.Enigma: reflectors :: [Name]
+ Crypto.Enigma: rotors :: [Name]

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ See also: the list of [code releases] and [closed milestones]. +### [0.0.2.0]++* Start testing module.+* Expose valid rotor and reflector names in API.+ ### [0.0.1.7]  * Fix Hackage uploading and versioning errors.@@ -20,7 +25,6 @@ * Added CHANGELOG. * Support for [build checks]. - ### [0.0.1.3]  Initial Hackage version. First upload of package to Hackage,@@ -30,6 +34,7 @@ [build checks]: https://travis-ci.org/orome/crypto-enigma/branches [code releases]: https://github.com/orome/crypto-enigma/releases [closed milestones]: https://github.com/orome/crypto-enigma/milestones?state=closed+[0.0.2.0]: https://github.com/orome/crypto-enigma/releases/tag/0.0.2.0 [0.0.1.7]: https://github.com/orome/crypto-enigma/releases/tag/0.0.1.7 [0.0.1.6]: https://github.com/orome/crypto-enigma/releases/tag/0.0.1.6 [0.0.1.5]: https://github.com/orome/crypto-enigma/releases/tag/0.0.1.5
Crypto/Enigma.hs view
@@ -1,13 +1,16 @@ {-# OPTIONS_HADDOCK show-extensions #-} {-| Module      : Crypto.Enigma-Description : Enigma machine simulator.+Description : Enigma machine simulator+Copyright   : (c) 2014-2015 Roy Levien License     : BSD3 Maintainer  : royl@aldaron.com Stability   : experimental Portability : POSIX -An enigma machine simulator with rudimentary display. Richer display is provided by "Crypto.Enigma.Display".+An Enigma machine simulator with rudimentary display, currently limited to the I, M3, and M4 models.++Richer display is provided by "Crypto.Enigma.Display". -} --{-# LANGUAGE MultiWayIf #-} --{-# LANGUAGE Trustworthy #-}@@ -22,6 +25,8 @@         Wiring,         Turnovers,         component,+        rotors,+        reflectors,          -- * Machine configurations and transitions         EnigmaConfig,         configEnigma,@@ -71,8 +76,6 @@ -- TBD - Use Arrow more? -- TBD - EnigmaMachine as Monad instance? -- TBD - Add more walkthroughs to documentation (either README or Haddock)?--- TBD - Proper testing script - <http://dev.stephendiehl.com/hask/#testing> <<<--- TBD - Code review <<< -- REV - Move plugboard last (it's 'optional'?)? -- 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?@@ -113,8 +116,8 @@  -- REV - \c -> (name c, c) instead of (name &&& id) ? -- Definitions of rotor Components; people died for this information---comps :: [(Name,Component)]-comps = M.fromList $ (name &&& id) <$> [+rots :: M.Map Name Component+rots = M.fromList $ (name &&& id) <$> [         -- rotors         Component "I"    "EKMFLGDQVZNTOWYHXUSPAIBRCJ" "Q",         Component "II"   "AJDKSIRUXBLHWTMCQGZNPYFVOE" "E",@@ -125,25 +128,38 @@         Component "VII"  "NZJHGRCXMYSWBOUFAIVLPEKQDT" "ZM",         Component "VIII" "FKQHTLXOCBJSPDZRAMEWNIUYGV" "ZM",         Component "β"    "LEYJVCNIXWPBQMDRTAKZGFUHOS" "",-        Component "γ"    "FSOKANUERHMBTIYCWLQPZXVGJD" "",+        Component "γ"    "FSOKANUERHMBTIYCWLQPZXVGJD" ""]+refs ::  M.Map Name Component+refs = M.fromList $ (name &&& id) <$> [         -- reflectors         Component "A"    "EJMZALYXVBWFCRQUONTSPIKHGD" "",         Component "B"    "YRUHQSLDPXNGOKMIEBFZCWVJAT" "",         Component "C"    "FVPJIAOYEDRZXWGCTKUQSBNMHL" "",         Component "b"    "ENKQAUYWJICOPBLMDXZVFTHRGS" "",-        Component "c"    "RDOBJNTKVEHMLFCWZAXGYIPSUQ" "",+        Component "c"    "RDOBJNTKVEHMLFCWZAXGYIPSUQ" ""]         -- base case (e.g. for "unplugged" plugboard, or the keyboard)-        Component ""     "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ""]+kbd = M.fromList [("",Component ""     "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "")] +comps :: M.Map Name Component+comps = rots `M.union` refs `M.union` kbd++-- | The list of valid 'Component' 'Name's for rotors.+rotors :: [Name]+rotors = M.keys rots++-- | The list of valid 'Component' 'Name's for reflectors.+reflectors :: [Name]+reflectors = M.keys refs+ -- | The 'Component' with the specified 'Name'. component :: Name -> Component component n = fromMaybe (Component n (foldr plug letters (splitOn "." n)) "") (M.lookup n comps)-            -- Either lookup the rotor or reflector by name, or use the plugboard spec to-            -- generate a component with wiring in which the specified letter pairs are exchanged.-            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+        -- Either lookup the rotor or reflector by name, or use the plugboard spec to+        -- generate a component with wiring in which the specified letter pairs are exchanged.+    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   -- Machine configurations and transitions ------------------------------------@@ -161,45 +177,45 @@  -- | The complete description of the state of an Enigma machine, consisting 'components', 'positions', and 'rings'. data EnigmaConfig = EnigmaConfig {-                        -- | The 'Name' of each 'Component' in an 'EnigmaConfig', in processing order.-                        --   Unchanged by 'step'.-                        ---                        --   >>> components $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"-                        --   ["AM.EU.ZL","II","III","V","\946","c"]-                        ---                        --   (Note that any Unicode charcters are-                        --   <http://stackoverflow.com/a/24953885/656912 stored by Haskell> as their Unicode value:-                        --   here @"\\946" == "β"@.)-                        components :: ![Name],-                        -- | The 'Position' of each 'Component' in an 'EnigmaConfig', in machine processing order.-                        --   May be changed by 'step'.-                        ---                        --   >>> positions $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"-                        --   [1,25,2,17,23,1]-                        ---                        --   For plugboard and reflector, this will always be @1@ since the former cannot rotate,-                        --   and the latter does not (neither will be changed by 'step'):-                        ---                        --   prop> head (positions cfg) == 1-                        --   prop> last (positions cfg) == 1-                        ---                        --   This determines the encoding performed by a component (see 'componentMapping').-                        positions :: ![Position],-                        -- | The location of ring letter 'A' on the rotor for each 'Component' in an 'EnigmaConfig',-                        --   in machine processing order.-                        --   Unchanged by 'step'.-                        ---                        --   >>> rings $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"-                        --   [1,11,21,1,16,1]-                        ---                        --   For plugboard and reflector, this will always be @1@ since the former lacks a ring,-                        --   and for latter ring position is irrelevant (the letter ring is not visible, and has-                        --   no effect on turnovers):-                        ---                        --   prop> head (rings cfg) == 1-                        --   prop> last (rings cfg) == 1-                        rings :: ![Int]-                    } deriving Eq+        -- | The 'Name' of each 'Component' in an 'EnigmaConfig', in processing order.+        --   Unchanged by 'step'.+        --+        --   >>> components $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"+        --   ["AM.EU.ZL","II","III","V","\946","c"]+        --+        --   (Note that any Unicode charcters are+        --   <http://stackoverflow.com/a/24953885/656912 stored by Haskell> as their Unicode value:+        --   here @"\\946" == "β"@.)+        components :: ![Name],+        -- | The 'Position' of each 'Component' in an 'EnigmaConfig', in machine processing order.+        --   May be changed by 'step'.+        --+        --   >>> positions $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"+        --   [1,25,2,17,23,1]+        --+        --   For plugboard and reflector, this will always be @1@ since the former cannot rotate,+        --   and the latter does not (neither will be changed by 'step'):+        --+        --   prop> head (positions cfg) == 1+        --   prop> last (positions cfg) == 1+        --+        --   This determines the encoding performed by a component (see 'componentMapping').+        positions :: ![Position],+        -- | The location of ring letter 'A' on the rotor for each 'Component' in an 'EnigmaConfig',+        --   in machine processing order.+        --   Unchanged by 'step'.+        --+        --   >>> rings $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"+        --   [1,11,21,1,16,1]+        --+        --   For plugboard and reflector, this will always be @1@ since the former lacks a ring,+        --   and for latter ring position is irrelevant (the letter ring is not visible, and has+        --   no effect on turnovers):+        --+        --   prop> head (rings cfg) == 1+        --   prop> last (rings cfg) == 1+        rings :: ![Int]+        } deriving Eq  -- | The sequential, (forward) processing-order, 'Stage' occupied by each 'Component' in an 'EnigmaConfig', starting --   with @0@ for the plugboard and ending with the reflector.@@ -255,7 +271,7 @@                    | i >  3                 = 0  -- only the first three rotors can step                    | i == 1                 = 1  -- the first rotor always steps                    | i == 2 && isTurn 2     = 1  -- the second rotor steps if it is in a turnover position-                   |           isTurn (i-1) = 1  -- other rotors (<=3) step if the previous component is in a turnover position+                   |           isTurn (i-1) = 1  -- others (<=3) step if previous component is in a turnover position                    | otherwise              = 0                 isTurn :: Stage -> Bool                 isTurn j = elem (windowLetter ec j) (turnovers $ component (components ec !! j))@@ -272,7 +288,9 @@ windows :: EnigmaConfig -> String windows ec = reverse $ tail.init $ windowLetter ec <$> (stages ec) --- | A (safe plublic, <https://wiki.haskell.org/Smart_constructors "smart">) constructor that does validation and takes a conventional specification as input, in the+-- REV - Add assertion 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: -- --   * The rotor 'Name's, separated by dashes (e.g. @\"C-V-I-II\"@); see 'Name'.@@ -295,17 +313,17 @@                                                             ((\s -> s == nub s) $ filter (/='.') plug))                                             ) &&                                             (and $ (`M.member` comps) <$> tail components'))-                                           EnigmaConfig {-                                               components = components',-                                               positions = zipWith (\w r -> (mod (numA0 w - r + 1) 26) + 1) winds' rngs',-                                              rings = rngs'-                                           }-                                            where-                                                -- Order is reversed, from physical to processing order-                                                -- Rings and windows are padded with values for the plugboard and reflector-                                                rngs' = reverse $ (read <$> (splitOn "." $ "01." ++ rngs ++ ".01") :: [Int])-                                                winds' = "A" ++ reverse winds ++ "A"-                                                components' = reverse $ splitOn "-" $ rots ++ "-" ++ plug+        EnigmaConfig {+                components = components',+                positions = zipWith (\w r -> (mod (numA0 w - r + 1) 26) + 1) winds' rngs',+                rings = rngs'+        }+    where+        -- Order is reversed, from physical to processing order+        -- Rings and windows are padded with values for the plugboard and reflector+        rngs' = reverse $ (read <$> (splitOn "." $ "01." ++ rngs ++ ".01") :: [Int])+        winds' = "A" ++ reverse winds ++ "A"+        components' = reverse $ splitOn "-" $ rots ++ "-" ++ plug   -- TBD - Some checking, e.g. that four "words" have been provided?@@ -316,21 +334,21 @@ --   >>> cfg == cfg' --   True instance Read EnigmaConfig where-    readsPrec _ i = [(configEnigma c w s r, "")] 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. -- --   >>> configEnigma "b-β-V-VIII-II" "XQVI" "UX.MO.KZ.AY.EF.PL" "03.17.24.11" --   "b-β-V-VIII-III XQVI UX.MO.KZ.AY.EF.PL 03.17.24.11" instance Show EnigmaConfig where-    show ec = unwords [intercalate "-" $ reverse.tail $ components ec,-                       windows ec,-                       head $ components ec,-                       intercalate "." $ reverse.tail.init $ (printf "%02d") <$> (rings ec)]+        show ec = unwords [intercalate "-" $ reverse.tail $ components ec,+                           windows ec,+                           head $ components ec,+                           intercalate "." $ reverse.tail.init $ (printf "%02d") <$> (rings ec)]  -- | Show a 'Component' as a formatted string consisting of its 'name', 'wiring', and 'turnovers' (if any). instance Show Component where-    show c = printf "%-5.5s" (name c) ++ " " ++ wiring c ++ " " ++ turnovers c+        show c = printf "%-5.5s" (name c) ++ " " ++ wiring c ++ " " ++ turnovers c   @@ -351,8 +369,9 @@  -- REV - These only need to be exposed to allow componentMapping to be exposed; necessary? <<< -- | The direction that a signal flows through a 'Component'. During encoding of a character, the signal---   passes first through every component in a forward ('Fwd') direction, then through the reflector, and then, in---   revers order, through each component again, in reverse ('Rev').+--   passes first through the wiring of each component, from right to left in the machine, in a forward ('Fwd')+--   direction, then through the reflector, and then, from left to right, through each component again,+--   in reverse ('Rev'). --   This direction affects the encoding performed by the component (see 'componentMapping'). data Direction = Fwd | Rev @@ -377,11 +396,11 @@ --   True componentMapping:: Direction -> Component -> Position -> Mapping componentMapping d c p = case d of-                        Fwd -> map (\ch -> rotMap (1-p) letters !! (numA0 ch)) (rotMap (p-1) (wiring c))-                        Rev -> chrA0 <$> (ordering $ componentMapping Fwd c p)-                    where-                        rotMap :: Int -> Wiring -> Mapping-                        rotMap o w = take 26 . drop (mod o 26) . cycle $ w+        Fwd -> map (\ch -> rotMap (1-p) letters !! (numA0 ch)) (rotMap (p-1) (wiring c))+        Rev -> chrA0 <$> (ordering $ componentMapping Fwd c p)+    where+        rotMap :: Int -> Wiring -> Mapping+        rotMap o w = take 26 . drop (mod o 26) . cycle $ w  -- | The list of 'Mapping's for each stage of an 'EnigmaConfig': the encoding performed by the --   'Component' /at that point/ in the progress through the machine.@@ -416,9 +435,9 @@ --   can be found in "Crypto.Enigma.Display#showEnigmaConfigInternalEG". stageMappingList:: EnigmaConfig -> [Mapping] stageMappingList ec = ((stageMapping Fwd) <$>) <> ((stageMapping Rev) <$>).tail.reverse $ stages ec-                where-                        stageMapping :: Direction -> Stage -> Mapping-                        stageMapping d sn = componentMapping d (component $ components ec !! sn) (positions ec !! sn)+    where+        stageMapping :: Direction -> Stage -> Mapping+        stageMapping d sn = componentMapping d (component $ components ec !! sn) (positions ec !! sn)  -- | The list of 'Mapping's an 'EnigmaConfig' has performed by each stage: --   the encoding performed by the 'EnigmaConfig' /up to that point/ in the progress through the machine.@@ -473,6 +492,6 @@ --   prop> enigmaEncoding cfg (enigmaEncoding cfg msg) == msg enigmaEncoding :: EnigmaConfig -> Message -> String enigmaEncoding ec msg = assert (all (`elem` letters) msg) $-                        -- The encoding of a string is the sequence encodings performed by sequentially-                        -- stepped configurations (preceded by a step)-                        zipWith encode (enigmaMapping <$> (iterate step (step ec))) msg+        -- 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)
Crypto/Enigma/Display.hs view
@@ -1,13 +1,14 @@ {-# OPTIONS_HADDOCK show-extensions #-} {-| Module      : Crypto.Enigma.Display-Description : Display of Enigma machine state and encoding.+Description : Display of Enigma machine state and encoding+Copyright   : (c) 2014-2015 Roy Levien License     : BSD3 Maintainer  : royl@aldaron.com Stability   : experimental Portability : POSIX -A module for rich display of the state state of and encoding performed by Enigma machines defined in "Crypto.Enigma".+A module for rich display of the state of and encoding performed by Enigma machines defined in "Crypto.Enigma". -}  module Crypto.Enigma.Display (@@ -37,7 +38,7 @@ {-# ANN module ("HLint: error Redundant $"::String) #-} {-# ANN module ("HLint: ignore Use ."::String) #-} --- TBD - Fix name of more detaild display -> ..EnigmConfigSchematic ? <<<+-- TBD - Fix name of more detailed display -> ..EnigmConfigSchematic ? <<< -- REV - Final newline in show... functions is a bit inconsistent  @@ -47,14 +48,14 @@  -- Message entry ------------------------------------------------------------- --- Some standard substitions performed by (Kriegsmarine) operators+-- 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")]+    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 -----------------------------------------------------------@@ -85,8 +86,8 @@  -- Machine operation display ================================================= --- Preprocess a message and produce a configuration display for the starting configuraiton--- and for each character of the message, using the provied configuration display function.+-- 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)) @@ -105,21 +106,23 @@ -- --   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) (windows ec) (reverse $ tail.init $ positions ec)-                            where-                                ch' = if ch `elem` letters then ch else ' '-                                enc = enigmaMapping ec-                                fmt ch e ws ps = lbl ++ " " ++ e ++ "  " ++ ws ++ "  "++ ps'-                                    where-                                       lbl = if ch == ' ' then "   " else  ch:" >"-                                       ps' = unwords $ (printf "%02d") <$> ps+showEnigmaConfig ec ch = fmt ch' (markedMapping (locCar ch' enc enc) enc)+                                 (windows ec)+                                 (reverse $ tail.init $ positions ec)+    where+        ch' = if ch `elem` letters then ch else ' '+        enc = enigmaMapping ec+        fmt ch e ws ps = printf "%s %s  %s  %s" lbl e ws ps'+            where+                lbl = if ch == ' ' then "   " else  ch:" >"+                ps' = unwords $ (printf "%02d") <$> ps  -- TBD - Add figure from MMA tool showing mapping -- | Display a summary of the Enigma machine configuration as a schematic showing the encoding (see 'Mapping') --   performed by each stage (see 'stageMappingList'), along with an indication of the stage --   (rotor number, @\"P\"@ for plugboard, or @\"R\"@ for reflector), window letter (see 'windows'), --   'Position' (see 'positions') and 'Name',---   followed by the encoding for the machine, and preceeded by  a (trivial, no-op) keyboard \"encoding\"+--   followed by the encoding for the machine, and preceded by  a (trivial, no-op) keyboard \"encoding\" --   for reference. -- --   If a valid 'Message' character is provided, indicate that as input and mark the letter it is encoded to at@@ -171,22 +174,24 @@ --   Note that (as follows from 'Mapping') the position of the marked letter at each stage is the alphabetic position --   of the marked letter at the previous stage. showEnigmaConfigInternal :: EnigmaConfig -> Char -> String-showEnigmaConfigInternal ec ch = unlines $-                            [fmt (if ch' == ' ' then "" else ch':" >") (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'):" <") (markedMapping (last charLocs) (enigmaMapping ec)) ' ' 0 ""]-                        where-                            ch' = if ch `elem` letters then ch else ' '-                            charLocs = zipWith (locCar ch') ([letters] ++ stageMappingList ec ++ [enigmaMapping ec]) ([letters] ++ enigmaMappingList ec ++ [enigmaMapping ec])-                            fmt l e w p n = lbl ++ " " ++ e ++ "  " ++ (w:[]) ++ "  " ++ p' ++ "  " ++ n-                                where-                                    lbl = printf "%3.3s" l :: String-                                    p' = if p == 0 then "  " else printf "%02d" (p::Int)+showEnigmaConfigInternal ec ch =+        unlines $ [fmt (if ch' == ' ' then "" else ch':" >") (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'):" <")+                       (markedMapping (last charLocs) (enigmaMapping ec)) ' ' 0 ""]+    where+        ch' = if ch `elem` letters then ch else ' '+        charLocs = zipWith (locCar ch')+                           ([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+            where+                p' = if p == 0 then "  " else printf "%02d" (p::Int)   -- Operation display ---------------------------------------------------------@@ -204,11 +209,11 @@ -- --   Note that the first line of the display represents the initial configuration of the machine, but does not --   perform any encoding (as explained in 'step').---   Note also that the second line of this display is the same as one displayed in the examle for 'showEnigmaConfig'.+--   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 --- | Show a schematic of an Enigma machine's internal configuration (see 'showEnigmaConfigInternal')+-- | 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. -- --   >>> putStr $ showEnigmaOperationInternal (configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11") "KR"@@ -256,7 +261,7 @@ -- --   Note that the first block of the display represents the initial configuration of the machine, but does not --   perform any encoding (as explained in 'step'). Note also that the second block of this display is the same---   as one displayed in the examle for 'showEnigmaConfigInternal'.+--   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 @@ -264,7 +269,7 @@  -- Encoding display ========================================================== --- | Show the conventionally formatted encoding of a mssage 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"
README.md view
@@ -8,9 +8,17 @@  An Enigma machine simulator with state and encoding display. +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. +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/issues/10)+that will be addressed in a future release.+ Full [documentation] — for the latest [release version] — is available on Hackage.  For other Haskell Enigma machines see:@@ -35,3 +43,8 @@ [development version]: https://github.com/orome/crypto-enigma/tree/develop [milestones]: https://github.com/orome/crypto-enigma/milestones [open issues]: https://github.com/orome/crypto-enigma/issues++[machine models]: http://www.cryptomuseum.com/crypto/enigma/tree.htm+[I]: http://www.cryptomuseum.com/crypto/enigma/i/index.htm+[M3]: http://www.cryptomuseum.com/crypto/enigma/m3/index.htm+[M4]: http://www.cryptomuseum.com/crypto/enigma/m4/index.htm
crypto-enigma.cabal view
@@ -3,11 +3,14 @@ -- PVP summary:         +-+------- breaking API changes --                      | | +----- non-breaking API additions --                      | | | +--- code changes with no API change-version:                0.0.1.7+version:                0.0.2.0 synopsis:               An Enigma machine simulator with display. description:            The crypto-enigma package is an Enigma machine simulator                         with rich display and machine state details.                         .+                        Currently support is only provided for the machine models in most widespread general use+                        during the war years: the I, M3, and M4.+                        .                         For basic functionality, including some introspection of machine state                         and the mappings performed, simply                         .@@ -16,18 +19,21 @@                         For richer display functionality additionally                         .                         > > import Crypto.Enigma.Display+                        .+                        (Note that this package assumes support for combining Unicode, without which some output may+                        appear garbled.) homepage:               https://github.com/orome/crypto-enigma bug-reports:	        https://github.com/orome/crypto-enigma/issues license:                BSD3 license-file:           LICENSE author:                 Roy Levien maintainer:             royl@aldaron.com--- copyright:           +copyright:              (c) 2014-2015 Roy Levien category:               Cryptography, Education build-type:             Simple extra-source-files:     CHANGELOG.md                         README.md-cabal-version:          >= 1.22+cabal-version:          >=1.22  source-repository head         type:           git@@ -38,7 +44,7 @@         type:           git         location:       git://github.com/orome/crypto-enigma.git         branch:         hackage-        tag:            0.0.1.7+        tag:            0.0.2.0  library     -- default-extensions: Trustworthy@@ -46,9 +52,19 @@                         Crypto.Enigma.Display     other-modules:      Crypto.Enigma.Utils     -- other-extensions:-    build-depends:      base >= 4.8.1.0 && <4.9,-                        containers >= 0.5.5.1,-                        split >= 0.2.2,-                        MissingH >= 1.3.0.1+    build-depends:      base >=4.8.1.0 && <4.9,+                        containers >=0.5.5.1,+                        split >=0.2.2,+                        MissingH >=1.3.0.1     -- hs-source-dirs:+    default-language:   Haskell2010++test-suite crypto-enigma-test+    type:               exitcode-stdio-1.0+    hs-source-dirs:     test+    main-is:            Test.hs+    build-depends:      base >=4.8.1.0 && <4.9,+                        HUnit >=1.3,+                        crypto-enigma+    -- ghc-options:        -threaded -rtsopts -with-rtsopts=-N     default-language:   Haskell2010
+ test/Test.hs view
@@ -0,0 +1,151 @@+module Main where++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"])+        (sort rotors)++testReflectorNames :: Test+testReflectorNames = TestCase $ assertEqual "Invalid reflector list"+        (sort ["A","B","C","b","c"])+        (sort reflectors)++testPlugboardWiring :: Name -> String -> Test+testPlugboardWiring plug wire = TestCase $ assertEqual ("Wrong plugboard generated for " ++ plug)+        wire+        (wiring $ component plug)++testWindowsInstantiation :: String -> String -> String -> String -> Test+testWindowsInstantiation rots winds plug rings = TestCase $ assertEqual ("Invalid windows from instantiation for " ++ show cfg)+        winds+        (windows cfg)+    where cfg = configEnigma rots winds plug rings++testReadShowIsNoOp :: EnigmaConfig -> Test+testReadShowIsNoOp cfg = TestCase $ assertEqual ("Read/Show implementation not compliant for " ++ show cfg)+        cfg+        (read (show cfg) :: EnigmaConfig)++testPlugboardIsOwnInverse :: Name -> Message -> Test+testPlugboardIsOwnInverse plug msg = TestCase $ assertEqual ("Plugboard is not self-inverse for " ++ plug)+        msg+        (enigmaEncoding (configEnigma "----" "AAAA" plug "01.01.01.01") msg)++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)++testStageMappings :: EnigmaConfig -> [Mapping] -> Test+testStageMappings cfg mps = TestCase $ assertEqual ("Incorrect mappings for " ++ show cfg)+        mps+        (stageMappingList cfg)++-- See: http://www.enigma.hoerenberg.com/index.php?cat=The%20U534%20messages+testHistoricalMessage :: String -> EnigmaConfig -> Message -> String -> Test+testHistoricalMessage hmn cfg msg enc = TestCase $ assertEqual ("Error processing historical message " ++ hmn)+        enc+        (enigmaEncoding cfg msg)++testShowConfig :: EnigmaConfig -> Char -> String -> Test+testShowConfig cfg ch scfg = TestCase $ assertEqual ("Incorrect config display for " ++ show cfg)+        scfg+        (showEnigmaConfig cfg ch)++testShowConfigIntenral :: EnigmaConfig -> Char -> [String] -> Test+testShowConfigIntenral cfg ch scfg = TestCase $ assertEqual ("Incorrect internal config display for " ++ show cfg)+        scfg+        (lines $ showEnigmaConfigInternal cfg ch)++testShowOperation :: EnigmaConfig -> String -> [String] -> Test+testShowOperation cfg msg sop = TestCase $ assertEqual ("Incorrect operation display for " ++ show cfg)+        sop+        (lines $ showEnigmaOperation cfg msg)++testShowOperationInternal :: EnigmaConfig -> String -> [String] -> Test+testShowOperationInternal cfg msg sop = TestCase $ assertEqual ("Incorrect operation internal display for " ++ show cfg)+        sop+        (lines $ showEnigmaOperationInternal cfg msg)++testShowEncoding :: EnigmaConfig -> Message -> [String] -> Test+testShowEncoding cfg msg senc = TestCase $ assertEqual ("Incorrect encoding display for " ++ show cfg)+        senc+        (lines $ showEnigmaEncoding cfg msg)++testTest :: Test+testTest = TestCase $ assertEqual "Should be one" 1 1+++main :: IO ()+main = do+        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 "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'])+        results <- runTestTT $ TestList [+                testRotorNames,+                testReflectorNames,+                testPlugboardWiring "UX.PO.KY.AZ.EF.ML" "ZBCDFEGHIJYMLNPOQRSTXVWUKA",+                testPlugboardWiring "AE.QB.CM.DF.WH.JN.LX.PR.ZS.VU" "EQMFADGWINKXCJORBPZTVUHLYS",+                testWindowsInstantiation "b-β-V-VIII-II" "XQVI" "UX.MO.KZ.AY.EF.PL" "03.17.24.11",+                testWindowsInstantiation "C-V-VIII-III" "MUM" "AY.EF.PL" "09.16.24",+                testWindowsInstantiation "c-β-VIII-VII-IV" "LMOI" "" "21.01.19.01",+                testReadShowIsNoOp (configEnigma "b-β-VI-I-III" "AIQM" "AI.JF.DQ.HU.LX.PR.SZ" "22.17.04.22"),+                testReadShowIsNoOp (configEnigma "C-I-II-III" "PLA" "~" "15.01.06"),+                testPlugboardIsOwnInverse "AI.JF.CM.DQ.HU.LX.PR.SZ" "UNCHANGEDUNCHANGEDUNCHANGED",+                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")+                        ["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"],+                testHistoricalMessage "U534-P1030662"+                        (configEnigma "c-β-V-VI-VIII" "WIIJ" "AE.BF.CM.DQ.HU.JN.LX.PR.SZ.VW" "01.01.05.12")+                        "UUUVIRSIBENNULEINSYNACHRXUUUSTUETZPUNKTLUEBECKVVVCHEFVIERXUUUFLOTTXXMITUUUVIERSIBENNULZWOUNDUUUVIERSIBENNULDREIZURFLENDERWERFTLUEBECKGEHENXFONDORTFOLGTWEITERESX"+                        "LIRZMLWRCDMSNKLKBEBHRMFQFEQAZWXBGBIEXJPYFCQAAWSEKDEACOHDZKCZTOVSYHFNSCMAIMIMMAVJNLFXEWNPUIRINOZNCRVDHCGKCYRVUJQPVKEUIVVXGLQMKRJMDMLXLLRLYBKJWRXBQRZWGCCNDOPMGCKJ",+                testHistoricalMessage "U534-P1030694"+                        (configEnigma "b-γ-IV-III-VIII" "RCPO" "CH.EJ.NV.OU.TY.LG.SZ.PK.DI.QB" "01.01.03.21")+                        "PLLEVONVONZEHNXSIDIXXHAFENWARNEMUENDEFEINDBESETZTCHV"+                        "VBBHSYTWZEEDGKYCAKYVWBWUUZVZIGCTBZLZYUHYWILFYUPBIPCM",+                testHistoricalMessage "U534-P1030681 (Dönitz)"+                        (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",+                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         "],+                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"+                        ["    LQZRYKTWJIFAXUVSBDPGNOHMEC  OMLQ  13 24 09 07","U > TDRBJWSNVEPMLHUKXCGAO\818\773IFQZY  OMLR  13 24 09 08","B > VK\818\773JTYSXMLCBIHRZWUNFDQAPGEO  OMLS  13 24 09 09","O > VTSFJDZWNEPRUIY\818\773KXLCBMAHQOG  OMLT  13 24 09 10","O > VYPSMILKFQHGEZW\818\773CJUDXRAOTBN  OMLU  13 24 09 11","T > BARPQGFWLTUIZXVDECYJ\818\773KOHNSM  OMLV  13 24 09 12"],+                testShowOperationInternal (configEnigma "b-β-II-IV-V" "MLKQ" "UJ.LK.BV.SF.ZX.QA" "11.12.19.01") "SIE"+                        ["    ABCDEFGHIJKLMNOPQRSTUVWXYZ         ","  P QVCDESGHIULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","  1 KGWTAYPOMUFJLBQSDIEZCNXRVH  Q  17  V","  2 BOSLKUEJMAWDXHRIGYCQZPFTVN  K  19  IV","  3 AJDKSIRUXBLHWTMCQGZNPYFVOE  L  01  II","  4 WHTALGVUNZOKBPRYIXEDSFMQJC  M  03  \946","  R ENKQAUYWJICOPBLMDXZVFTHRGS         b","  4 DMZTSVFBQYLEWIKNXOUCHGARPJ         \946","  3 AJPCZWRLFBDKOTYUQGENHXMIVS         II","  2 JASLGWQNPHEDIZBVTOCXFYKMRU         IV","  1 ENUQSKBZRLAMIVHGOXPDJYCWFT         V","  P QVCDESGHIULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","    IENSBJTYAFXQUCZVLWDGMPRKHO         ","","S > ABCDEFGHIJKLMNOPQRS\818\773TUVWXYZ         ","  P QVCDESGHIULKMNOPARF\818\773TJBWZYX         UJ.LK.BV.SF.ZX.QA","  1 FVSZXO\818\773NLTEIKAPRCHDYBMWQUGJ  R  18  V","  2 BOSLKUEJMAWDXHR\818\773IGYCQZPFTVN  K  19  IV","  3 AJDKSIRUXBLHWTMCQG\818\773ZNPYFVOE  L  01  II","  4 WHTALGV\818\773UNZOKBPRYIXEDSFMQJC  M  03  \946","  R ENKQAUYWJICOPBLMDXZVFT\818\773HRGS         b","  4 DMZTSVFBQYLEWIKNXOUC\818\773HGARPJ         \946","  3 AJP\818\773CZWRLFBDKOTYUQGENHXMIVS         II","  2 JASLGWQNPHEDIZBV\818\773TOCXFYKMRU         IV","  1 MTPRJAYQKZLHUGFNWOCIXB\818\773VESD         V","  P QV\818\773CDESGHIULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","V < XTYNLIMZFRQEGDWUKJV\818\773BPSOACH         ","","I > ABCDEFGHI\818\773JKLMNOPQRSTUVWXYZ         ","  P QVCDESGHI\818\773ULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","  1 URYWNMKSD\818\773HJZOQBGCXALVPTFIE  S  19  V","  2 BOSL\818\773KUEJMAWDXHRIGYCQZPFTVN  K  19  IV","  3 AJDKSIRUXBLH\818\773WTMCQGZNPYFVOE  L  01  II","  4 WHTALGVU\818\773NZOKBPRYIXEDSFMQJC  M  03  \946","  R ENKQAUYWJICOPBLMDXZVF\818\773THRGS         b","  4 DMZTSV\818\773FBQYLEWIKNXOUCHGARPJ         \946","  3 AJPCZWRLFBDKOTYUQGENHX\818\773MIVS         II","  2 JASLGWQNPHEDIZBVTOCXFYKM\818\773RU         IV","  1 SOQIZXPJYKGTF\818\773EMVNBHWAUDRCL         V","  P QVCDES\818\773GHIULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","S < XKWOFEZPS\818\773MBUJVDHTYIQLNCARG         ","","E > ABCDE\818\773FGHIJKLMNOPQRSTUVWXYZ         ","  P QVCDE\818\773SGHIULKMNOPARFTJBWZYX         UJ.LK.BV.SF.ZX.QA","  1 QXVML\818\773JRCGIYNPAFBWZKUOSEHDT  T  20  V","  2 BOSLKUEJMAWD\818\773XHRIGYCQZPFTVN  K  19  IV","  3 AJDK\818\773SIRUXBLHWTMCQGZNPYFVOE  L  01  II","  4 WHTALGVUNZO\818\773KBPRYIXEDSFMQJC  M  03  \946","  R ENKQAUYWJICOPBL\818\773MDXZVFTHRGS         b","  4 DMZTSVFBQYLE\818\773WIKNXOUCHGARPJ         \946","  3 AJPCZ\818\773WRLFBDKOTYUQGENHXMIVS         II","  2 JASLGWQNPHEDIZBVTOCXFYKMRU\818\773         IV","  1 NPHYWOIXJFSEDLUMAGVZT\818\773CQBKR         V","  P QVCDESGHIULKMNOPARFT\818\773JBWZYX         UJ.LK.BV.SF.ZX.QA","T < PIJYT\818\773OQWBCNXRKFAGMZEVUHLDS         ",""],+                testShowEncoding (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")+                        "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]+        if (errors results + failures results == 0) then+                exitWith ExitSuccess+        else+                exitWith (ExitFailure 1)