packages feed

fir-0.1.0.0: src/Math/Logic/Bits.hs

{-# LANGUAGE AllowAmbiguousTypes    #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE DerivingVia            #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE StandaloneDeriving     #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: Math.Logic.Bits

Alternative "Data.Bits" type class tailored for this library.

Justification: some of the methods of "Data.Bits" are unsuited for this library.
Consider for instance the class method

> shiftL :: a -> Int -> a

This function bakes in the type `Int`.
For this library, this would mean that only shifts
by Haskell-level constants are possible.
Instead, we want something which can instantiate to, e.g.:

> shiftL :: AST Word64 -> AST Word8 -> AST Word64

See also "Math.Algebra.Class" for further explanations
regarding the limitations of the standard type classes.

-}

module Math.Logic.Bits
  ( Bits(..)
  , BitShift(..)
  , BitCast(..)
  , zipBits2
  )
  where

-- base
import Prelude
  ( Int, Word )
import qualified Prelude
import qualified Data.Bits as Base
import Foreign.C.Types
import GHC.Float
  ( castWord32ToFloat , castFloatToWord32
  , castWord64ToDouble, castDoubleToWord64
  )

-- half
import Numeric.Half
  ( Half(..) )

-- fir
import Deriving.Base
  ( Base(..) ) -- newtype for deriving via base instances
import FIR.Prelude
import Math.Algebra.Class
  ( Integral )

----------------------------------------------------------------------

infixl 7 .&.
infixl 5 .|.
infixl 6 `xor`

class Bits a where
  (.&.) :: a -> a -> a
  (.|.) :: a -> a -> a
  xor   :: a -> a -> a
  complement :: a -> a
  zeroBits :: a
  -- | Reverse the order of the bits.
  bitReverse :: a -> a

instance Base.FiniteBits a => Bits (Base a) where
  (.&.) = coerce ( (Base..&.) :: a -> a -> a)
  (.|.) = coerce ( (Base..|.) :: a -> a -> a)
  xor   = coerce ( Base.xor :: a -> a -> a)
  complement = coerce (Base.complement :: a -> a)
  zeroBits   = coerce (Base.zeroBits :: a)
  bitReverse = coerce ( reverseBits :: a -> a )
    where
      reverseBits :: a -> a
      reverseBits x =
        Prelude.foldr
          ( \ i acc -> if Base.testBit x i then Base.setBit acc ( n Prelude.- 1 Prelude.- i ) else acc )
          Base.zeroBits
          [ 0 .. n Prelude.- 1 ]
        where
          n = Base.finiteBitSize x

deriving via Base Bool   instance Bits Bool
deriving via Base Word8  instance Bits Word8
deriving via Base Word16 instance Bits Word16
deriving via Base Word32 instance Bits Word32
deriving via Base Word64 instance Bits Word64
deriving via Base Word   instance Bits Word
deriving via Base Int8   instance Bits Int8
deriving via Base Int16  instance Bits Int16
deriving via Base Int32  instance Bits Int32
deriving via Base Int64  instance Bits Int64
deriving via Base Int    instance Bits Int

deriving via Base CChar   instance Bits CChar
deriving via Base CSChar  instance Bits CSChar
deriving via Base CUChar  instance Bits CUChar
deriving via Base CShort  instance Bits CShort
deriving via Base CUShort instance Bits CUShort
deriving via Base CInt    instance Bits CInt
deriving via Base CUInt   instance Bits CUInt
deriving via Base CLong   instance Bits CLong
deriving via Base CULong  instance Bits CULong
deriving via Base CLLong  instance Bits CLLong
deriving via Base CULLong instance Bits CULLong

type family BitsType (bs :: (Type,Type)) :: Type where
  BitsType '(b,_) = b
type family ShiftType (bs :: (Type,Type)) :: Type where
  ShiftType '(_,b) = b

-- | Bit-shift operations.
class (bs ~ '(BitsType bs, ShiftType bs)) => BitShift (bs :: (Type,Type)) where
  -- | Shift the first argument left by the specified number of bits.
  --
  -- Ignores the sign of the shift argument (second argument).
  --
  -- This is a logical shift: least-significant bits are set to 0.
  shiftL :: BitsType bs -> ShiftType bs -> BitsType bs
  -- | Shift the first argument right by the specified number of bits.
  --
  -- Ignores the sign of the shift argument (second argument).
  --
  -- For signed types this is an arithmetic shift (most-significant bits
  -- are filled with the sign of the first argument), and for unsigned
  -- types a logical shift (most-significant bits are set to 0),
  -- as with 'Data.Bits.shiftR'.
  shiftR :: BitsType bs -> ShiftType bs -> BitsType bs

instance (Base.Bits a, Prelude.Integral i)
  => BitShift '(Base a,i) where
  shiftL (Base a) i = Base $ Base.shiftL a (Prelude.fromIntegral i)
  shiftR (Base a) i = Base $ Base.shiftR a (Prelude.fromIntegral i)

deriving via '(Base Word8 ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Word8 ,s)
deriving via '(Base Word16,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Word16,s)
deriving via '(Base Word32,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Word32,s)
deriving via '(Base Word64,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Word64,s)
deriving via '(Base Word  ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Word  ,s)
deriving via '(Base Int8  ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Int8  ,s)
deriving via '(Base Int16 ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Int16 ,s)
deriving via '(Base Int32 ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Int32 ,s)
deriving via '(Base Int64 ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Int64 ,s)
deriving via '(Base Int   ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(Int   ,s)

deriving via '(Base CChar  ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CChar  ,s)
deriving via '(Base CSChar ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CSChar ,s)
deriving via '(Base CUChar ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CUChar ,s)
deriving via '(Base CShort ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CShort ,s)
deriving via '(Base CUShort,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CUShort,s)
deriving via '(Base CInt   ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CInt   ,s)
deriving via '(Base CUInt  ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CUInt  ,s)
deriving via '(Base CLong  ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CLong  ,s)
deriving via '(Base CULong ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CULong ,s)
deriving via '(Base CLLong ,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CLLong ,s)
deriving via '(Base CULLong,(s :: Type)) instance (Integral s, Prelude.Integral s) => BitShift '(CULLong,s)

-- | Reinterpret the bits of a value as a value of another type of the same width,
-- e.g. a 'Float' as the 'Word32' with the same bit pattern, or a signed integer as the
-- unsigned integer with the same two's complement representation.
class BitCast a b where
  bitcast :: a -> b

instance BitCast Word8  Int8   where
  bitcast = Prelude.fromIntegral
instance BitCast Word16 Int16  where
  bitcast = Prelude.fromIntegral
instance BitCast Word32 Int32  where
  bitcast = Prelude.fromIntegral
instance BitCast Word64 Int64  where
  bitcast = Prelude.fromIntegral
instance BitCast Int8   Word8  where
  bitcast = Prelude.fromIntegral
instance BitCast Int16  Word16 where
  bitcast = Prelude.fromIntegral
instance BitCast Int32  Word32 where
  bitcast = Prelude.fromIntegral
instance BitCast Int64  Word64 where
  bitcast = Prelude.fromIntegral

instance BitCast Word16 Half where
  bitcast = coerce
instance BitCast Word32 Float where
  bitcast = castWord32ToFloat
instance BitCast Word64 Double where
  bitcast = castWord64ToDouble
instance BitCast Half   Word16 where
  bitcast = coerce
instance BitCast Float  Word32 where
  bitcast = castFloatToWord32
instance BitCast Double Word64 where
  bitcast = castDoubleToWord64
instance BitCast Int16 Half where
  bitcast = coerce . ( Prelude.fromIntegral :: Int16 -> Word16 )
instance BitCast Int32 Float where
  bitcast = castWord32ToFloat . ( Prelude.fromIntegral :: Int32 -> Word32 )
instance BitCast Int64 Double where
  bitcast = castWord64ToDouble . ( Prelude.fromIntegral :: Int64 -> Word64 )
instance BitCast Half   Int16 where
  bitcast = ( Prelude.fromIntegral :: Word16 -> Int16 ) . coerce
instance BitCast Float  Int32 where
  bitcast = ( Prelude.fromIntegral :: Word32 -> Int32 ) . castFloatToWord32
instance BitCast Double Int64 where
  bitcast = ( Prelude.fromIntegral :: Word64 -> Int64 ) . castDoubleToWord64

zipBits2 :: Bits a
         => (Bool -> Bool -> Bool)
         -> (a    -> a    -> a   )
zipBits2 f
  = case ( f False False, f False True, f True False, f True True ) of
      ( False, False, False, False ) -> \ _ _ -> zeroBits
      ( False, False, False, True  ) ->          (.&.)
      ( False, False, True , False ) -> \ x y -> x .&. complement y
      ( False, False, True , True  ) -> \ x _ -> x
      ( False, True , False, False ) -> \ x y -> complement x .&. y
      ( False, True , False, True  ) -> \ _ y -> y
      ( False, True , True , False ) ->          xor
      ( False, True , True , True  ) ->          (.|.)
      ( True , False, False, False ) -> \ x y -> complement ( x .|. y )
      ( True , False, False, True  ) -> \ x y -> complement ( x `xor` y )
      ( True , False, True , False ) -> \ _ y -> complement y
      ( True , False, True , True  ) -> \ x y -> x .|. complement y
      ( True , True , False, False ) -> \ x _ -> complement x
      ( True , True , False, True  ) -> \ x y -> complement x .|. y
      ( True , True , True , False ) -> \ x y -> complement ( x .&. y )
      ( True , True , True , True  ) -> \ _ _ -> complement zeroBits