ByteString.hs (1349B)
1 module Cryptopals.Util.ByteString ( 2 hamming 3 , nhamming 4 , panhamming 5 , chunks 6 , rotate 7 ) where 8 9 import qualified Data.Bits as B 10 import qualified Data.ByteString as BS 11 import qualified Data.List as L 12 import qualified Data.List.NonEmpty as NE 13 14 -- | Hamming distance between bytestrings. 15 hamming :: BS.ByteString -> BS.ByteString -> Maybe Int 16 hamming l r 17 | BS.length l /= BS.length r = Nothing 18 | otherwise = Just (foldr alg 0 (BS.zip l r)) 19 where 20 ham a b = B.popCount (B.xor a b) 21 alg = (+) . uncurry ham 22 23 -- | Normalized Hamming distance between bytestrings. 24 nhamming :: BS.ByteString -> BS.ByteString -> Maybe Double 25 nhamming a b = 26 let len = fromIntegral (BS.length a) 27 in fmap (\s -> fromIntegral s / len) (hamming a b) 28 29 -- | Average pairwise normalized Hamming distance between bytestrings. 30 panhamming:: [BS.ByteString] -> Maybe Double 31 panhamming bs = case bs of 32 [] -> Nothing 33 _ -> do 34 ps <- sequence [nhamming h b | (h:t) <- L.tails bs, b <- t] 35 pure $ sum ps / fromIntegral (length ps) 36 37 chunks :: Int -> BS.ByteString -> [BS.ByteString] 38 chunks size = loop mempty where 39 loop !acc bs 40 | BS.null bs = reverse acc 41 | otherwise = case BS.splitAt size bs of 42 (chunk, rest) -> loop (chunk : acc) rest 43 44 rotate :: Int -> BS.ByteString -> [BS.ByteString] 45 rotate rows = BS.transpose . chunks rows