I finally read Neal Stephenson’s book *Cryptonomicon* this summer which – besides being a great read – introduced an interesting cipher called *Pontifex*. This cipher is based on the Solitaire cipher by Bruce Schneier and the idea is that one simply needs a deck of card in order to communicate securely if a list of keys has been exchanged. When seeing that yesterday’s challenge on Programming Praxis was to implement this cipher I simply *had* to do it, and here’s the resulting implementation, in Haskell, of the Solitaire cipher:

```
import Data.Char (ord, chr)
import Data.List (elemIndex)
import Data.Maybe (fromJust)
import Control.Arrow
data Card a = Card a | JokerA | JokerB deriving (Show, Eq)
type Deck = [Card Int]
-- we start with a deck in bridge order
startDeck :: Deck
startDeck = map Card [1..52] ++ [JokerA] ++ [JokerB]
-- ‘A’ represents 1, ‘B’ 2, … , ‘Z‘ represents 26
charNum :: Char -> Int
charNum c = 1 + ord c - ord 'A'
-- 1 = ‘A’, … , 26 = ‘Z’, 27 = ‘A’, …
numChar :: Int -> Char
numChar n
| n > 26 = numChar (n-26)
| otherwise = chr (n+ord 'A'-1)
-- insert an element at a given position in a list
insertAt :: Int -> a -> [a] -> [a]
insertAt i e l = take i' l ++ [e] ++ drop i' l
where i' = if i <= length l
then i
else i `mod` length l
-- regular cards have a value, 1 ≤ v ≤ 52, jokers have the value 53
cardVal :: Card Int -> Int
cardVal (Card n) = n
cardVal _ = 53
-- a counted cut takes n cards from the top of the deck and places them just
-- over the bottommost card
countedCut :: Deck -> Int -> Deck
countedCut d n = (init . drop n) d ++ take n d ++ [last d]
-- operation 1: the “A” joker is moved one card down the deck
op1 :: Deck -> Deck
op1 d = insertAt (i+1) JokerA (filter (/= JokerA) d)
where i = fromJust $ elemIndex JokerA d
-- operation 2: the “B” joker is moved two cards down the deck
op2 :: Deck -> Deck
op2 d = insertAt (i+2) JokerB (filter (/= JokerB) d)
where i = fromJust $ elemIndex JokerB d
-- operation 3: a triple-cut swaps all the cards above the highest joker in the
-- deck with all the cards below the lowest joker in the deck, leaving the two
-- jokers and the cards between them in place
op3 :: Deck -> Deck
op3 d = d3 ++ [d!!min j1 j2] ++ d2 ++ [d!!max j1 j2] ++ d1
where joker c = c `elem` [JokerA, JokerB]
j1 = fromJust $ elemIndex JokerA d
j2 = fromJust $ elemIndex JokerB d
d1 = take (min j1 j2) d
d2 = takeWhile (not . joker) $ drop (min j1 j2 + 1) d
d3 = drop (max j1 j2 + 1) d
-- operation 4: a counted cut, based on the number of the bottom card in the
-- deck, moves the top “count” cards to just above the bottom card
op4 :: Deck -> Deck
op4 d = countedCut d ((last >>> cardVal) d)
-- one step of the algorithm is the four operations above in sequence
step :: Deck -> Deck
step = op1 >>> op2 >>> op3 >>> op4
-- keying a deck consists of one step, and then, for each character in the key,
-- do a counted cut on the number of the current character followed by another
-- single step
keyDeck :: String -> Deck
keyDeck = foldl (\x c -> step (countedCut x (charNum c))) (step startDeck)
-- a keyed deck is key stream, each card representing a number 1 ≤ n ≤ 52
keyStream :: Deck -> [Int]
keyStream d@(c:cs) = [val | val /= 53] ++ keyStream (step d)
where val = cardVal (d!!cardVal c)
-- encryption adds each character value to the value of the corresponding key
encrypt :: String -> String -> String
encrypt key plaintext = five $ zipWith (\a b -> cAdd a (numChar b)) text keys
where deck = keyDeck key
keys = keyStream deck
text = filter (/= ' ') plaintext
cAdd a b = numChar (charNum a + charNum b)
five (a:b:c:d:e:t) = a:b:c:d:e:' ':five t
five s = s
-- decryption subtracts each character value from the value of the
-- corresponding key
decrypt :: String -> String -> String
decrypt key plaintext = zipWith (\a b -> cSub a (numChar b)) text keys
where deck = keyDeck key
keys = keyStream deck
text = filter (/= ' ') plaintext
cSub a b = numChar (charNum a - charNum b)
main = do
putStrLn $ encrypt "" "AAAAAAAAAA"
putStrLn $ encrypt "FOO" "AAAAAAAAAAAAAAA"
putStrLn $ encrypt "CRYPTONOMICON" "SOLITAIRE"
```