packages feed

huff-0.1.0.0: src/Huff/FF/Fixpoint.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-}

module Huff.FF.Fixpoint (
    buildFixpoint
  ) where

import           Huff.ConnGraph

import           Data.Foldable (foldlM)
import           Data.IORef ( readIORef, writeIORef )
import           Data.Monoid ( mconcat )
import qualified Data.Set as Set


-- Predicates ------------------------------------------------------------------

-- | Loop until the goal state is activated in the connection graph.  As the
-- connection graph should only be built from domains that can activate all
-- facts, and delete effects are ignored, this operation will terminate.  The
-- set of effects returned is the set of effects that are immediately applicable
-- to the initial state.
buildFixpoint :: ConnGraph a -> State a -> Goals a -> IO Int
buildFixpoint gr s0 g =
  do resetConnGraph gr
     loop 0 s0
  where
  loop level facts =
    do effs <- mconcat `fmap` traverse (activateFact level) (Set.toList facts)
       done <- allGoalsReached g
       if done
          then return level
          else do facts' <- mconcat `fmap` mapM (activateEffect level)
                                                (Set.toList effs)
                  if Set.null facts'
                     then return level
                     else loop (level + 1) facts'


-- | All goals have been reached if they are all activated in the connection
-- graph.
allGoalsReached :: Goals a -> IO Bool
allGoalsReached g = go goals
  where
  goals = Set.toList g

  -- require that all goals have a level that isn't infinity.
  go (fact:rs) =
    do mb <- getLevel fact
       case mb of
         Just{}  -> go rs
         Nothing -> return False

  go [] = return True


-- | Set a fact to true at this level of the relaxed graph.  Return any effects
-- that were enabled by adding this fact.
activateFact :: Level -> Fact a -> IO (Effects a)
activateFact level fact =
  do activate fact level
     foldlM addedPrecond Set.empty (requiresFact fact)

  where

  addedPrecond effs eff =
    do -- skip effects that are already activated
       mb <- getLevel eff
       case mb of

         Just{}  ->
             return effs

         Nothing ->
           do activated <- activatePrecondition eff
              if activated
                 then return (Set.insert eff effs)
                 else return effs


-- | Add an effect at level i, and return all of its add effects.
activateEffect :: Level -> Effect a -> IO (Facts a)
activateEffect level e =
  do activate e level
     return (effectAdds e)