packages feed

qr-0.2.0.0: src/Data/QR/ReedSolomon.hs

module Data.QR.ReedSolomon
  ( errorWords
  ) where

import Data.Bits
import Data.Word

raise :: Word8 -> Word8
raise w | w .&. 0x80 == 0 = w'
        | otherwise       = w' `xor` 29
  where w' = shiftL w 1

mult :: Word8 -> Word8 -> Word8
mult = go 0
  where
    go a 0 _ = a
    go a x y = go (a `xor` r) x' y'
      where
        x' = shiftR x 1
        y' = raise y
        r = if x .&. 1 == 0 then 0 else y

data Poly a = Poly
  { incDegree :: Int
  , coeff :: [a] }
  deriving (Eq, Read, Show)

mkPoly :: (Eq a, Num a) => [a] -> Poly a
mkPoly xs = Poly (length xs') xs'
  where
    xs' = dropWhile (== 0) xs

polyMod :: Poly Word8 -> Poly Word8 -> Poly Word8
polyMod p q
  | k < 0     = p
  | null as = mkPoly []
  | otherwise = polyMod (mkPoly xs) q
  where
    k = incDegree p - incDegree q
    bs = coeff q ++ replicate k 0
    as = coeff p
    a = head as
    bs' = map (mult a) bs
    xs = zipWith xor as bs'

polyAdd :: Poly Word8 -> Poly Word8 -> Poly Word8
polyAdd p q = mkPoly (zipWith xor as bs)
  where
    d = incDegree p `max` incDegree q
    as = replicate (d - incDegree p) 0 ++ coeff p
    bs = replicate (d - incDegree q) 0 ++ coeff q

polyMult' :: Word8 -> Poly Word8 -> Poly Word8
polyMult' 0 _ = mkPoly []
polyMult' a p = Poly (incDegree p) (map (mult a) (coeff p))

polyRaise :: Poly Word8 -> Poly Word8
polyRaise p = Poly (incDegree p + 1) (coeff p ++ [0])

polyMult :: Poly Word8 -> Poly Word8 -> Poly Word8
polyMult p = go (mkPoly []) (reverse (coeff p))
  where
    go rs [] _ = rs
    go rs (a : as) q =
      go (polyAdd rs (polyMult' a q))
         as (polyRaise q)

gen :: Int -> Poly Word8
gen n | n < 1 = error "invalid generator"
gen n = foldr1 polyMult [mkPoly [1,a] | a <- pows]
  where
    pows = take n $ iterate (mult 2) 1

errorPoly :: Int -> Poly Word8 -> Poly Word8
errorPoly n p = polyMod p' (gen n)
  where
    p' = Poly (incDegree p + n) (coeff p ++ replicate n 0)

errorWords :: Int -> [Word8] -> [Word8]
errorWords n = coeff . errorPoly n . mkPoly