{----------------------------------------------------------------------------- reactive-banana ------------------------------------------------------------------------------} {-# LANGUAGE Rank2Types, BangPatterns, RecordWildCards #-} module Reactive.Banana.Prim.Order ( -- * Synopsis -- | Data structure that represents a partial ordering by levels. -- * Order Order, flat, ensureAbove, recalculateParent, Level, level, ) where import Data.Functor import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import Data.Hashable import qualified Data.IntMap.Strict as IntMap type IntMap = IntMap.IntMap type Map = Map.HashMap type Set = Set.HashSet {----------------------------------------------------------------------------- Order by levels ------------------------------------------------------------------------------} -- | Each element is assigned a /level/. -- Elements in lower levels come before elements in higher levels. -- There is no order on elements within the same level. type Order a = Map a Level -- | FIXME: Level should be an 'Integer' to avoid overflow. -- -- FIXME: The algorithms in this module currently do not try to -- shrink the number or width of levels. type Level = Integer -- | The flat order where every element is at 'ground' level. flat :: Order a flat = Map.empty -- | Ground level. ground :: Level ground = 0 -- | Look up the level of an element. Default level is 'ground'. level :: (Eq a, Hashable a) => a -> Order a -> Level level x = {-# SCC level #-} maybe ground id . Map.lookup x -- | Make sure that the first argument is at least one level -- above the second argument. ensureAbove :: (Eq a, Hashable a) => a -> a -> Order a -> Order a ensureAbove child parent order = Map.insertWith max child (level parent order + 1) order -- | Reassign the parent for a child and recalculate the levels -- for the new parents and grandparents. recalculateParent :: (Eq a, Hashable a) => a -- Child. -> a -- Parent. -> Graph a -- Query parents of a node. -> Order a -> Order a recalculateParent child parent parents order | d <= 0 = order | otherwise = concatenate [ Map.insertWith (+) node (-d) | node <- dfs parent parents ] order where d = level parent order - level child order + 1 -- level parent - d = level child - 1 concatenate = foldr (.) id {----------------------------------------------------------------------------- Graph traversal ------------------------------------------------------------------------------} -- | Graph represented as map of successors. type Graph a = a -> [a] -- | Depth-first search. List all transitive successors of a node. dfs :: (Eq a, Hashable a) => a -> Graph a -> [a] dfs x succs = go [x] Set.empty where go [] _ = [] go (x:xs) seen | x `Set.member` seen = go xs seen | otherwise = x : go (ys ++ xs) (Set.insert x seen) where ys = succs x