{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ViewPatterns #-}
module What4.Expr.BoolMap
( BoolMap
, var
, addVar
, fromVars
, combine
, Polarity(..)
, negatePolarity
, contains
, isInconsistent
, isNull
, BoolMapView(..)
, viewBoolMap
, traverseVars
, reversePolarities
, removeVar
, Wrap(..)
) where
import Control.Lens (_1, over)
import Data.Hashable
import Data.List (foldl')
import Data.List.NonEmpty (NonEmpty(..))
import Data.Kind (Type)
import Data.Parameterized.Classes
import What4.BaseTypes
import qualified What4.Utils.AnnotatedMap as AM
import What4.Utils.IncrHash
data Polarity = Positive | Negative
deriving (Eq,Ord,Show)
instance Hashable Polarity where
hashWithSalt s Positive = hashWithSalt s (0::Int)
hashWithSalt s Negative = hashWithSalt s (1::Int)
negatePolarity :: Polarity -> Polarity
negatePolarity Positive = Negative
negatePolarity Negative = Positive
newtype Wrap (f :: k -> Type) (x :: k) = Wrap { unWrap:: f x }
instance TestEquality f => Eq (Wrap f x) where
Wrap a == Wrap b = isJust $ testEquality a b
instance OrdF f => Ord (Wrap f x) where
compare (Wrap a) (Wrap b) = toOrdering $ compareF a b
instance HashableF f => Hashable (Wrap f x) where
hashWithSalt s (Wrap a) = hashWithSaltF s a
data BoolMap (f :: BaseType -> Type)
= InconsistentMap
| BoolMap !(AM.AnnotatedMap (Wrap f BaseBoolType) IncrHash Polarity)
instance OrdF f => Eq (BoolMap f) where
InconsistentMap == InconsistentMap = True
BoolMap m1 == BoolMap m2 = AM.eqBy (==) m1 m2
_ == _ = False
traverseVars :: (Applicative m, HashableF g, OrdF g) =>
(f BaseBoolType -> m (g (BaseBoolType))) ->
BoolMap f -> m (BoolMap g)
traverseVars _ InconsistentMap = pure InconsistentMap
traverseVars f (BoolMap m) =
fromVars <$> traverse (_1 (f . unWrap)) (AM.toList m)
elementHash :: HashableF f => f BaseBoolType -> Polarity -> IncrHash
elementHash x p = mkIncrHash (hashWithSaltF (hash p) x)
instance (OrdF f, HashableF f) => Hashable (BoolMap f) where
hashWithSalt s InconsistentMap = hashWithSalt s (0::Int)
hashWithSalt s (BoolMap m) =
case AM.annotation m of
Nothing -> hashWithSalt s (1::Int)
Just h -> hashWithSalt (hashWithSalt s (1::Int)) h
data BoolMapView f
= BoolMapUnit
| BoolMapDualUnit
| BoolMapTerms (NonEmpty (f BaseBoolType, Polarity))
viewBoolMap :: BoolMap f -> BoolMapView f
viewBoolMap InconsistentMap = BoolMapDualUnit
viewBoolMap (BoolMap m) =
case AM.toList m of
[] -> BoolMapUnit
(Wrap x,p):xs -> BoolMapTerms ((x,p):|(map (over _1 unWrap) xs))
isInconsistent :: BoolMap f -> Bool
isInconsistent InconsistentMap = True
isInconsistent _ = False
isNull :: BoolMap f -> Bool
isNull InconsistentMap = False
isNull (BoolMap m) = AM.null m
var :: (HashableF f, OrdF f) => f BaseBoolType -> Polarity -> BoolMap f
var x p = BoolMap (AM.singleton (Wrap x) (elementHash x p) p)
addVar :: (HashableF f, OrdF f) => f BaseBoolType -> Polarity -> BoolMap f -> BoolMap f
addVar _ _ InconsistentMap = InconsistentMap
addVar x p1 (BoolMap bm) = maybe InconsistentMap BoolMap $ AM.alterF f (Wrap x) bm
where
f Nothing = return (Just (elementHash x p1, p1))
f el@(Just (_,p2)) | p1 == p2 = return el
| otherwise = Nothing
fromVars :: (HashableF f, OrdF f) => [(f BaseBoolType, Polarity)] -> BoolMap f
fromVars = foldl' (\m (x,p) -> addVar x p m) (BoolMap AM.empty)
combine :: OrdF f => BoolMap f -> BoolMap f -> BoolMap f
combine InconsistentMap _ = InconsistentMap
combine _ InconsistentMap = InconsistentMap
combine (BoolMap m1) (BoolMap m2) =
maybe InconsistentMap BoolMap $ AM.mergeA f m1 m2
where f _k (v,p1) (_,p2)
| p1 == p2 = Just (v,p1)
| otherwise = Nothing
contains :: OrdF f => BoolMap f -> f BaseBoolType -> Maybe Polarity
contains InconsistentMap _ = Nothing
contains (BoolMap m) x = snd <$> AM.lookup (Wrap x) m
reversePolarities :: OrdF f => BoolMap f -> BoolMap f
reversePolarities InconsistentMap = InconsistentMap
reversePolarities (BoolMap m) = BoolMap $! fmap negatePolarity m
removeVar :: OrdF f => BoolMap f -> f BaseBoolType -> BoolMap f
removeVar InconsistentMap _ = InconsistentMap
removeVar (BoolMap m) x = BoolMap (AM.delete (Wrap x) m)