module UHC.Util.CHR.Solve.TreeTrie.Mono
( CHRStore
, emptyCHRStore
, StoredCHR(..)
, chrStoreFromElems
, chrStoreUnion
, chrStoreUnions
, chrStoreSingletonElem
, chrStoreToList
, chrStoreElems
, ppCHRStore
, ppCHRStore'
, SolveStep'(..)
, SolveStep
, SolveTrace
, ppSolveTrace
, SolveState
, emptySolveState
, solveStateResetDone
, chrSolveStateDoneConstraints
, chrSolveStateTrace
, IsCHRSolvable(..)
, chrSolve'
, chrSolve''
, chrSolveM
)
where
import UHC.Util.CHR.Base
import UHC.Util.CHR.Key
import UHC.Util.CHR.Solve.TreeTrie.Internal
import UHC.Util.Substitutable
import UHC.Util.VarLookup
import UHC.Util.VarMp
import UHC.Util.AssocL
import UHC.Util.TreeTrie as TreeTrie
import qualified Data.Set as Set
import qualified Data.Map as Map
import Data.List as List
import Data.Typeable
import Data.Data
import Data.Maybe
import UHC.Util.Pretty as Pretty
import UHC.Util.Serialize
import Control.Monad
import Control.Monad.State.Strict
import UHC.Util.Utils
data StoredCHR c g
= StoredCHR
{ storedChr :: !(Rule c g () ())
, storedKeyedInx :: !Int
, storedKeys :: ![Maybe (CHRKey c)]
, storedIdent :: !(UsedByKey c)
}
deriving (Typeable)
type instance TTKey (StoredCHR c g) = TTKey c
instance (TTKeyable (Rule c g () ())) => TTKeyable (StoredCHR c g) where
toTTKey' o schr = toTTKey' o $ storedChr schr
storedSimpSz :: StoredCHR c g -> Int
storedSimpSz = ruleSimpSz . storedChr
newtype CHRStore cnstr guard
= CHRStore
{ chrstoreTrie :: CHRTrie [StoredCHR cnstr guard]
}
deriving (Typeable)
mkCHRStore trie = CHRStore trie
emptyCHRStore :: CHRStore cnstr guard
emptyCHRStore = mkCHRStore emptyCHRTrie
cmbStoredCHRs :: [StoredCHR c g] -> [StoredCHR c g] -> [StoredCHR c g]
cmbStoredCHRs s1 s2
= map (\s@(StoredCHR {storedIdent=(k,nr)}) -> s {storedIdent = (k,nr+l)}) s1 ++ s2
where l = length s2
instance Show (StoredCHR c g) where
show _ = "StoredCHR"
ppStoredCHR :: (PP (TTKey c), PP c, PP g) => StoredCHR c g -> PP_Doc
ppStoredCHR c@(StoredCHR {storedIdent=(idKey,idSeqNr)})
= storedChr c
>-< indent 2
(ppParensCommas
[ pp $ storedKeyedInx c
, pp $ storedSimpSz c
, "keys" >#< (ppBracketsCommas $ map (maybe (pp "?") ppTreeTrieKey) $ storedKeys c)
, "ident" >#< ppParensCommas [ppTreeTrieKey idKey,pp idSeqNr]
])
instance (PP (TTKey c), PP c, PP g) => PP (StoredCHR c g) where
pp = ppStoredCHR
chrStoreFromElems :: (TTKeyable c, Ord (TTKey c), TTKey c ~ TrTrKey c) => [Rule c g () ()] -> CHRStore c g
chrStoreFromElems chrs
= mkCHRStore
$ chrTrieFromListByKeyWith cmbStoredCHRs
[ (k,[StoredCHR chr i ks' (concat ks,0)])
| chr <- chrs
, let cs = ruleHead chr
simpSz = ruleSimpSz chr
ks = map chrToKey cs
, (c,k,i) <- zip3 cs ks [0..]
, let (ks1,(_:ks2)) = splitAt i ks
ks' = map Just ks1 ++ [Nothing] ++ map Just ks2
]
chrStoreSingletonElem :: (TTKeyable c, Ord (TTKey c), TTKey c ~ TrTrKey c) => Rule c g () () -> CHRStore c g
chrStoreSingletonElem x = chrStoreFromElems [x]
chrStoreUnion :: (Ord (TTKey c)) => CHRStore c g -> CHRStore c g -> CHRStore c g
chrStoreUnion cs1 cs2 = mkCHRStore $ chrTrieUnionWith cmbStoredCHRs (chrstoreTrie cs1) (chrstoreTrie cs2)
chrStoreUnions :: (Ord (TTKey c)) => [CHRStore c g] -> CHRStore c g
chrStoreUnions [] = emptyCHRStore
chrStoreUnions [s] = s
chrStoreUnions ss = foldr1 chrStoreUnion ss
chrStoreToList :: (Ord (TTKey c)) => CHRStore c g -> [(CHRKey c,[Rule c g () ()])]
chrStoreToList cs
= [ (k,chrs)
| (k,e) <- chrTrieToListByKey $ chrstoreTrie cs
, let chrs = [chr | (StoredCHR {storedChr = chr, storedKeyedInx = 0}) <- e]
, not $ Prelude.null chrs
]
chrStoreElems :: (Ord (TTKey c)) => CHRStore c g -> [Rule c g () ()]
chrStoreElems = concatMap snd . chrStoreToList
ppCHRStore :: (PP c, PP g, Ord (TTKey c), PP (TTKey c)) => CHRStore c g -> PP_Doc
ppCHRStore = ppCurlysCommasBlock . map (\(k,v) -> ppTreeTrieKey k >-< indent 2 (":" >#< ppBracketsCommasBlock v)) . chrStoreToList
ppCHRStore' :: (PP c, PP g, Ord (TTKey c), PP (TTKey c)) => CHRStore c g -> PP_Doc
ppCHRStore' = ppCurlysCommasBlock . map (\(k,v) -> ppTreeTrieKey k >-< indent 2 (":" >#< ppBracketsCommasBlock v)) . chrTrieToListByKey . chrstoreTrie
type SolveStep c g s = SolveStep' c (Rule c g () ()) s
type SolveTrace c g s = SolveTrace' c (Rule c g () ()) s
type SolveMatchCache c g b p s = SolveMatchCache' c (StoredCHR c g) s
type SolveState c g s = SolveState' c (Rule c g () ()) (StoredCHR c g) s
class ( IsCHRConstraint env c s
, IsCHRGuard env g s
, LookupApply s s
, CHREmptySubstitution s
, TrTrKey c ~ TTKey c
) => IsCHRSolvable env c g s
| c g -> s
chrSolve'
:: forall env c g s .
( IsCHRSolvable env c g s
)
=> [CHRTrOpt]
-> env
-> CHRStore c g
-> [c]
-> ([c],[c],SolveTrace c g s)
chrSolve' tropts env chrStore cnstrs
= (wlToList (stWorkList finalState), stDoneCnstrs finalState, stTrace finalState)
where finalState = chrSolve'' tropts env chrStore cnstrs emptySolveState
chrSolve''
:: forall env c g s .
( IsCHRSolvable env c g s
)
=> [CHRTrOpt]
-> env
-> CHRStore c g
-> [c]
-> SolveState c g s
-> SolveState c g s
chrSolve'' tropts env chrStore cnstrs prevState
= flip execState prevState $ chrSolveM tropts env chrStore cnstrs
chrSolveM
:: forall env c g s .
( IsCHRSolvable env c g s
)
=> [CHRTrOpt]
-> env
-> CHRStore c g
-> [c]
-> State (SolveState c g s) ()
chrSolveM tropts env chrStore cnstrs = do
modify initState
iter
modify $ \st -> st {stMatchCache = Map.empty}
where iter = do
st <- get
case st of
(SolveState {stWorkList = wl@(WorkList {wlQueue = (workHd@(workHdKey,_) : workTl)})}) ->
case matches of
(_:_) -> do
put
stmatch
expandMatch matches
where
expandMatch ( ( ( schr@(StoredCHR {storedIdent = chrId, storedChr = chr@(Rule {ruleSimpSz = simpSz})})
, (keys,works)
)
, subst
) : tlMatch
) = do
let b = ruleBody chr
st@(SolveState {stWorkList = wl, stHistoryCount = histCount, stUsedRules = usedRules }) <- get
let (tlMatchY,tlMatchN) = partition (\(r@(_,(ks,_)),_) -> not (any (`elem` keysSimp) ks || slvIsUsedByPropPart (wlUsedIn wl') r)) tlMatch
(keysSimp,keysProp) = splitAt simpSz keys
usedIn = Map.singleton (Set.fromList keysProp) (Set.singleton chrId)
(bTodo,bDone) = splitDone $ map (varUpd subst) b
bTodo' = wlCnstrToIns wl bTodo
wl' = wlDeleteByKeyAndInsert' histCount keysSimp bTodo'
$ wl { wlUsedIn = usedIn `wlUsedInUnion` wlUsedIn wl
, wlScanned = []
, wlQueue = wlQueue wl ++ wlScanned wl
}
st' = st { stWorkList = wl'
, stDoneCnstrSet = Set.unions [Set.fromList bDone, Set.fromList $ map workCnstr $ take simpSz works, stDoneCnstrSet st]
, stMatchCache = if List.null bTodo' then stMatchCache st else Map.empty
, stHistoryCount = histCount + 1
, stUsedRules = (chr : usedRules)
}
put
st'
expandMatch tlMatchY
expandMatch _
= iter
_ -> do
put
st'
iter
where wl' = wl { wlScanned = workHd : wlScanned wl, wlQueue = workTl }
st' = stmatch { stWorkList = wl' }
where (matches,lastQuery ) = workMatches st
stmatch =
(st { stCountCnstr = scntInc workHdKey "workMatched" $ stCountCnstr st
, stMatchCache = Map.insert workHdKey [] (stMatchCache st)
, stLastQuery = lastQuery
})
_ -> do
return ()
mkStats stats new = stats `Map.union` Map.fromList (assocLMapKey showPP new)
addStats _ _ st = st
mkDbgPP o | o `elem` tropts = id
| otherwise = const Pretty.empty
workMatches st@(SolveState {stWorkList = WorkList {wlQueue = (workHd@(workHdKey,Work {workTime = workHdTm}) : _), wlTrie = wlTrie, wlUsedIn = wlUsedIn}, stHistoryCount = histCount, stLastQuery = lastQuery})
| isJust mbInCache = ( fromJust mbInCache
, lastQuery
)
| otherwise = ( r5
, foldr lqUnion lastQuery [ lqSingleton ck wks histCount | (_,(_,(ck,wks))) <- r23 ]
)
where
mbInCache = Map.lookup workHdKey (stMatchCache st)
r2 :: [StoredCHR c g]
r2 = concat
$ TreeTrie.lookupResultToList
$ chrTrieLookup chrLookupHowWildAtTrie workHdKey
$ chrstoreTrie chrStore
r23 :: [( StoredCHR c g
, ( [( [(CHRKey c, Work c)]
, [(CHRKey c, Work c)]
)]
, (CHRKey c, Set.Set (CHRKey c))
) )]
r23 = map (\c -> (c, slvCandidate workHdKey lastQuery wlTrie c)) r2
r3, r4
:: [( StoredCHR c g
, ( [CHRKey c]
, [Work c]
) )]
r3 = concatMap (\(c,cands) -> zip (repeat c) (map unzip $ slvCombine cands)) $ r23
r4 = filter (not . slvIsUsedByPropPart wlUsedIn) r3
r5 :: [( ( StoredCHR c g
, ( [CHRKey c]
, [Work c]
) )
, s
)]
r5 = mapMaybe (\r@(chr,kw@(_,works)) -> fmap (\s -> (r,s)) $ slvMatch env chr (map workCnstr works)) r4
pp2 = "lookups" >#< ("for" >#< ppTreeTrieKey workHdKey >-< ppBracketsCommasBlock r2)
pp2b = "cand1" >#< ("lastQ" >#< ppLastQuery lastQuery >-< vlist [ pp (storedKeyedInx chr) | (chr,mtch) <- r23 ])
pp3 = "candidates" >#< (ppBracketsCommasBlock $ map (\(chr,(ks,ws)) -> "chr" >#< chr >-< "keys" >#< ppBracketsCommas (map ppTreeTrieKey ks) >-< "works" >#< ppBracketsCommasBlock ws) $ r3)
initState st = st { stWorkList = wlInsert (stHistoryCount st) wlnew $ stWorkList st, stDoneCnstrSet = Set.unions [Set.fromList done, stDoneCnstrSet st] }
where (wlnew,done) = splitDone cnstrs
splitDone = partition cnstrRequiresSolve
slvCandidate
:: (Ord (TTKey c), PP (TTKey c))
=> CHRKey c
-> LastQuery c
-> WorkTrie c
-> StoredCHR c g
-> ( [( [(CHRKey c, Work c)]
, [(CHRKey c, Work c)]
)]
, (CHRKey c, Set.Set (CHRKey c))
)
slvCandidate workHdKey lastQuery wlTrie (StoredCHR {storedIdent = (ck,_), storedKeys = ks, storedChr = chr})
= ( map (maybe (lkup chrLookupHowExact workHdKey) (lkup chrLookupHowWildAtKey)) ks
, ( ck
, Set.fromList $ map (maybe workHdKey id) ks
) )
where lkup how k = partition (\(_,w) -> workTime w < lastQueryTm)
$ map (\w -> (workKey w,w))
$ TreeTrie.lookupResultToList
$ chrTrieLookup how k wlTrie
where lastQueryTm = lqLookupW k $ lqLookupC ck lastQuery
slvIsUsedByPropPart
:: (Ord k, Ord (TTKey c))
=> Map.Map (Set.Set k) (Set.Set (UsedByKey c))
-> (StoredCHR c g, ([k], t))
-> Bool
slvIsUsedByPropPart wlUsedIn (chr,(keys,_))
= fnd $ drop (storedSimpSz chr) keys
where fnd k = maybe False (storedIdent chr `Set.member`) $ Map.lookup (Set.fromList k) wlUsedIn
slvMatch
:: ( CHREmptySubstitution s
, CHRMatchable env c s
, CHRCheckable env g s
, LookupApply s s
)
=> env -> StoredCHR c g -> [c] -> Maybe s
slvMatch env chr cnstrs
= foldl cmb (Just chrEmptySubst) $ matches chr cnstrs ++ checks chr
where matches (StoredCHR {storedChr = Rule {ruleHead = hc}}) cnstrs
= zipWith mt hc cnstrs
where mt cFr cTo subst = chrMatchTo env subst cFr cTo
checks (StoredCHR {storedChr = Rule {ruleGuard = gd}})
= map chk gd
where chk g subst = chrCheck env subst g
cmb (Just s) next = fmap (|+> s) $ next s
cmb _ _ = Nothing
instance (Ord (TTKey c), Serialize (TTKey c), Serialize c, Serialize g) => Serialize (CHRStore c g) where
sput (CHRStore a) = sput a
sget = liftM CHRStore sget
instance (Serialize c, Serialize g, Serialize (TTKey c)) => Serialize (StoredCHR c g) where
sput (StoredCHR a b c d) = sput a >> sput b >> sput c >> sput d
sget = liftM4 StoredCHR sget sget sget sget