{-# OPTIONS_GHC -fplugin GHC.TypeLits.Extra.Solver #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE ViewPatterns #-}
#ifndef BITVEC_UNSAFE
module Data.Finitary.PackBits
#else
module Data.Finitary.PackBits.Unsafe
#endif
(
PackBits(PackedBits, Packed)
, BulkPack, exposeVector
, intoBits, outOfBits
)
where
import Data.Kind (Type)
import Data.Hashable (Hashable(..))
import GHC.Exts
import GHC.TypeNats
import qualified Data.Binary as Bin
#ifndef BITVEC_UNSAFE
import qualified Data.Bit.ThreadSafe as BV
#else
import qualified Data.Bit as BV
#endif
import CoercibleUtils (op, over, over2)
import Control.DeepSeq (NFData(..))
import Data.Finitary (Finitary(..))
import Data.Finitary.PackWords
( PackWords(PackedWords), intoWords, outOfWords )
import Data.Finite.Internal (Finite(..))
import GHC.TypeLits.Extra
import Data.Primitive.ByteArray (ByteArray(..))
import qualified Data.Vector.Unboxed.Base as VU
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import Data.Vector.Binary ()
import Data.Vector.Instances ()
newtype PackBits (a :: Type) = PackedBits (VU.Vector BV.Bit)
deriving (Eq, Show)
type role PackBits nominal
{-# COMPLETE Packed #-}
pattern Packed :: forall (a :: Type) .
(Finitary a, 1 <= Cardinality a) =>
a -> PackBits a
pattern Packed x <- (unpackBits -> x)
where Packed x = packBits x
instance (Finitary a, 1 <= Cardinality a) => Ord (PackBits a) where
{-# INLINABLE compare #-}
compare (PackedBits (BV.BitVec _ _ v1)) (PackedBits (BV.BitVec _ _ v2)) =
compare (PackedWords v1 :: PackWords a) (PackedWords v2 :: PackWords a)
instance NFData (PackBits a) where
{-# INLINE rnf #-}
rnf = rnf . op PackedBits
instance (Finitary a, 1 <= Cardinality a) => Finitary (PackBits a) where
type Cardinality (PackBits a) = Cardinality a
{-# INLINE fromFinite #-}
fromFinite = PackedBits . intoBits
{-# INLINE toFinite #-}
toFinite = outOfBits . op PackedBits
instance (Finitary a, 1 <= Cardinality a) => Bounded (PackBits a) where
{-# INLINE minBound #-}
minBound = start
{-# INLINE maxBound #-}
maxBound = end
newtype instance VU.MVector s (PackBits a) = MV_PackBits (VU.MVector s BV.Bit)
instance (Finitary a, 1 <= Cardinality a) => VGM.MVector VU.MVector (PackBits a) where
{-# INLINE basicLength #-}
basicLength = over MV_PackBits ((`div` bitLength @a) . VGM.basicLength)
{-# INLINE basicOverlaps #-}
basicOverlaps = over2 MV_PackBits VGM.basicOverlaps
{-# INLINABLE basicUnsafeSlice #-}
basicUnsafeSlice i len = over MV_PackBits (VGM.basicUnsafeSlice (i * bitLength @a) (len * bitLength @a))
{-# INLINABLE basicUnsafeNew #-}
basicUnsafeNew len = fmap MV_PackBits (VGM.basicUnsafeNew (len * bitLength @a))
{-# INLINE basicInitialize #-}
basicInitialize = VGM.basicInitialize . op MV_PackBits
{-# INLINABLE basicUnsafeRead #-}
basicUnsafeRead (MV_PackBits v) i = fmap PackedBits . VG.freeze . VGM.unsafeSlice (i * bitLength @a) (bitLength @a) $ v
{-# INLINABLE basicUnsafeWrite #-}
basicUnsafeWrite (MV_PackBits v) i (PackedBits x) = let slice = VGM.unsafeSlice (i * bitLength @a) (bitLength @a) v in
VG.unsafeCopy slice x
newtype instance VU.Vector (PackBits a) = V_PackBits (VU.Vector BV.Bit)
instance (Finitary a, 1 <= Cardinality a) => VG.Vector VU.Vector (PackBits a) where
{-# INLINE basicLength #-}
basicLength = over V_PackBits ((`div` bitLength @a) . VG.basicLength)
{-# INLINE basicUnsafeFreeze #-}
basicUnsafeFreeze = fmap V_PackBits . VG.basicUnsafeFreeze . op MV_PackBits
{-# INLINE basicUnsafeThaw #-}
basicUnsafeThaw = fmap MV_PackBits . VG.basicUnsafeThaw . op V_PackBits
{-# INLINABLE basicUnsafeSlice #-}
basicUnsafeSlice i len = over V_PackBits (VG.basicUnsafeSlice (i * bitLength @a) (len * bitLength @a))
{-# INLINABLE basicUnsafeIndexM #-}
basicUnsafeIndexM (V_PackBits v) i = pure . PackedBits . VG.unsafeSlice (i * bitLength @a) (bitLength @a) $ v
instance (Finitary a, 1 <= Cardinality a) => VU.Unbox (PackBits a)
newtype BulkPack a = BulkPack { exposeVector :: VU.Vector (PackBits a) }
deriving (NFData)
deriving instance (Finitary a, 1 <= Cardinality a) => Eq (BulkPack a)
deriving instance (Finitary a, 1 <= Cardinality a) => Ord (BulkPack a)
instance Hashable (BulkPack a) where
{-# INLINABLE hashWithSalt #-}
hashWithSalt salt = hashWithSalt salt . BV.cloneToWords . op V_PackBits . op BulkPack
instance Bin.Binary (BulkPack a) where
{-# INLINE put #-}
put = Bin.put . BV.cloneToWords . op V_PackBits . op BulkPack
{-# INLINE get #-}
get = BulkPack . V_PackBits . BV.castFromWords <$> Bin.get
type BitLength a = CLog 2 (Cardinality a)
{-# INLINE packBits #-}
packBits :: forall (a :: Type) .
(Finitary a, 1 <= Cardinality a) =>
a -> PackBits a
packBits = fromFinite . toFinite
{-# INLINE unpackBits #-}
unpackBits :: forall (a :: Type) .
(Finitary a, 1 <= Cardinality a) =>
PackBits a -> a
unpackBits = fromFinite . toFinite
{-# INLINE bitLength #-}
bitLength :: forall (a :: Type) (b :: Type) .
(Finitary a, 1 <= Cardinality a, Num b) =>
b
bitLength = fromIntegral $ natVal' @(BitLength a) proxy#
{-# INLINABLE intoBits #-}
intoBits :: forall (n :: Nat) .
(KnownNat n, 1 <= n) =>
Finite n -> VU.Vector BV.Bit
intoBits f = BV.BitVec 0 nbBits wordArray
where
wordArray :: ByteArray
wordArray = intoWords f
nbBits :: Int
nbBits = fromIntegral $ natVal' @( CLog 2 n ) proxy#
{-# INLINABLE outOfBits #-}
outOfBits :: forall (n :: Nat) .
(KnownNat n) =>
VU.Vector BV.Bit -> Finite n
outOfBits (BV.BitVec _ _ wordArray) = outOfWords @n wordArray