-- Cesar Cipher Example -- from Hutton "Programming in Haskell", p42ff ----------------------------------------------------------------------------- module Cesar where import Char let2int :: Char -> Int let2int c = ord c - ord 'a' int2let :: Int -> Char int2let n = chr (ord 'a' + n) -- shift a character c by n slots to the right shift :: Int -> Char -> Char shift n c | isLower c = int2let (((let2int c) + n) `mod` 26) | otherwise = c -- top-level string encoding function encode :: Int -> String -> String encode n cs = [ shift n c | c <- cs ] -- table of frequencies of letters 'a'..'z' table :: [Float] table = [8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.2, 0.8, 4.0, 2.4, 6.7, 7.5, 1.9, 0.1, 6.0, 6.3, 9.1, 2.8, 1.0, 2.4, 0.2, 2.0, 0.1] percent :: Int -> Int -> Float percent n m = (fromIntegral n / fromIntegral m)*100 -- compute frequencies of letters 'a'..'z' in a given string freqs :: String -> [Float] freqs cs = [percent (count c cs) n | c <- ['a'..'z'] ] where n = lowers cs -- chi-square function for computing distance between 2 frequency lists chisqr :: [Float] -> [Float] -> Float chisqr os es = sum [((o-e)^2)/e | (o,e) <- zip os es] -- rotate a list by n slots to the left; take, drop are Prelude functions rotate :: Int -> [a] -> [a] rotate n xs = drop n xs ++ take n xs -- the number of lower case letters in a given string lowers :: String -> Int lowers cs = length [ c | c <- cs, isLower c] -- count the number of occurrences of c in cs count :: Char -> String -> Int count c cs = length [ c' | c' <- cs, c==c'] -- find list of positions of x in the list xs positions :: Eq a => a -> [a] -> [Int] positions x xs = [ i' | (x', i') <- zip xs [0..n], x==x' ] where n = length xs - 1 -- top-level decoding function crack :: String -> String crack cs = encode (-factor) cs where factor = head (positions (minimum chitab) chitab) chitab = [ chisqr (rotate n table') table | n <- [0..25] ] table' = freqs cs -- to test this in ghci do: -- # ghci cesar-cipher.hs -- #> let s1 = "haskell is cool" -- #> let c1 = encode 3 s1 -- #> c1 -- #> let d1 = crack c1 -- #> d1