module Codec.Fountain
( Droplet (..)
, Decoder
, droplets
, decoder
, decode
, test
) where
import Data.Bits
import Data.IntMap (IntMap)
import qualified Data.IntMap as M
import Data.IntSet (IntSet)
import qualified Data.IntSet as S
import Data.Word
import System.Random
data Decoder a = Decoder Int (IntMap a) [Droplet a]
data Droplet a = Droplet IntSet a deriving (Show, Eq)
droplets :: Bits a => Int -> Int -> [a] -> [Droplet a]
droplets seed maxDegree message = droplets' $ randoms $ mkStdGen seed
where
droplets' a = Droplet indices symbol : droplets' (drop (n + 1) a)
where
n = head a `mod` maxDegree + 1
indices = S.fromList $ map (`mod` length message) (take n $ tail a)
symbol = foldl1 xor $ map (message !!) $ S.toList indices
decoder :: Int -> Decoder a
decoder messageLength = Decoder messageLength M.empty []
decode :: Bits a => Decoder a -> Droplet a -> Either (Decoder a) [a]
decode decoder droplet = decode' decoder [droplet]
decode' :: Bits a => Decoder a -> [Droplet a] -> Either (Decoder a) [a]
decode' (Decoder messageLength symbols _) _ | M.size symbols == messageLength = Right $ map (symbols M.!) [0 .. messageLength 1]
decode' decoder [] = Left decoder
decode' decoder@(Decoder messageLength symbols droplets) (droplet' : newDroplets)
| S.size indices == 0 = decode' decoder newDroplets
| S.size indices == 1 = decode' (Decoder messageLength (M.insert (head $ S.toList indices) symbol symbols) old) (new ++ newDroplets)
| otherwise = decode' (Decoder messageLength symbols $ old) (new ++ newDroplets)
where
droplet@(Droplet indices symbol) = refineDroplet symbols droplet'
(new, old) = refineDroplets droplet droplets
refineDroplets :: Bits a => Droplet a -> [Droplet a] -> ([Droplet a], [Droplet a])
refineDroplets d@(Droplet indices symbol) droplets = foldl f ([], [d]) droplets
where
f (new, old) d@(Droplet indices1 symbol1)
| S.isSubsetOf indices indices1 = (Droplet (S.difference indices1 indices) symbol2 : new, old)
| S.isSubsetOf indices1 indices = (Droplet (S.difference indices indices1) symbol2 : new, d : old)
| otherwise = (new, d : old)
where
symbol2 = symbol `xor` symbol1
refineDroplet :: Bits a => IntMap a -> Droplet a -> Droplet a
refineDroplet symbols (Droplet indices symbol) = f (S.toList indices) S.empty symbol
where
f [] indices symbol = Droplet indices symbol
f (a : b) indices symbol
| M.member a symbols = f b indices $ symbol `xor` (symbols M.! a)
| otherwise = f b (S.insert a indices) symbol
test :: Int -> Int -> Int -> IO Int
test seed messageLength maxDegree = f 1 (decoder messageLength) (droplets seed' maxDegree message)
where
g = mkStdGen seed
r = randoms g
message = map fromIntegral $ take messageLength r :: [Word8]
seed' = r !! messageLength
f i decoder newDroplets = do
case decode decoder $ head newDroplets of
Left decoder -> f (i + 1) decoder $ tail newDroplets
Right m -> if m == message then return i else error "failed to reconstruct message"