grisette-0.12.0.0: src/Grisette/Internal/Core/Data/Class/SymFiniteBits.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Grisette.Internal.Core.Data.Class.SymFiniteBits
-- Copyright : (c) Sirui Lu 2024
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Internal.Core.Data.Class.SymFiniteBits
( lsb,
msb,
setBitTo,
bitBlast,
FromBits (..),
SymFiniteBits (..),
symBitBlast,
symLsb,
symMsb,
symPopCount,
symCountLeadingZeros,
symCountTrailingZeros,
)
where
import Data.Bits
( Bits (bit, clearBit, setBit, testBit, zeroBits, (.|.)),
FiniteBits (finiteBitSize),
)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.TypeLits (KnownNat, type (<=))
import Grisette.Internal.Core.Data.Class.BitVector
( BV (bv, bvConcat, bvSelect),
)
import Grisette.Internal.Core.Data.Class.ITEOp (ITEOp (symIte))
import Grisette.Internal.Core.Data.Class.SymEq (SymEq ((.==)))
import Grisette.Internal.SymPrim.BV (IntN, WordN)
import Grisette.Internal.SymPrim.SomeBV
( SomeBV (SomeBV),
SomeIntN,
SomeWordN,
unsafeSomeBV,
)
import Grisette.Internal.SymPrim.SymBV (SymIntN, SymWordN)
import Grisette.Internal.SymPrim.SymBool (SymBool)
import Unsafe.Coerce (unsafeCoerce)
-- | Set a bit in a concrete value to a specific value.
setBitTo :: (Bits a) => a -> Int -> Bool -> a
setBitTo v i b = if b then setBit v i else clearBit v i
-- | Bit-blast a concrete value into a list of concrete bits. The first element
-- in the resulting list corresponds to the least significant bit.
bitBlast :: (FiniteBits a) => a -> [Bool]
bitBlast x = map (testBit x) [0 .. finiteBitSize x - 1]
-- | Extract the least significant bit of a concrete value.
lsb :: (Bits a) => a -> Bool
lsb x = testBit x 0
-- | Extract the most significant bit of a concrete value.
msb :: (FiniteBits a) => a -> Bool
msb x = testBit x (finiteBitSize x - 1)
-- | Type class for assembling concrete bits to a bit-vector.
class (FiniteBits a) => FromBits a where
-- | Assembling concrete bits to a bit-vector. The first boolean value in the
-- list corresponding to the least signification value.
fromBits :: [Bool] -> a
fromBits bits
| length bits /= finiteBitSize (undefined :: a) =
error "fromBits: length mismatch"
| otherwise = foldl1 (.|.) lst
where
lst :: [a]
lst = (\(pos, b) -> if b then bit pos else zeroBits) <$> zip [0 ..] bits
instance FromBits Int
instance FromBits Int8
instance FromBits Int16
instance FromBits Int32
instance FromBits Int64
instance FromBits Word
instance FromBits Word8
instance FromBits Word16
instance FromBits Word32
instance FromBits Word64
instance (KnownNat n, 1 <= n) => FromBits (WordN n)
instance (KnownNat n, 1 <= n) => FromBits (IntN n)
instance FromBits SomeIntN where
fromBits bits
| null bits =
error "Cannot create a SomeBV from an empty list of bits."
fromBits bits = unsafeSomeBV (length bits) $ \_ -> fromBits bits
instance FromBits SomeWordN where
fromBits bits
| null bits =
error "Cannot create a SomeBV from an empty list of bits."
fromBits bits = unsafeSomeBV (length bits) $ \_ -> fromBits bits
-- | A class for symbolic finite bit operations.
class (FiniteBits a, ITEOp a) => SymFiniteBits a where
-- | Test a symbolic bit in a symbolic bit-vector.
symTestBit :: a -> Int -> SymBool
-- | Set a bit in a symbolic value to a specific value.
symSetBitTo :: a -> Int -> SymBool -> a
symSetBitTo v i b = symIte b (setBit v i) (clearBit v i)
-- | Assembling symbolic bits to a symbolic bit-vector. The first symbolic
-- boolean value in the list corresponding to the least signification value.
symFromBits :: [SymBool] -> a
instance SymFiniteBits (SomeBV SymIntN) where
symTestBit v i = bvSelect i 1 v .== bv 1 1
symFromBits bits
| null bits =
error "Cannot create a SomeBV from an empty list of bits."
symFromBits bits = unsafeSomeBV (length bits) $ \_ -> symFromBits bits
instance SymFiniteBits (SomeBV SymWordN) where
symTestBit v i = bvSelect i 1 v .== bv 1 1
symFromBits bits
| null bits =
error "Cannot create a SomeBV from an empty list of bits."
symFromBits bits = unsafeSomeBV (length bits) $ \_ -> symFromBits bits
instance (KnownNat n, 1 <= n) => SymFiniteBits (SymIntN n) where
symTestBit v = symTestBit (SomeBV v)
symSetBitTo v i b = symIte b (setBit v i) (clearBit v i)
symFromBits bits
| length bits /= finiteBitSize (undefined :: SymWordN n) =
error "symFromBits: length mismatch"
| otherwise = case foldl1 bvConcat $ SomeBV <$> reverse lst of
SomeBV r -> unsafeCoerce r
_ -> error "symFromBits: length mismatch"
where
lst :: [SymIntN 1]
lst = (\b -> symIte b 1 0) <$> bits
instance (KnownNat n, 1 <= n) => SymFiniteBits (SymWordN n) where
symTestBit v = symTestBit (SomeBV v)
symSetBitTo v i b = symIte b (setBit v i) (clearBit v i)
symFromBits bits
| length bits /= finiteBitSize (undefined :: SymWordN n) =
error "symFromBits: length mismatch"
| otherwise = case foldl1 bvConcat $ SomeBV <$> reverse lst of
SomeBV r -> unsafeCoerce r
_ -> error "symFromBits: length mismatch"
where
lst :: [SymWordN 1]
lst = (\b -> symIte b 1 0) <$> bits
-- | Bit-blast a symbolic value into a list of symbolic bits. The first element
-- in the resulting list corresponds to the least significant bit.
symBitBlast :: (SymFiniteBits a) => a -> [SymBool]
symBitBlast x = map (symTestBit x) [0 .. finiteBitSize x - 1]
-- | Extract the least significant bit of a symbolic value.
symLsb :: (SymFiniteBits a) => a -> SymBool
symLsb x = symTestBit x 0
-- | Extract the most significant bit of a symbolic value.
symMsb :: (SymFiniteBits a) => a -> SymBool
symMsb x = symTestBit x (finiteBitSize x - 1)
-- | Count the number of set bits in a symbolic value.
symPopCount :: (Num a, ITEOp a, SymFiniteBits a) => a -> a
symPopCount v = 0 * v + adderTree (map fromBool (symBitBlast v))
where
-- Convert a symbolic boolean to a numeric value (0 or 1)
-- Node: 0 * v + is a trick to assign the correct bit-width to the result.
fromBool b = 0 * v + symIte b 1 0
-- Implement binary adder tree to efficiently sum values
adderTree :: (Num a) => [a] -> a
adderTree [] = 0
adderTree [x] = x
adderTree xs = adderTree (pairwiseAdd xs)
-- Add adjacent pairs of values
pairwiseAdd :: (Num a) => [a] -> [a]
pairwiseAdd [] = []
pairwiseAdd [x] = [x]
pairwiseAdd (x : y : rest) = (x + y) : pairwiseAdd rest
-- | Count the number of leading zeros in a symbolic value.
symCountLeadingZeros :: (Num a, ITEOp a, SymFiniteBits a) => a -> a
-- Node: 0 * v + is a trick to assign the correct bit-width to the result.
symCountLeadingZeros v = 0 * v + go bits rs
where
bits = reverse $ symBitBlast v
rs = fromIntegral <$> [0 ..]
go [] (r : _) = r
go (b : bs) (r : rs) = symIte b r (go bs rs)
go _ [] = error "Should not happen"
-- | Count the number of trailing zeros in a symbolic value.
symCountTrailingZeros :: (Num a, ITEOp a, SymFiniteBits a) => a -> a
-- Node: 0 * v + is a trick to assign the correct bit-width to the result.
symCountTrailingZeros v = 0 * v + go bits rs
where
bits = symBitBlast v
rs = fromIntegral <$> [0 ..]
go [] (r : _) = r
go (b : bs) (r : rs) = symIte b r (go bs rs)
go _ [] = error "Should not happen"