diff options
| author | Charles <sircharlesaze@gmail.com> | 2019-08-17 20:39:03 +0200 |
|---|---|---|
| committer | Charles <sircharlesaze@gmail.com> | 2019-08-17 20:39:03 +0200 |
| commit | 9a65938232d1fa9e1afe9a6eb2de48d25ff738a6 (patch) | |
| tree | 6f5e9dbef23a884a74f9fa10643ff5c0bea3d751 /haskell/059-xor_decryption.hs | |
| parent | 78fbf8dbcf39aa51cf682a8795d0d0c3be6034c6 (diff) | |
| download | project_euler-9a65938232d1fa9e1afe9a6eb2de48d25ff738a6.tar.gz project_euler-9a65938232d1fa9e1afe9a6eb2de48d25ff738a6.tar.bz2 project_euler-9a65938232d1fa9e1afe9a6eb2de48d25ff738a6.zip | |
haskell problem 22, 24, 25, 29, 39, 40, 45, 53, 59
Diffstat (limited to 'haskell/059-xor_decryption.hs')
| -rw-r--r-- | haskell/059-xor_decryption.hs | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/haskell/059-xor_decryption.hs b/haskell/059-xor_decryption.hs new file mode 100644 index 0000000..9a2bbe7 --- /dev/null +++ b/haskell/059-xor_decryption.hs @@ -0,0 +1,58 @@ +-- XOR decryption +-- +-- Problem 59 +-- Each character on a computer is assigned a unique code and the preferred standard is +-- ASCII (American Standard Code for Information Interchange). For example, +-- uppercase A = 65, asterisk (*) = 42, and lowercase k = 107. +-- +-- A modern encryption method is to take a text file, convert the bytes to ASCII, then +-- XOR each byte with a given value, taken from a secret key. The advantage with the +-- XOR function is that using the same encryption key on the cipher text, +-- restores the plain text; for example, 65 XOR 42 = 107, then 107 XOR 42 = 65. +-- +-- For unbreakable encryption, the key is the same length as the plain text message, +-- and the key is made up of random bytes. The user would keep the encrypted message +-- and the encryption key in different locations, and without both "halves", +-- it is impossible to decrypt the message. +-- +-- Unfortunately, this method is impractical for most users, so the modified method is +-- to use a password as a key. If the password is shorter than the message, +-- which is likely, the key is repeated cyclically throughout the message. The balance +-- for this method is using a sufficiently long password key for security, +-- but short enough to be memorable. +-- +-- Your task has been made easy, as the encryption key consists of three lower case characters. +-- Using p059_cipher.txt (right click and 'Save Link/Target As...'), +-- a file containing the encrypted ASCII codes, and the knowledge that the plain text +-- must contain common English words, decrypt the message and find the sum of +-- the ASCII values in the original text. + + +import Data.Bits(xor) +import Data.Char(ord, chr) +import qualified Data.Map as M + +type Frequencies = M.Map Char Int + +main = do + content <- readFile "../data/059_cipher.txt" + let formatted = map (\s -> read s :: Int) $ + (words $ map (\c -> if c == ',' then ' 'else c) content) + let freqs = [((applyKey k formatted), frequency $ applyKey k formatted) | k <- keys] + filtered = filterEnglish freqs + print (sum (fst $ filtered !! 1)) + +filterEnglish :: [(a, Frequencies)] -> [(a, Frequencies)] +filterEnglish fs = filter (\(t, f) -> filt_ea (M.lookup 'e' f)) fs + where filt_ea Nothing = False + filt_ea (Just freq) = freq > 10 + +keys = [[ord a, ord b, ord c] | a <- low, b <- low, c <- low] + where low = ['a'..'z'] + +applyKey :: [Int] -> [Int] -> [Int] +applyKey key code = zipWith (xor) (cycle key) code + +frequency :: [Int] -> Frequencies +frequency code = M.map (\v -> v * 100 `div` length code) counter + where counter = M.fromListWith (+) (map (\c -> (chr c, 1)) code) |
