grisette-0.4.0.0: src/Grisette/Core/Data/BV.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
-- |
-- Module : Grisette.Core.Data.BV
-- Copyright : (c) Sirui Lu 2021-2023
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Core.Data.BV
( BitwidthMismatch (..),
IntN (..),
WordN (..),
SomeIntN (..),
SomeWordN (..),
unarySomeIntN,
unarySomeIntNR1,
binSomeIntN,
binSomeIntNR1,
binSomeIntNR2,
unarySomeWordN,
unarySomeWordNR1,
binSomeWordN,
binSomeWordNR1,
binSomeWordNR2,
)
where
import Control.Applicative (Alternative ((<|>)))
import Control.DeepSeq (NFData (rnf))
import Control.Exception
( ArithException (Overflow),
Exception (displayException),
throw,
)
import Data.Bits
( Bits
( bit,
bitSize,
bitSizeMaybe,
clearBit,
complement,
complementBit,
isSigned,
popCount,
rotate,
rotateL,
rotateR,
setBit,
shift,
shiftL,
shiftR,
testBit,
unsafeShiftL,
unsafeShiftR,
xor,
zeroBits,
(.&.),
(.|.)
),
FiniteBits (countLeadingZeros, countTrailingZeros, finiteBitSize),
)
import Data.Hashable (Hashable (hashWithSalt))
import Data.Maybe (fromMaybe, isJust)
import Data.Proxy (Proxy (Proxy))
import Data.Typeable (type (:~:) (Refl))
import GHC.Enum
( boundedEnumFrom,
boundedEnumFromThen,
predError,
succError,
toEnumError,
)
import GHC.Generics (Generic)
import GHC.Read
( Read (readListPrec, readPrec),
parens,
readListDefault,
readListPrecDefault,
readNumber,
)
import GHC.Real ((%))
import GHC.TypeNats
( KnownNat,
Nat,
natVal,
sameNat,
type (+),
type (<=),
)
import Grisette.Core.Data.Class.BitVector
( BV (bvConcat, bvExt, bvSelect, bvSext, bvZext),
SizedBV
( sizedBVConcat,
sizedBVExt,
sizedBVSelect,
sizedBVSext,
sizedBVZext
),
)
import Grisette.Core.Data.Class.SignConversion
( SignConversion (toSigned, toUnsigned),
)
import Grisette.Core.Data.Class.SymRotate
( DefaultFiniteBitsSymRotate (DefaultFiniteBitsSymRotate),
SymRotate,
)
import Grisette.Core.Data.Class.SymShift
( DefaultFiniteBitsSymShift (DefaultFiniteBitsSymShift),
SymShift,
)
import Grisette.Utils.Parameterized
( KnownProof (KnownProof),
LeqProof (LeqProof),
knownAdd,
leqAddPos,
unsafeKnownProof,
unsafeLeqProof,
)
import Language.Haskell.TH.Syntax (Lift (liftTyped))
import Numeric (showHex, showIntAtBase)
import qualified Test.QuickCheck as QC
import Text.ParserCombinators.ReadP (string)
import Text.ParserCombinators.ReadPrec
( ReadPrec,
get,
look,
pfail,
)
import Text.Read (lift)
import qualified Text.Read.Lex as L
data BitwidthMismatch = BitwidthMismatch
deriving (Show, Eq, Ord, Generic)
instance Exception BitwidthMismatch where
displayException BitwidthMismatch = "Bit width does not match"
-- |
-- Symbolic unsigned bit vectors.
newtype WordN (n :: Nat) = WordN {unWordN :: Integer}
deriving (Eq, Ord, Generic, Lift, Hashable, NFData)
-- |
-- A non-indexed version of 'WordN'.
data SomeWordN where
SomeWordN :: (KnownNat n, 1 <= n) => WordN n -> SomeWordN
unarySomeWordN :: (forall n. (KnownNat n, 1 <= n) => WordN n -> r) -> SomeWordN -> r
unarySomeWordN op (SomeWordN (w :: WordN w)) = op w
{-# INLINE unarySomeWordN #-}
unarySomeWordNR1 :: (forall n. (KnownNat n, 1 <= n) => WordN n -> WordN n) -> SomeWordN -> SomeWordN
unarySomeWordNR1 op (SomeWordN (w :: WordN w)) = SomeWordN $ op w
{-# INLINE unarySomeWordNR1 #-}
binSomeWordN :: (forall n. (KnownNat n, 1 <= n) => WordN n -> WordN n -> r) -> SomeWordN -> SomeWordN -> r
binSomeWordN op (SomeWordN (l :: WordN l)) (SomeWordN (r :: WordN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> op l r
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeWordN #-}
binSomeWordNR1 :: (forall n. (KnownNat n, 1 <= n) => WordN n -> WordN n -> WordN n) -> SomeWordN -> SomeWordN -> SomeWordN
binSomeWordNR1 op (SomeWordN (l :: WordN l)) (SomeWordN (r :: WordN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> SomeWordN $ op l r
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeWordNR1 #-}
binSomeWordNR2 :: (forall n. (KnownNat n, 1 <= n) => WordN n -> WordN n -> (WordN n, WordN n)) -> SomeWordN -> SomeWordN -> (SomeWordN, SomeWordN)
binSomeWordNR2 op (SomeWordN (l :: WordN l)) (SomeWordN (r :: WordN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl ->
case op l r of
(a, b) -> (SomeWordN a, SomeWordN b)
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeWordNR2 #-}
instance Eq SomeWordN where
SomeWordN (l :: WordN l) == SomeWordN (r :: WordN r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l == r
Nothing -> False
{-# INLINE (==) #-}
SomeWordN (l :: WordN l) /= SomeWordN (r :: WordN r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l /= r
Nothing -> True
{-# INLINE (/=) #-}
instance Ord SomeWordN where
(<=) = binSomeWordN (<=)
{-# INLINE (<=) #-}
(<) = binSomeWordN (<)
{-# INLINE (<) #-}
(>=) = binSomeWordN (>=)
{-# INLINE (>=) #-}
(>) = binSomeWordN (>)
{-# INLINE (>) #-}
max = binSomeWordNR1 max
{-# INLINE max #-}
min = binSomeWordNR1 min
{-# INLINE min #-}
compare = binSomeWordN compare
{-# INLINE compare #-}
instance Lift SomeWordN where
liftTyped (SomeWordN w) = [||SomeWordN w||]
instance Hashable SomeWordN where
s `hashWithSalt` (SomeWordN (w :: WordN n)) = s `hashWithSalt` natVal (Proxy @n) `hashWithSalt` w
instance NFData SomeWordN where
rnf (SomeWordN w) = rnf w
instance (KnownNat n, 1 <= n) => Show (WordN n) where
show (WordN w) = if (bitwidth `mod` 4) == 0 then hexRepPre ++ hexRep else binRepPre ++ binRep
where
bitwidth = natVal (Proxy :: Proxy n)
hexRepPre = "0x" ++ replicate (fromIntegral (bitwidth `div` 4) - length hexRep) '0'
hexRep = showHex w ""
binRepPre = "0b" ++ replicate (fromIntegral bitwidth - length binRep) '0'
binRep = showIntAtBase 2 (\x -> if x == 0 then '0' else '1') w ""
convertInt :: (Num a) => L.Lexeme -> ReadPrec a
convertInt (L.Number n)
| Just i <- L.numberToInteger n = return (fromInteger i)
convertInt _ = pfail
readBinary :: (Num a) => ReadPrec a
readBinary = parens $ do
r0 <- look
case r0 of
('-' : _) -> do
_ <- get
negate <$> parens parse0b
_ -> parse0b
where
isDigit c = isJust (valDig c)
valDigit c = fromMaybe 0 (valDig c)
valDig '0' = Just 0
valDig '1' = Just 1
valDig _ = Nothing
parse0b = do
_ <- Text.Read.lift $ string "0b"
fromInteger <$> Text.Read.lift (L.readIntP 2 isDigit valDigit)
instance (KnownNat n, 1 <= n) => Read (WordN n) where
readPrec = readNumber convertInt <|> readBinary
readListPrec = readListPrecDefault
readList = readListDefault
instance Show SomeWordN where
show (SomeWordN w) = show w
-- |
-- Symbolic signed bit vectors.
newtype IntN (n :: Nat) = IntN {unIntN :: Integer}
deriving (Eq, Generic, Lift, Hashable, NFData)
-- |
-- A non-indexed version of 'IntN'.
data SomeIntN where
SomeIntN :: (KnownNat n, 1 <= n) => IntN n -> SomeIntN
unarySomeIntN :: (forall n. (KnownNat n, 1 <= n) => IntN n -> r) -> SomeIntN -> r
unarySomeIntN op (SomeIntN (w :: IntN w)) = op w
{-# INLINE unarySomeIntN #-}
unarySomeIntNR1 :: (forall n. (KnownNat n, 1 <= n) => IntN n -> IntN n) -> SomeIntN -> SomeIntN
unarySomeIntNR1 op (SomeIntN (w :: IntN w)) = SomeIntN $ op w
{-# INLINE unarySomeIntNR1 #-}
binSomeIntN :: (forall n. (KnownNat n, 1 <= n) => IntN n -> IntN n -> r) -> SomeIntN -> SomeIntN -> r
binSomeIntN op (SomeIntN (l :: IntN l)) (SomeIntN (r :: IntN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> op l r
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeIntN #-}
binSomeIntNR1 :: (forall n. (KnownNat n, 1 <= n) => IntN n -> IntN n -> IntN n) -> SomeIntN -> SomeIntN -> SomeIntN
binSomeIntNR1 op (SomeIntN (l :: IntN l)) (SomeIntN (r :: IntN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> SomeIntN $ op l r
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeIntNR1 #-}
binSomeIntNR2 :: (forall n. (KnownNat n, 1 <= n) => IntN n -> IntN n -> (IntN n, IntN n)) -> SomeIntN -> SomeIntN -> (SomeIntN, SomeIntN)
binSomeIntNR2 op (SomeIntN (l :: IntN l)) (SomeIntN (r :: IntN r)) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl ->
case op l r of
(a, b) -> (SomeIntN a, SomeIntN b)
Nothing -> throw BitwidthMismatch
{-# INLINE binSomeIntNR2 #-}
instance Eq SomeIntN where
SomeIntN (l :: IntN l) == SomeIntN (r :: IntN r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l == r
Nothing -> False
{-# INLINE (==) #-}
SomeIntN (l :: IntN l) /= SomeIntN (r :: IntN r) =
case sameNat (Proxy @l) (Proxy @r) of
Just Refl -> l /= r
Nothing -> True
{-# INLINE (/=) #-}
instance Ord SomeIntN where
(<=) = binSomeIntN (<=)
{-# INLINE (<=) #-}
(<) = binSomeIntN (<)
{-# INLINE (<) #-}
(>=) = binSomeIntN (>=)
{-# INLINE (>=) #-}
(>) = binSomeIntN (>)
{-# INLINE (>) #-}
max = binSomeIntNR1 max
{-# INLINE max #-}
min = binSomeIntNR1 min
{-# INLINE min #-}
compare = binSomeIntN compare
{-# INLINE compare #-}
instance Lift SomeIntN where
liftTyped (SomeIntN w) = [||SomeIntN w||]
instance Hashable SomeIntN where
s `hashWithSalt` (SomeIntN (w :: IntN n)) = s `hashWithSalt` natVal (Proxy @n) `hashWithSalt` w
instance NFData SomeIntN where
rnf (SomeIntN w) = rnf w
instance (KnownNat n, 1 <= n) => Show (IntN n) where
show (IntN w) = if (bitwidth `mod` 4) == 0 then hexRepPre ++ hexRep else binRepPre ++ binRep
where
bitwidth = natVal (Proxy :: Proxy n)
hexRepPre = "0x" ++ replicate (fromIntegral (bitwidth `div` 4) - length hexRep) '0'
hexRep = showHex w ""
binRepPre = "0b" ++ replicate (fromIntegral bitwidth - length binRep) '0'
binRep = showIntAtBase 2 (\x -> if x == 0 then '0' else '1') w ""
instance (KnownNat n, 1 <= n) => Read (IntN n) where
readPrec = readNumber convertInt <|> readBinary
readListPrec = readListPrecDefault
readList = readListDefault
instance Show SomeIntN where
show (SomeIntN w) = show w
instance (KnownNat n, 1 <= n) => Bits (WordN n) where
WordN a .&. WordN b = WordN (a .&. b)
WordN a .|. WordN b = WordN (a .|. b)
WordN a `xor` WordN b = WordN (a `xor` b)
complement a = maxBound `xor` a
-- shift use default implementation
-- rotate use default implementation
zeroBits = WordN 0
bit i
| i < 0 || i >= fromIntegral (natVal (Proxy :: Proxy n)) = zeroBits
| otherwise = WordN (bit i)
-- setBit use default implementation
clearBit (WordN a) i = WordN (clearBit a i)
-- complementBit use default implementation
testBit (WordN a) = testBit a
bitSizeMaybe = Just . finiteBitSize
bitSize = finiteBitSize
isSigned _ = False
shiftL w i | i >= finiteBitSize w = 0
shiftL (WordN a) i = WordN (a `shiftL` i) .&. maxBound
-- unsafeShiftL use default implementation
shiftR w i | i >= finiteBitSize w = 0
shiftR (WordN a) i = WordN (a `shiftR` i)
-- unsafeShiftR use default implementation
rotateL a 0 = a
rotateL (WordN a) k
| k >= n = rotateL (WordN a) (k `mod` n)
| otherwise = WordN $ l + h
where
n = fromIntegral $ natVal (Proxy :: Proxy n)
s = n - k
l = a `shiftR` s
h = (a - (l `shiftL` s)) `shiftL` k
rotateR a 0 = a
rotateR (WordN a) k
| k >= n = rotateR (WordN a) (k `mod` n)
| otherwise = WordN $ l + h
where
n = fromIntegral $ natVal (Proxy :: Proxy n)
s = n - k
l = a `shiftR` k
h = (a - (l `shiftL` k)) `shiftL` s
popCount (WordN n) = popCount n
instance Bits SomeWordN where
(.&.) = binSomeWordNR1 (.&.)
(.|.) = binSomeWordNR1 (.|.)
xor = binSomeWordNR1 xor
complement = unarySomeWordNR1 complement
shift s i = unarySomeWordNR1 (`shift` i) s
rotate s i = unarySomeWordNR1 (`rotate` i) s
zeroBits = error "zeroBits is not defined for SomeWordN as no bitwidth is known"
bit = error "bit is not defined for SomeWordN as no bitwidth is known"
setBit s i = unarySomeWordNR1 (`setBit` i) s
clearBit s i = unarySomeWordNR1 (`clearBit` i) s
complementBit s i = unarySomeWordNR1 (`complementBit` i) s
testBit s i = unarySomeWordN (`testBit` i) s
bitSizeMaybe = Just . finiteBitSize
bitSize = finiteBitSize
isSigned _ = False
shiftL s i = unarySomeWordNR1 (`shiftL` i) s
unsafeShiftL s i = unarySomeWordNR1 (`unsafeShiftL` i) s
shiftR s i = unarySomeWordNR1 (`shiftR` i) s
unsafeShiftR s i = unarySomeWordNR1 (`unsafeShiftR` i) s
rotateL s i = unarySomeWordNR1 (`rotateL` i) s
rotateR s i = unarySomeWordNR1 (`rotateR` i) s
popCount = unarySomeWordN popCount
instance (KnownNat n, 1 <= n) => FiniteBits (WordN n) where
finiteBitSize _ = fromIntegral (natVal (Proxy :: Proxy n))
instance FiniteBits SomeWordN where
finiteBitSize (SomeWordN (n :: WordN n)) = fromIntegral $ natVal n
countLeadingZeros = unarySomeWordN countLeadingZeros
countTrailingZeros = unarySomeWordN countTrailingZeros
instance (KnownNat n, 1 <= n) => Bounded (WordN n) where
maxBound = WordN ((1 `shiftL` fromIntegral (natVal (Proxy :: Proxy n))) - 1)
minBound = WordN 0
instance (KnownNat n, 1 <= n) => Enum (WordN n) where
succ x
| x /= maxBound = x + 1
| otherwise = succError $ "WordN " ++ show (natVal (Proxy :: Proxy n))
pred x
| x /= minBound = x - 1
| otherwise = predError $ "WordN " ++ show (natVal (Proxy :: Proxy n))
toEnum i
| i >= 0 && toInteger i <= toInteger (maxBound :: WordN n) = WordN (toInteger i)
| otherwise = toEnumError ("WordN " ++ show (natVal (Proxy :: Proxy n))) i (minBound :: WordN n, maxBound :: WordN n)
fromEnum (WordN n) = fromEnum n
enumFrom = boundedEnumFrom
{-# INLINE enumFrom #-}
enumFromThen = boundedEnumFromThen
{-# INLINE enumFromThen #-}
instance Enum SomeWordN where
toEnum = error "SomeWordN is not really a Enum type as the bit width is unknown, please consider using WordN instead"
fromEnum = error "SomeWordN is not really a Enum type as the bit width is unknown, please consider using WordN instead"
instance (KnownNat n, 1 <= n) => Real (WordN n) where
toRational (WordN n) = n % 1
instance Real SomeWordN where
toRational = unarySomeWordN toRational
instance (KnownNat n, 1 <= n) => Integral (WordN n) where
quot (WordN x) (WordN y) = WordN (x `quot` y)
rem (WordN x) (WordN y) = WordN (x `rem` y)
quotRem (WordN x) (WordN y) = case quotRem x y of
(q, r) -> (WordN q, WordN r)
div = quot
mod = rem
divMod = quotRem
toInteger (WordN n) = n
instance Integral SomeWordN where
quot = binSomeWordNR1 quot
rem = binSomeWordNR1 rem
quotRem = binSomeWordNR2 quotRem
div = binSomeWordNR1 div
mod = binSomeWordNR1 mod
divMod = binSomeWordNR2 divMod
toInteger = unarySomeWordN toInteger
instance (KnownNat n, 1 <= n) => Num (WordN n) where
WordN x + WordN y = WordN (x + y) .&. maxBound
WordN x * WordN y = WordN (x * y) .&. maxBound
WordN x - WordN y
| x >= y = WordN (x - y)
| otherwise = WordN ((1 `shiftL` fromIntegral (natVal (Proxy :: Proxy n))) + x - y)
negate (WordN 0) = WordN 0
negate a = complement a + WordN 1
abs x = x
signum (WordN 0) = 0
signum _ = 1
fromInteger !x
| x == 0 = WordN 0
| x > 0 = WordN (x .&. unWordN (maxBound :: WordN n))
| otherwise = -fromInteger (-x)
instance Num SomeWordN where
(+) = binSomeWordNR1 (+)
(-) = binSomeWordNR1 (-)
(*) = binSomeWordNR1 (*)
negate = unarySomeWordNR1 negate
abs = unarySomeWordNR1 abs
signum = unarySomeWordNR1 signum
fromInteger = error "fromInteger is not defined for SomeWordN as no bitwidth is known"
instance (KnownNat n, 1 <= n) => QC.Arbitrary (WordN n) where
arbitrary = QC.arbitrarySizedBoundedIntegral
-- QC.shrinkIntegral assumes that 2 is representable by the number, which is
-- not the case for 1-bit bit vector.
shrink i
| i == 0 = []
| i == 1 = [0]
| otherwise = QC.shrinkIntegral i
minusOneIntN :: forall proxy n. (KnownNat n) => proxy n -> IntN n
minusOneIntN _ = IntN (1 `shiftL` fromIntegral (natVal (Proxy :: Proxy n)) - 1)
instance (KnownNat n, 1 <= n) => Bits (IntN n) where
IntN a .&. IntN b = IntN (a .&. b)
IntN a .|. IntN b = IntN (a .|. b)
IntN a `xor` IntN b = IntN (a `xor` b)
complement a = minusOneIntN (Proxy :: Proxy n) `xor` a
-- shift use default implementation
-- rotate use default implementation
zeroBits = IntN 0
bit i = IntN (unWordN (bit i :: WordN n))
-- setBit use default implementation
clearBit (IntN a) i = IntN (clearBit a i)
-- complementBit use default implementation
testBit (IntN a) = testBit a
bitSizeMaybe = Just . finiteBitSize
bitSize = finiteBitSize
isSigned _ = True
shiftL (IntN a) i = IntN (unWordN $ (WordN a :: WordN n) `shiftL` i)
-- unsafeShiftL use default implementation
shiftR i 0 = i
shiftR (IntN i) k
| k >= n = if b then IntN (maxi - 1) else IntN 0
| otherwise = if b then IntN (maxi - noi + (i `shiftR` k)) else IntN (i `shiftR` k)
where
b = testBit i (n - 1)
n = fromIntegral $ natVal (Proxy :: Proxy n)
maxi = (1 :: Integer) `shiftL` n
noi = (1 :: Integer) `shiftL` (n - k)
-- unsafeShiftR use default implementation
rotateL (IntN i) k = IntN $ unWordN $ rotateL (WordN i :: WordN n) k
rotateR (IntN i) k = IntN $ unWordN $ rotateR (WordN i :: WordN n) k
popCount (IntN i) = popCount i
instance Bits SomeIntN where
(.&.) = binSomeIntNR1 (.&.)
(.|.) = binSomeIntNR1 (.|.)
xor = binSomeIntNR1 xor
complement = unarySomeIntNR1 complement
shift s i = unarySomeIntNR1 (`shift` i) s
rotate s i = unarySomeIntNR1 (`rotate` i) s
zeroBits = error "zeroBits is not defined for SomeIntN as no bitwidth is known"
bit = error "bit is not defined for SomeIntN as no bitwidth is known"
setBit s i = unarySomeIntNR1 (`setBit` i) s
clearBit s i = unarySomeIntNR1 (`clearBit` i) s
complementBit s i = unarySomeIntNR1 (`complementBit` i) s
testBit s i = unarySomeIntN (`testBit` i) s
bitSizeMaybe = Just . finiteBitSize
bitSize = finiteBitSize
isSigned _ = False
shiftL s i = unarySomeIntNR1 (`shiftL` i) s
unsafeShiftL s i = unarySomeIntNR1 (`unsafeShiftL` i) s
shiftR s i = unarySomeIntNR1 (`shiftR` i) s
unsafeShiftR s i = unarySomeIntNR1 (`unsafeShiftR` i) s
rotateL s i = unarySomeIntNR1 (`rotateL` i) s
rotateR s i = unarySomeIntNR1 (`rotateR` i) s
popCount = unarySomeIntN popCount
instance (KnownNat n, 1 <= n) => FiniteBits (IntN n) where
finiteBitSize _ = fromIntegral (natVal (Proxy :: Proxy n))
instance FiniteBits SomeIntN where
finiteBitSize (SomeIntN (n :: IntN n)) = fromIntegral $ natVal n
countLeadingZeros = unarySomeIntN countLeadingZeros
countTrailingZeros = unarySomeIntN countTrailingZeros
instance (KnownNat n, 1 <= n) => Bounded (IntN n) where
maxBound = IntN (1 `shiftL` (fromIntegral (natVal (Proxy :: Proxy n)) - 1) - 1)
minBound = maxBound + 1
instance (KnownNat n, 1 <= n) => Enum (IntN n) where
succ x
| x /= maxBound = x + 1
| otherwise = succError $ "IntN " ++ show (natVal (Proxy :: Proxy n))
pred x
| x /= minBound = x - 1
| otherwise = predError $ "IntN " ++ show (natVal (Proxy :: Proxy n))
toEnum i
| i >= fromIntegral (minBound :: IntN n) && i <= fromIntegral (maxBound :: IntN n) = fromIntegral i
| otherwise = toEnumError ("IntN " ++ show (natVal (Proxy :: Proxy n))) i (minBound :: WordN n, maxBound :: WordN n)
fromEnum = fromEnum . toInteger
enumFrom = boundedEnumFrom
{-# INLINE enumFrom #-}
enumFromThen = boundedEnumFromThen
{-# INLINE enumFromThen #-}
instance Enum SomeIntN where
toEnum = error "SomeIntN is not really a Enum type as the bit width is unknown, please consider using IntN instead"
fromEnum = error "SomeIntN is not really a Enum type as the bit width is unknown, please consider using IntN instead"
instance (KnownNat n, 1 <= n) => Real (IntN n) where
toRational i = toInteger i % 1
instance Real SomeIntN where
toRational = unarySomeIntN toRational
instance (KnownNat n, 1 <= n) => Integral (IntN n) where
quot x y =
if x == minBound && y == -1
then throw Overflow
else fromInteger (toInteger x `quot` toInteger y)
rem x y = fromInteger (toInteger x `rem` toInteger y)
quotRem x y =
if x == minBound && y == -1
then throw Overflow
else case quotRem (toInteger x) (toInteger y) of
(q, r) -> (fromInteger q, fromInteger r)
div x y =
if x == minBound && y == -1
then throw Overflow
else fromInteger (toInteger x `div` toInteger y)
mod x y = fromInteger (toInteger x `mod` toInteger y)
divMod x y =
if x == minBound && y == -1
then throw Overflow
else case divMod (toInteger x) (toInteger y) of
(q, r) -> (fromInteger q, fromInteger r)
toInteger i@(IntN n) = case signum i of
0 -> 0
-1 ->
let x = negate i
in if signum x == -1 then -n else negate (toInteger x)
1 -> n
_ -> undefined
instance Integral SomeIntN where
quot = binSomeIntNR1 quot
rem = binSomeIntNR1 rem
quotRem = binSomeIntNR2 quotRem
div = binSomeIntNR1 div
mod = binSomeIntNR1 mod
divMod = binSomeIntNR2 divMod
toInteger = unarySomeIntN toInteger
instance (KnownNat n, 1 <= n) => Num (IntN n) where
IntN x + IntN y = IntN (x + y) .&. minusOneIntN (Proxy :: Proxy n)
IntN x * IntN y = IntN (x * y) .&. minusOneIntN (Proxy :: Proxy n)
IntN x - IntN y
| x >= y = IntN (x - y)
| otherwise = IntN ((1 `shiftL` fromIntegral (natVal (Proxy :: Proxy n))) + x - y)
negate (IntN 0) = IntN 0
negate a = complement a + IntN 1
abs x = if testBit x (fromIntegral $ natVal (Proxy :: Proxy n) - 1) then negate x else x
signum (IntN 0) = IntN 0
signum i = if testBit i (fromIntegral $ natVal (Proxy :: Proxy n) - 1) then -1 else 1
fromInteger !x = IntN $ if v >= 0 then v else (1 `shiftL` n) + v
where
v = unWordN (fromInteger (x + maxn) :: WordN n) - maxn
n = fromIntegral (natVal (Proxy :: Proxy n))
maxn = 1 `shiftL` (n - 1) - 1
instance Num SomeIntN where
(+) = binSomeIntNR1 (+)
(-) = binSomeIntNR1 (-)
(*) = binSomeIntNR1 (*)
negate = unarySomeIntNR1 negate
abs = unarySomeIntNR1 abs
signum = unarySomeIntNR1 signum
fromInteger = error "fromInteger is not defined for SomeIntN as no bitwidth is known"
instance (KnownNat n, 1 <= n) => Ord (IntN n) where
IntN a <= IntN b
| as && not bs = True
| not as && bs = False
| otherwise = a <= b
where
n = fromIntegral (natVal (Proxy :: Proxy n))
as = testBit a (n - 1)
bs = testBit b (n - 1)
instance (KnownNat n, 1 <= n) => QC.Arbitrary (IntN n) where
arbitrary = QC.arbitrarySizedBoundedIntegral
-- QC.shrinkIntegral assumes that 2 is representable by the number, which is
-- not the case for 1-bit bit vector.
shrink i
| i == 0 = []
| i == 1 = [0]
| otherwise = QC.shrinkIntegral i
instance SizedBV WordN where
sizedBVConcat :: forall l r. (KnownNat l, KnownNat r, 1 <= l, 1 <= r) => WordN l -> WordN r -> WordN (l + r)
sizedBVConcat (WordN a) (WordN b) = WordN ((a `shiftL` fromIntegral (natVal (Proxy :: Proxy r))) .|. b)
sizedBVZext _ (WordN v) = WordN v
sizedBVSext :: forall l r proxy. (KnownNat l, KnownNat r, 1 <= l, KnownNat r, l <= r) => proxy r -> WordN l -> WordN r
sizedBVSext pr (WordN v) = if s then WordN (maxi - noi + v) else WordN v
where
r = fromIntegral $ natVal pr
l = fromIntegral $ natVal (Proxy :: Proxy l)
s = testBit v (l - 1)
maxi = (1 :: Integer) `shiftL` r
noi = (1 :: Integer) `shiftL` l
sizedBVExt = sizedBVZext
sizedBVSelect ::
forall n ix w p q.
(KnownNat n, KnownNat ix, KnownNat w, 1 <= n, 1 <= w, ix + w <= n) =>
p ix ->
q w ->
WordN n ->
WordN w
sizedBVSelect pix pw (WordN v) = WordN ((v `shiftR` ix) .&. mask)
where
ix = fromIntegral $ natVal pix
w = fromIntegral $ natVal pw
mask = (1 `shiftL` w) - 1
instance SizedBV IntN where
sizedBVConcat :: forall l r. (KnownNat l, KnownNat r, 1 <= l, 1 <= r) => IntN l -> IntN r -> IntN (l + r)
sizedBVConcat (IntN a) (IntN b) = IntN $ unWordN $ sizedBVConcat (WordN a :: WordN l) (WordN b :: WordN r)
sizedBVZext _ (IntN v) = IntN v
sizedBVSext :: forall l r proxy. (KnownNat l, KnownNat r, 1 <= l, KnownNat r, l <= r) => proxy r -> IntN l -> IntN r
sizedBVSext pr (IntN v) = IntN $ unWordN $ sizedBVSext pr (WordN v :: WordN l)
sizedBVExt = sizedBVSext
sizedBVSelect ::
forall n ix w p q.
(KnownNat n, KnownNat ix, KnownNat w, 1 <= n, 1 <= w, ix + w <= n) =>
p ix ->
q w ->
IntN n ->
IntN w
sizedBVSelect pix pw (IntN v) = IntN $ unWordN $ sizedBVSelect pix pw (WordN v :: WordN n)
instance BV SomeWordN where
bvConcat (SomeWordN (a :: WordN l)) (SomeWordN (b :: WordN r)) =
case (leqAddPos (Proxy @l) (Proxy @r), knownAdd @l @r KnownProof KnownProof) of
(LeqProof, KnownProof) ->
SomeWordN $ sizedBVConcat a b
{-# INLINE bvConcat #-}
bvZext l (SomeWordN (a :: WordN n))
| l < n = error "bvZext: trying to zero extend a value to a smaller size"
| otherwise = res (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (l :: Nat). Proxy l -> SomeWordN
res p =
case (unsafeKnownProof @l (fromIntegral l), unsafeLeqProof @1 @l, unsafeLeqProof @n @l) of
(KnownProof, LeqProof, LeqProof) -> SomeWordN $ sizedBVZext p a
bvSext l (SomeWordN (a :: WordN n))
| l < n = error "bvSext: trying to zero extend a value to a smaller size"
| otherwise = res (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (l :: Nat). Proxy l -> SomeWordN
res p =
case (unsafeKnownProof @l (fromIntegral l), unsafeLeqProof @1 @l, unsafeLeqProof @n @l) of
(KnownProof, LeqProof, LeqProof) -> SomeWordN $ sizedBVSext p a
bvExt = bvZext
bvSelect ix w (SomeWordN (a :: WordN n))
| ix + w > n = error "bvSelect: trying to select a bitvector outside the bounds of the input"
| w == 0 = error "bvSelect: trying to select a bitvector of size 0"
| otherwise = res (Proxy @n) (Proxy @n)
where
n = fromIntegral $ natVal (Proxy @n)
res :: forall (w :: Nat) (ix :: Nat). Proxy w -> Proxy ix -> SomeWordN
res _ _ =
case ( unsafeKnownProof @ix (fromIntegral ix),
unsafeKnownProof @w (fromIntegral w),
unsafeLeqProof @1 @w,
unsafeLeqProof @(ix + w) @n
) of
(KnownProof, KnownProof, LeqProof, LeqProof) ->
SomeWordN $ sizedBVSelect (Proxy @ix) (Proxy @w) a
instance BV SomeIntN where
bvConcat l r = toSigned $ bvConcat (toUnsigned l) (toUnsigned r)
{-# INLINE bvConcat #-}
bvZext l = toSigned . bvZext l . toUnsigned
{-# INLINE bvZext #-}
bvSext l = toSigned . bvSext l . toUnsigned
{-# INLINE bvSext #-}
bvExt l = toSigned . bvExt l . toUnsigned
{-# INLINE bvExt #-}
bvSelect ix w = toSigned . bvSelect ix w . toUnsigned
{-# INLINE bvSelect #-}
instance (KnownNat n, 1 <= n) => SignConversion (WordN n) (IntN n) where
toSigned (WordN i) = IntN i
toUnsigned (IntN i) = WordN i
instance SignConversion SomeWordN SomeIntN where
toSigned (SomeWordN i) = SomeIntN $ toSigned i
toUnsigned (SomeIntN i) = SomeWordN $ toUnsigned i
deriving via
(DefaultFiniteBitsSymShift (IntN n))
instance
(KnownNat n, 1 <= n) => SymShift (IntN n)
deriving via
(DefaultFiniteBitsSymShift (WordN n))
instance
(KnownNat n, 1 <= n) => SymShift (WordN n)
deriving via
(DefaultFiniteBitsSymRotate (IntN n))
instance
(KnownNat n, 1 <= n) => SymRotate (IntN n)
deriving via
(DefaultFiniteBitsSymRotate (WordN n))
instance
(KnownNat n, 1 <= n) => SymRotate (WordN n)