packages feed

cardano-crypto-1.3.0: src/Crypto/Math/Bits.hs

-- copied & adapted from cryptic
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE ConstraintKinds     #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE Rank2Types          #-}
{-# LANGUAGE GADTs               #-}
module Crypto.Math.Bits
    ( FBits(..)
    , FBitsK(..)
    , SizeValid
    , splitHalf
    , append
    , dropBitsOnRight
    , dropBitsOnLeft
    ) where

import Data.Bits
import Data.Proxy
import GHC.Natural
import GHC.TypeLits

-- | Finite Bits
--
-- Sadly Bits is taken by Bits operation
data FBits (n :: Nat) = FBits { unFBits :: Natural }
    deriving (Show,Eq,Ord)

data FBitsK = FBitsK (forall n . (KnownNat n, SizeValid n) => FBits n)

type SizeValid n = (KnownNat n, 1 <= n)

toFBits :: SizeValid n => Natural -> FBits n
toFBits nat = FBits nat .&. allOne

instance SizeValid n => Enum (FBits n) where
    toEnum i | i < 0 && fromIntegral i > unFBits maxi = error "FBits n not within bound"
             | otherwise                              = FBits (fromIntegral i)
      where maxi = allOne :: FBits n
    fromEnum (FBits n) = fromEnum n

instance SizeValid n => Bounded (FBits n) where
    minBound = FBits 0
    maxBound = allOne

instance SizeValid n => Num (FBits n) where
    fromInteger = toFBits . fromInteger
    (+) (FBits a) (FBits b) = toFBits (a + b)
    (-) (FBits a) (FBits b) = toFBits (a - b)
    (*) (FBits a) (FBits b) = toFBits (a * b)
    abs = id
    signum (FBits a)
        | a == 0    = FBits 0
        | otherwise = FBits 1

instance SizeValid n => Bits (FBits n) where
    (.&.) (FBits a) (FBits b) = FBits (a .&. b)
    (.|.) (FBits a) (FBits b) = FBits (a .|. b)
    xor (FBits a) (FBits b) = FBits (a `xor` b)
    shiftR (FBits a) n = FBits (a `shiftR` n)
    shiftL (FBits a) n = toFBits (a `shiftL` n) -- shiftL can overflow here, so explicit safe reconstruction from natural
    rotateL a i = ((a `shiftL` i) .|. (a `shiftR` (n - i)))
      where n = fromIntegral $ natVal (Proxy :: Proxy n)
    rotateR a i = ((a `shiftR` i) .|. (a `shiftL` (n - i)))
      where n = fromIntegral $ natVal (Proxy :: Proxy n)
    zeroBits = FBits 0
    bit i
        | i < 0 || fromIntegral i >= natVal (Proxy :: Proxy n) = FBits 0
        | otherwise                                            = FBits (2^i)
    testBit (FBits a) i = testBit a i
    bitSize _ = fromIntegral $ natVal (Proxy :: Proxy n)
    bitSizeMaybe _ = Just $ fromIntegral $ natVal (Proxy :: Proxy n)
    isSigned _ = False
    complement a = allOne `xor` a
    popCount (FBits a) = popCount a

allOne :: forall n . SizeValid n => FBits n
allOne = FBits (2 ^ n - 1)
  where n = natVal (Proxy :: Proxy n)

splitHalf :: forall m n . (SizeValid n, (n * 2) ~ m) => FBits m -> (FBits n, FBits n)
splitHalf (FBits a) = (FBits (a `shiftR` n), toFBits a)
  where n = fromIntegral $ natVal (Proxy :: Proxy n)

-- | Append 2 FBits together where the left member is shifted to make room for the right
-- element.
--
-- e.g. append (0x1 :: FBits 1) (0x70 :: FBits 7) = 0xf0 :: FBits 8
append :: forall m n r . (SizeValid m, SizeValid n, SizeValid r, (m + n) ~ r)
       => FBits n -> FBits m -> FBits r
append (FBits a) (FBits b) =
    FBits ((a `shiftL` m) .|.  b)
  where m = fromIntegral $ natVal (Proxy :: Proxy m)

--appendK :: FBitsK -> FBitsK -> FBitsK
--appendK (FBitsK a) (FBitsK b) = FBitsK (a `append` b)
    -- FBits ((a `shiftL` m) .|.  b)

dropBitsOnRight :: forall a b diff . (KnownNat diff, b <= a, SizeValid a, SizeValid b, (a - b) ~ diff)
              => FBits a
              -> FBits b
dropBitsOnRight (FBits a) = FBits (a `shiftR` fromInteger (natVal (Proxy :: Proxy diff)))

dropBitsOnLeft :: forall a b . (KnownNat b, b <= a, SizeValid a, SizeValid b)
             => FBits a
             -> FBits b
dropBitsOnLeft (FBits a) = toFBits a