module Codec.Fountain
(
Droplet (..)
, Decoder
, Precoding
, precoding
, droplets
, decoder
, decode
, test
, test'
, decoderProgress
) 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.List
import Data.Word
import System.Random
data Decoder a = Decoder Int Int (IntMap a) [Droplet a]
data Droplet a = Droplet IntSet a deriving (Show, Eq)
type Precoding = [IntSet]
precoding :: Int -> Int -> Int -> (Int, Int) -> Precoding
precoding seed messageLength extraSymbols boundaries = f 0 $ mkStdGen seed
where
f :: RandomGen g => Int -> g -> Precoding
f stage g
| stage >= extraSymbols = []
| otherwise = indices : f (stage + 1) g1
where
(indices, g1) = randomRow (messageLength + stage) boundaries g
randomRow :: RandomGen g => Int -> (Int, Int) -> g -> (IntSet, g)
randomRow rowWidth boundaries g = f S.empty g1
where
(degree, g1) = randomR boundaries g
f s g
| S.size s == degree = (s, g)
| S.member index s = f s g1
| otherwise = f (S.insert index s) g1
where
(index, g1) = randomR (0, rowWidth 1) g
droplets :: (Num a, Bits a) => Int -> Int -> Precoding -> [a] -> [Droplet a]
droplets seed maxDegree precoding message' = droplets $ mkStdGen seed
where
symbol s = foldl1' xor . map (message !!) $ S.toList s
message = message' ++ [ symbol s | s <- precoding ]
droplets g = Droplet indices (symbol indices) : droplets g1
where
(indices, g1) = randomRow (length message) (0, maxDegree) g
decoder :: (Num a, Bits a) => Int -> Precoding -> Decoder a
decoder messageLength precoding = f [ Droplet (S.insert i s) 0 | (i, s) <- zip [messageLength ..] precoding ] $ Decoder messageLength (length precoding) M.empty []
where
f [] decoder = decoder
f (a : b) decoder = f b $ fst $ decode decoder a
decode :: (Num a, Bits a) => Decoder a -> Droplet a -> (Decoder a, Maybe [a])
decode decoder droplet = decode' decoder [droplet]
decode' :: (Num a, Bits a) => Decoder a -> [Droplet a] -> (Decoder a, Maybe [a])
decode' decoder@(Decoder messageLength _ symbols _) _ | sort [ i | i <- M.keys symbols, i < messageLength ] == [0 .. messageLength 1] = (decoder, Just $ map (symbols M.!) [0 .. messageLength 1])
decode' decoder [] = (decoder, Nothing)
decode' decoder@(Decoder messageLength extraSymbols symbols droplets) (droplet' : newDroplets)
| S.size indices == 0 = decode' decoder newDroplets
| S.size indices == 1 = decode' (Decoder messageLength extraSymbols (M.insert (head $ S.toList indices) symbol symbols) old) (new ++ newDroplets)
| otherwise = decode' (Decoder messageLength extraSymbols symbols $ old) (new ++ newDroplets)
where
droplet@(Droplet indices symbol) = refineDroplet symbols droplet'
(new, old) = refineDroplets droplet droplets
refineDroplets :: (Num a, 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)
| otherwise = (new, d : old)
where
symbol2 = symbol `xor` symbol1
refineDroplet :: (Num a, 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 -> Precoding -> Int -> (Int, Bool, [Decoder Word8])
test messageLength maxDegree precoding seed = f 1 (decoder messageLength precoding) [] (droplets seed' maxDegree precoding message)
where
g = mkStdGen seed
r = randoms g
message = map fromIntegral $ take messageLength r :: [Word8]
seed' = r !! messageLength
f i decoder decoders newDroplets = case decode decoder $ head newDroplets of
(decoder', Nothing) -> f (i + 1) decoder' (decoders ++ [decoder]) (tail newDroplets)
(decoder', Just m) -> (i, m == message, decoders ++ [decoder, decoder'])
test' messageLength maxDegree extraSymbols minMaxDegree seed =
test messageLength maxDegree (precoding (seed + 1) messageLength extraSymbols minMaxDegree) seed
decoderProgress :: Decoder a -> String
decoderProgress (Decoder messageLength extraSymbols m _) = "[" ++
[ if M.member i m then 'x' else '-' | i <- [0 .. messageLength 1] ] ++ "|" ++
[ if M.member i m then 'x' else '-' | i <- [messageLength .. messageLength + extraSymbols 1] ] ++ "]"