module Kewar.Encoding.Error (encodeError) where

import Data.Bits (xor)
import Data.Foldable (toList)
import Data.List (foldl', nub)
import Data.Maybe (fromMaybe)
import qualified Data.Sequence as S
import Kewar.Constants (errorCorrectionCodeWordsPerBlock, fromExponent, toExponent)
import Kewar.Types (BitString, CorrectionLevel, Group, Version)
import Utils (leftPad, toBin, toDec)

-- | Returns a grouped list of Error CodeWords from a grouped Input
encodeError :: [Group] -> CorrectionLevel -> Version -> [Group]
encodeError :: [Group] -> CorrectionLevel -> Version -> [Group]
encodeError [Group]
groups CorrectionLevel
cl Version
version = (Group -> Group) -> [Group] -> [Group]
forall a b. (a -> b) -> [a] -> [b]
map (([BitString] -> [BitString]) -> Group -> Group
forall a b. (a -> b) -> [a] -> [b]
map (([BitString] -> [BitString]) -> Group -> Group)
-> ([BitString] -> [BitString]) -> Group -> Group
forall a b. (a -> b) -> a -> b
$ Version -> [BitString] -> [BitString]
encodeErrorBlock Version
requiredCodeWords) [Group]
groups
  where
    requiredCodeWords :: Version
requiredCodeWords = Version -> CorrectionLevel -> Version
errorCorrectionCodeWordsPerBlock Version
version CorrectionLevel
cl

encodeErrorBlock :: Int -> [BitString] -> [BitString]
encodeErrorBlock :: Version -> [BitString] -> [BitString]
encodeErrorBlock Version
requiredCodeWords [BitString]
block = (Version -> BitString) -> [Version] -> [BitString]
forall a b. (a -> b) -> [a] -> [b]
map (Version -> Char -> BitString -> BitString
leftPad Version
8 Char
'0' (BitString -> BitString)
-> (Version -> BitString) -> Version -> BitString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> BitString
toBin) ([Version] -> [BitString]) -> [Version] -> [BitString]
forall a b. (a -> b) -> a -> b
$ Seq Version -> [Version]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (Seq Version -> Seq Version -> Seq Version
divP Seq Version
message Seq Version
generator)
  where
    message :: Seq Version
message = [BitString] -> Seq Version
mkMessage [BitString]
block
    generator :: Seq Version
generator = Version -> Seq Version
mkGenerator Version
requiredCodeWords

-- Galois Field Polynomial whose entries are 0<=n<=255
type Polynomial = S.Seq Int

degree :: Polynomial -> Int
degree :: Seq Version -> Version
degree Seq Version
p = Seq Version -> Version
forall a. Seq a -> Version
S.length Seq Version
p Version -> Version -> Version
forall a. Num a => a -> a -> a
- Version
1

(!) :: Polynomial -> Int -> Int
(!) Seq Version
a Version
i = Version -> Maybe Version -> Version
forall a. a -> Maybe a -> a
fromMaybe Version
0 (Version -> Seq Version -> Maybe Version
forall a. Version -> Seq a -> Maybe a
S.lookup Version
i Seq Version
a)

mkPolynomial :: [Int] -> S.Seq Int
mkPolynomial :: [Version] -> Seq Version
mkPolynomial = [Version] -> Seq Version
forall a. [a] -> Seq a
S.fromList

gfSum :: Int -> Int -> Int
gfSum :: Version -> Version -> Version
gfSum Version
a Version
b = Version
a Version -> Version -> Version
forall a. Bits a => a -> a -> a
`xor` Version
b

gfProduct :: Int -> Int -> Int
gfProduct :: Version -> Version -> Version
gfProduct Version
a Version
b
  | Version
a Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
0 Bool -> Bool -> Bool
|| Version
b Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
0 = Version
0
  | Bool
otherwise = Version -> Version
fromExponent ((Version -> Version
toExponent Version
a Version -> Version -> Version
forall a. Num a => a -> a -> a
+ Version -> Version
toExponent Version
b) Version -> Version -> Version
forall a. Integral a => a -> a -> a
`mod` Version
255)

-- | Ensure two Polynomials have the same degree
normalize :: Polynomial -> Polynomial -> (Polynomial, Polynomial)
normalize :: Seq Version -> Seq Version -> (Seq Version, Seq Version)
normalize Seq Version
pa Seq Version
pb = (Seq Version -> Seq Version -> Seq Version
forall a. Seq a -> Seq a -> Seq a
(S.><) Seq Version
pa (Version -> Version -> Seq Version
forall a. Version -> a -> Seq a
S.replicate (Seq Version -> Version
degree Seq Version
pa) Version
0), Seq Version -> Seq Version -> Seq Version
forall a. Seq a -> Seq a -> Seq a
(S.><) Seq Version
pb (Version -> Version -> Seq Version
forall a. Version -> a -> Seq a
S.replicate (Seq Version -> Version
degree Seq Version
pb) Version
0))

sumP :: Polynomial -> Polynomial -> Polynomial
sumP :: Seq Version -> Seq Version -> Seq Version
sumP Seq Version
a Seq Version
b = (Version -> Version -> Version)
-> Seq Version -> Seq Version -> Seq Version
forall a b c. (a -> b -> c) -> Seq a -> Seq b -> Seq c
S.zipWith Version -> Version -> Version
gfSum Seq Version
a' Seq Version
b'
  where
    (Seq Version
a', Seq Version
b') = Seq Version -> Seq Version -> (Seq Version, Seq Version)
normalize Seq Version
a Seq Version
b

prodP :: Polynomial -> Polynomial -> Polynomial
prodP :: Seq Version -> Seq Version -> Seq Version
prodP Seq Version
a Seq Version
b = do
  let indices :: [(Version, Version)]
indices = [(Version
i, Version
j) | Version
i <- [Version
0 .. Seq Version -> Version
forall a. Seq a -> Version
S.length Seq Version
a Version -> Version -> Version
forall a. Num a => a -> a -> a
-Version
1], Version
j <- [Version
0 .. Seq Version -> Version
forall a. Seq a -> Version
S.length Seq Version
b Version -> Version -> Version
forall a. Num a => a -> a -> a
-Version
1]]
  let deg :: Version
deg = [Version] -> Version
forall (t :: * -> *) a. Foldable t => t a -> Version
length ([Version] -> Version) -> [Version] -> Version
forall a b. (a -> b) -> a -> b
$ [Version] -> [Version]
forall a. Eq a => [a] -> [a]
nub (((Version, Version) -> Version)
-> [(Version, Version)] -> [Version]
forall a b. (a -> b) -> [a] -> [b]
map ((Version -> Version -> Version) -> (Version, Version) -> Version
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Version -> Version -> Version
forall a. Num a => a -> a -> a
(+)) [(Version, Version)]
indices)
  let emp :: Seq Version
emp = [Version] -> Seq Version
mkPolynomial (Version -> Version -> [Version]
forall a. Version -> a -> [a]
replicate Version
deg Version
0)
  (Seq Version -> (Version, Version) -> Seq Version)
-> Seq Version -> [(Version, Version)] -> Seq Version
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\Seq Version
acc (Version
i, Version
j) -> Version -> Version -> Seq Version -> Seq Version
forall a. Version -> a -> Seq a -> Seq a
S.update (Version
i Version -> Version -> Version
forall a. Num a => a -> a -> a
+ Version
j) (Seq Version
acc Seq Version -> Version -> Version
! (Version
i Version -> Version -> Version
forall a. Num a => a -> a -> a
+ Version
j) Version -> Version -> Version
`gfSum` (Seq Version
a Seq Version -> Version -> Version
! Version
i Version -> Version -> Version
`gfProduct` (Seq Version
b Seq Version -> Version -> Version
! Version
j))) Seq Version
acc) Seq Version
emp [(Version, Version)]
indices

divP :: Polynomial -> Polynomial -> Polynomial
divP :: Seq Version -> Seq Version -> Seq Version
divP Seq Version
dividend Seq Version
divisor = do
  let (Seq Version
dividend', Seq Version
divisor') = Seq Version -> Seq Version -> (Seq Version, Seq Version)
normalize Seq Version
dividend Seq Version
divisor
  let r :: Seq Version
r = (Seq Version -> Version -> Seq Version)
-> Seq Version -> [Version] -> Seq Version
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Seq Version -> Seq Version -> Version -> Seq Version
forall p. Seq Version -> Seq Version -> p -> Seq Version
divisionStep Seq Version
divisor') Seq Version
dividend' [Version
1 .. (Seq Version -> Version
forall a. Seq a -> Version
S.length Seq Version
dividend)]
  Version -> Seq Version -> Seq Version
forall a. Version -> Seq a -> Seq a
S.take (Seq Version -> Version
forall a. Seq a -> Version
S.length Seq Version
r Version -> Version -> Version
forall a. Num a => a -> a -> a
- Seq Version -> Version
degree Seq Version
dividend Version -> Version -> Version
forall a. Num a => a -> a -> a
- Version
1) Seq Version
r
  where
    divisionStep :: Seq Version -> Seq Version -> p -> Seq Version
divisionStep Seq Version
pa Seq Version
pb p
_ = Version -> Seq Version -> Seq Version
forall a. Version -> Seq a -> Seq a
S.drop Version
1 (Seq Version -> Seq Version) -> Seq Version -> Seq Version
forall a b. (a -> b) -> a -> b
$ Seq Version -> Seq Version -> Seq Version
sumP Seq Version
pb Seq Version
pa'
      where
        pa' :: Seq Version
pa' = (Version -> Version -> Version) -> Seq Version -> Seq Version
forall a b. (Version -> a -> b) -> Seq a -> Seq b
S.mapWithIndex (\Version
_ Version
a -> Version
a Version -> Version -> Version
`gfProduct` (Seq Version
pb Seq Version -> Version -> Version
! Version
0)) Seq Version
pa

mkGenerator :: Int -> Polynomial
mkGenerator :: Version -> Seq Version
mkGenerator Version
1 = [Version] -> Seq Version
mkPolynomial [Version
1, Version
1]
mkGenerator Version
n = Seq Version -> Seq Version -> Seq Version
prodP (Version -> Seq Version
mkGenerator (Version
n Version -> Version -> Version
forall a. Num a => a -> a -> a
-Version
1)) ([Version] -> Seq Version
mkPolynomial [Version
1, Version -> Version
fromExponent (Version
n Version -> Version -> Version
forall a. Num a => a -> a -> a
-Version
1)])

mkMessage :: [BitString] -> Polynomial
mkMessage :: [BitString] -> Seq Version
mkMessage [BitString]
bs = [Version] -> Seq Version
mkPolynomial ((BitString -> Version) -> [BitString] -> [Version]
forall a b. (a -> b) -> [a] -> [b]
map BitString -> Version
toDec [BitString]
bs)