-----------------------------------------------------------------------------
-- |
-- Module      :  Data.SBV.Examples.CRC.CCITT_Unidir
-- Copyright   :  (c) Levent Erkok
-- License     :  BSD3
-- Maintainer  :  erkokl@gmail.com
-- Stability   :  experimental
-- Portability :  portable
--
-- Similar to CCITT. It shows that the CCITT is still HD 3
-- even if we consider only uni-directional errors
-----------------------------------------------------------------------------

module Data.SBV.Examples.CRC.CCITT_Unidir where

import Data.SBV

-- We don't have native support for 48 bits in Data.SBV
-- So, represent as 32 high-bits and 16 low
type SWord48 = (SWord32, SWord16)

extendData :: SWord48 -> SWord64
extendData (h, l) = h # l # 0

mkFrame :: SWord48 -> SWord64
mkFrame msg@(h, l) = h # l # crc msg

crc :: SWord48 -> SWord16
crc msg = res
  where msg64, divisor :: SWord64
        msg64   = extendData msg
        divisor = polynomial [16, 12, 5, 0]
        crc64 = pMod msg64 divisor
        (_, res) = split (snd (split crc64))

diffCount :: [SBool] -> [SBool] -> SWord8
diffCount xs ys = count $ zipWith (.==) xs ys
  where count []     = 0
        count (b:bs) = let r = count bs in ite b r (1+r)

-- returns true if there's a 0->1 error (1->0 is ok)
nonUnidir :: [SBool] -> [SBool] -> SBool
nonUnidir []     _      = false
nonUnidir _      []     = false
nonUnidir (a:as) (b:bs) = (bnot a &&& b) ||| nonUnidir as bs

crcUniGood :: SWord8 -> SWord48 -> SWord48 -> SBool
crcUniGood hd sent received =
     sent ./= received ==> nonUnidir frameSent frameReceived ||| diffCount frameSent frameReceived .> hd
   where frameSent     = blastLE $ mkFrame sent
         frameReceived = blastLE $ mkFrame received

-- Provable, i.e., HD is 3
ccitHDis3 :: IO ()
ccitHDis3 = print =<< prove (crcUniGood 3)

-- False; i.e., HD doesn't go to 4 just because we only look at uni-directional errors
ccitHDis4 :: IO ()
ccitHDis4 = print =<< prove (crcUniGood 4)