grisette-0.13.0.1: src/Grisette/Internal/Core/Data/Class/AsKey.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Grisette.Internal.Core.Data.Class.AsKey
( KeyEq (..),
KeyOrd (..),
KeyHashable (..),
KeyEq1 (..),
KeyOrd1 (..),
KeyHashable1 (..),
AsKey (..),
AsKey1 (..),
shouldUseAsKeyError,
shouldUseAsKeyHasSymbolicVersionError,
shouldUseSymbolicVersionError,
)
where
import Control.DeepSeq (NFData, NFData1)
import Control.Monad.Identity (Identity)
import qualified Data.Binary as Binary
import Data.Bits (Bits, FiniteBits)
import qualified Data.Bytes.Serial as Serial
import Data.Functor.Classes
( Eq1 (liftEq),
Ord1 (liftCompare),
Show1,
compare1,
eq1,
)
import Data.Hashable (Hashable (hashWithSalt))
import Data.Hashable.Lifted (Hashable1 (liftHashWithSalt), hashWithSalt1)
import Data.Proxy (Proxy (Proxy))
import qualified Data.Serialize as Cereal
import Data.String (IsString)
import GHC.Stack (HasCallStack)
import GHC.TypeLits (KnownNat, type (<=))
import Grisette.Internal.Core.Data.Class.BitCast
( BitCast (bitCast),
BitCastCanonical (bitCastCanonicalValue),
)
import Grisette.Internal.Core.Data.Class.BitVector
( BV (bv, bvConcat, bvExt, bvSelect, bvSext, bvZext),
)
import Grisette.Internal.Core.Data.Class.Concrete (Concrete)
import Grisette.Internal.Core.Data.Class.Function
( Apply (FunType, apply),
Function ((#)),
)
import Grisette.Internal.Core.Data.Class.IEEEFP
( IEEEFPConstants
( fpMaxNormalized,
fpMaxSubnormal,
fpMinNormalized,
fpMinSubnormal,
fpNaN,
fpNegativeInfinite,
fpNegativeZero,
fpPositiveInfinite,
fpPositiveZero
),
IEEEFPConvertible (fromFPOr, toFP),
IEEEFPOp
( fpAbs,
fpMaximum,
fpMaximumNumber,
fpMinimum,
fpMinimumNumber,
fpNeg,
fpRem
),
IEEEFPRoundingMode (rna, rne, rtn, rtp, rtz),
IEEEFPRoundingOp
( fpAdd,
fpDiv,
fpFMA,
fpMul,
fpRoundToIntegral,
fpSqrt,
fpSub
),
IEEEFPToAlgReal (fpToAlgReal),
)
import Grisette.Internal.Core.Data.Class.SignConversion
( SignConversion (toSigned, toUnsigned),
)
import Grisette.Internal.SymPrim.AlgReal (AlgReal)
import Grisette.Internal.SymPrim.BV (IntN, WordN)
import Grisette.Internal.SymPrim.FP (FP, ValidFP)
import Grisette.Internal.SymPrim.Prim.Internal.Term (ConRep (ConType))
import Language.Haskell.TH.Syntax (Lift)
-- | Type class for identity equality for terms.
class KeyEq a where
keyEq :: a -> a -> Bool
infix 4 `keyEq`
instance (KnownNat n, 1 <= n) => KeyEq (WordN n) where
keyEq = (==)
instance (KnownNat n, 1 <= n) => KeyEq (IntN n) where
keyEq = (==)
instance KeyEq Integer where
keyEq = (==)
instance KeyEq Bool where
keyEq = (==)
instance KeyEq AlgReal where
keyEq = (==)
instance (ValidFP a b) => KeyEq (FP a b) where
keyEq = (==)
instance (Eq a) => KeyEq (Identity a) where
keyEq = (==)
-- | Type class for comparing terms based on their identity.
class (KeyEq a) => KeyOrd a where
keyCompare :: a -> a -> Ordering
infix 4 `keyCompare`
-- | Type class for hashing terms based on their identity.
class (KeyEq a) => KeyHashable a where
keyHashWithSalt :: Int -> a -> Int
instance (KnownNat n, 1 <= n) => KeyHashable (WordN n) where
keyHashWithSalt = hashWithSalt
instance (KnownNat n, 1 <= n) => KeyHashable (IntN n) where
keyHashWithSalt = hashWithSalt
instance KeyHashable Integer where
keyHashWithSalt = hashWithSalt
instance KeyHashable Bool where
keyHashWithSalt = hashWithSalt
instance KeyHashable AlgReal where
keyHashWithSalt = hashWithSalt
instance (ValidFP a b) => KeyHashable (FP a b) where
keyHashWithSalt = hashWithSalt
instance (Eq a, Hashable a) => KeyHashable (Identity a) where
keyHashWithSalt = hashWithSalt
class KeyEq1 f where
liftKeyEq :: (a -> b -> Bool) -> f a -> f b -> Bool
class (KeyEq1 f) => KeyOrd1 f where
liftKeyCompare :: (a -> b -> Ordering) -> f a -> f b -> Ordering
class (KeyEq1 f) => KeyHashable1 f where
liftKeyHashWithSalt :: (Int -> a -> Int) -> Int -> f a -> Int
infixl 0 `keyHashWithSalt`
-- | Use a term as a key with identity equality.
--
-- For example, @t'AsKey' t'Grisette.SymPrim.SymBool'@ uses the term identity
-- for t'Grisette.SymPrim.SymBool'.
newtype AsKey a = AsKey {getAsKey :: a}
deriving newtype
( Binary.Binary,
Cereal.Serialize,
NFData,
IsString,
Show,
Num,
Bits,
FiniteBits,
Enum,
Bounded,
Fractional,
Floating
)
deriving stock (Functor, Lift)
-- | Use a union as a key with identity equality.
--
-- For example, @t'AsKey1' t'Grisette.Core.Union'@ uses the term identity
-- for t'Grisette.Core.Union'.
newtype AsKey1 f a = AsKey1 {getAsKey1 :: f a}
deriving newtype
( Binary.Binary,
Cereal.Serialize,
IsString,
Show,
Show1,
Functor,
NFData,
NFData1,
Applicative,
Monad,
Num
)
deriving stock (Lift)
instance (Serial.Serial a) => Serial.Serial (AsKey a) where
serialize = Serial.serialize . getAsKey
deserialize = AsKey <$> Serial.deserialize
instance (Serial.Serial a, Serial.Serial1 f) => Serial.Serial (AsKey1 f a) where
serialize = Serial.serialize1 . getAsKey1
deserialize = AsKey1 <$> Serial.deserialize1
instance (KeyEq a) => Eq (AsKey a) where
(AsKey a) == (AsKey b) = keyEq a b
instance (KeyEq1 f, Eq a) => Eq (AsKey1 f a) where
(==) = eq1
instance (KeyEq1 f) => Eq1 (AsKey1 f) where
liftEq f (AsKey1 a) (AsKey1 b) = liftKeyEq f a b
instance (KeyOrd a) => Ord (AsKey a) where
compare (AsKey a) (AsKey b) = keyCompare a b
instance (KeyHashable a) => Hashable (AsKey a) where
hashWithSalt salt = keyHashWithSalt salt . getAsKey
instance (KeyOrd1 f, Ord a) => Ord (AsKey1 f a) where
compare = compare1
instance (KeyOrd1 f) => Ord1 (AsKey1 f) where
liftCompare f (AsKey1 a) (AsKey1 b) = liftKeyCompare f a b
instance (KeyHashable1 f, Hashable a) => Hashable (AsKey1 f a) where
hashWithSalt = hashWithSalt1
instance (KeyHashable1 f) => Hashable1 (AsKey1 f) where
liftHashWithSalt f salt (AsKey1 a) = liftKeyHashWithSalt f salt a
shouldUseAsKeyError :: (HasCallStack) => String -> String -> a
shouldUseAsKeyError typ op =
error $
"As "
<> typ
<> " is a symbolic type, "
<> op
<> " is likely not going to work as expected.\n"
<> "You should use AsKey if you do want term identity based "
<> op
<> " on "
<> typ
<> "."
shouldUseAsKeyHasSymbolicVersionError ::
(HasCallStack) => String -> String -> String -> a
shouldUseAsKeyHasSymbolicVersionError typ op symop =
error $
"As "
<> typ
<> " is a symbolic type, "
<> op
<> " is likely not going to work as expected.\n"
<> "You should use AsKey if you do want term identity based "
<> op
<> " on "
<> typ
<> ",\n or use "
<> symop
<> " instead if you want symbolic version of "
<> op
<> "."
shouldUseSymbolicVersionError ::
(HasCallStack) => String -> String -> String -> a
shouldUseSymbolicVersionError typ op symop =
error $
"As "
<> typ
<> " is a symbolic type, "
<> op
<> " is likely not going to work as expected.\n"
<> "You should use "
<> symop
<> " instead if you want symbolic version of "
<> op
<> "."
instance (Function a arg res) => Function (AsKey a) arg res where
(AsKey a) # b = a # b
instance
(Function (f a) arg (f res)) =>
Function (AsKey1 f a) arg (AsKey1 f res)
where
(AsKey1 f) # b = AsKey1 $ f # b
instance (Apply a) => Apply (AsKey a) where
type FunType (AsKey a) = FunType a
apply (AsKey a) = apply a
instance (ConRep a) => ConRep (AsKey a) where
type ConType (AsKey a) = ConType a
instance {-# INCOHERENT #-} (BitCast a b) => BitCast (AsKey a) (AsKey b) where
bitCast (AsKey a) = AsKey $ bitCast a
{-# INLINE bitCast #-}
instance {-# INCOHERENT #-} (BitCast a b) => BitCast a (AsKey b) where
bitCast a = AsKey $ bitCast a
{-# INLINE bitCast #-}
instance {-# INCOHERENT #-} (BitCast a b) => BitCast (AsKey a) b where
bitCast (AsKey a) = bitCast a
{-# INLINE bitCast #-}
instance
{-# INCOHERENT #-}
(BitCastCanonical a b) =>
BitCastCanonical (AsKey a) (AsKey b)
where
bitCastCanonicalValue _ = AsKey $ bitCastCanonicalValue (Proxy @a)
instance
{-# INCOHERENT #-}
(BitCastCanonical a b) =>
BitCastCanonical (AsKey a) b
where
bitCastCanonicalValue _ = bitCastCanonicalValue (Proxy @a)
instance
{-# INCOHERENT #-}
(BitCastCanonical a b) =>
BitCastCanonical a (AsKey b)
where
bitCastCanonicalValue p = AsKey $ bitCastCanonicalValue p
instance (SignConversion a b) => SignConversion (AsKey a) (AsKey b) where
toSigned (AsKey a) = AsKey $ toSigned a
toUnsigned (AsKey a) = AsKey $ toUnsigned a
instance (IEEEFPConstants a) => IEEEFPConstants (AsKey a) where
fpPositiveInfinite = AsKey fpPositiveInfinite
fpNegativeInfinite = AsKey fpNegativeInfinite
fpNaN = AsKey fpNaN
fpNegativeZero = AsKey fpNegativeZero
fpPositiveZero = AsKey fpPositiveZero
fpMinNormalized = AsKey fpMinNormalized
fpMinSubnormal = AsKey fpMinSubnormal
fpMaxNormalized = AsKey fpMaxNormalized
fpMaxSubnormal = AsKey fpMaxSubnormal
instance (IEEEFPOp a) => IEEEFPOp (AsKey a) where
fpAbs (AsKey a) = AsKey $ fpAbs a
fpNeg (AsKey a) = AsKey $ fpNeg a
fpRem (AsKey a) (AsKey b) = AsKey $ fpRem a b
fpMinimum (AsKey a) (AsKey b) = AsKey $ fpMinimum a b
fpMaximum (AsKey a) (AsKey b) = AsKey $ fpMaximum a b
fpMinimumNumber (AsKey a) (AsKey b) = AsKey $ fpMinimumNumber a b
fpMaximumNumber (AsKey a) (AsKey b) = AsKey $ fpMaximumNumber a b
instance (IEEEFPRoundingMode a) => IEEEFPRoundingMode (AsKey a) where
rne = AsKey rne
rna = AsKey rna
rtp = AsKey rtp
rtn = AsKey rtn
rtz = AsKey rtz
instance
(IEEEFPRoundingOp a mode) =>
IEEEFPRoundingOp (AsKey a) (AsKey mode)
where
fpAdd (AsKey mode) (AsKey a) (AsKey b) = AsKey $ fpAdd mode a b
fpSub (AsKey mode) (AsKey a) (AsKey b) = AsKey $ fpSub mode a b
fpMul (AsKey mode) (AsKey a) (AsKey b) = AsKey $ fpMul mode a b
fpDiv (AsKey mode) (AsKey a) (AsKey b) = AsKey $ fpDiv mode a b
fpFMA (AsKey mode) (AsKey a) (AsKey b) (AsKey c) = AsKey $ fpFMA mode a b c
fpSqrt (AsKey mode) (AsKey a) = AsKey $ fpSqrt mode a
fpRoundToIntegral (AsKey mode) (AsKey a) = AsKey $ fpRoundToIntegral mode a
instance
{-# INCOHERENT #-}
(IEEEFPConvertible a fp mode) =>
IEEEFPConvertible (AsKey a) (AsKey fp) (AsKey mode)
where
fromFPOr (AsKey d) (AsKey mode) (AsKey fp) = AsKey $ fromFPOr d mode fp
toFP (AsKey mode) (AsKey a) = AsKey $ toFP mode a
instance
{-# INCOHERENT #-}
(IEEEFPConvertible a fp mode) =>
IEEEFPConvertible a (AsKey fp) (AsKey mode)
where
fromFPOr a (AsKey mode) (AsKey fp) = fromFPOr a mode fp
toFP (AsKey mode) a = AsKey $ toFP mode a
instance
{-# INCOHERENT #-}
(IEEEFPConvertible a fp mode) =>
IEEEFPConvertible (AsKey a) fp mode
where
fromFPOr (AsKey a) mode fp = AsKey $ fromFPOr a mode fp
toFP mode (AsKey a) = toFP mode a
instance
{-# INCOHERENT #-}
(IEEEFPToAlgReal a fp mode) =>
IEEEFPToAlgReal (AsKey a) (AsKey fp) (AsKey mode)
where
fpToAlgReal (AsKey d) (AsKey fp) = AsKey $ fpToAlgReal d fp
instance
{-# INCOHERENT #-}
(IEEEFPToAlgReal a fp mode) =>
IEEEFPToAlgReal a (AsKey fp) (AsKey mode)
where
fpToAlgReal a (AsKey fp) = fpToAlgReal a fp
instance
{-# INCOHERENT #-}
(IEEEFPToAlgReal a fp mode) =>
IEEEFPToAlgReal (AsKey a) fp mode
where
fpToAlgReal (AsKey d) fp = AsKey $ fpToAlgReal d fp
instance Concrete (AsKey a)
instance (BV a) => BV (AsKey a) where
bvConcat (AsKey a) (AsKey b) = AsKey $ bvConcat a b
bvZext n (AsKey a) = AsKey $ bvZext n a
bvSext n (AsKey a) = AsKey $ bvSext n a
bvExt n (AsKey a) = AsKey $ bvExt n a
bvSelect ix w (AsKey a) = AsKey $ bvSelect ix w a
bv n a = AsKey $ bv n a