module NLP.Adict.Graph
( minPath
, Edges
, IsEnd
) where
import qualified Data.PSQueue as P
import qualified Data.Map as M
type Edges n w = n -> [(w, n)]
type Edge n w = (n, w, n)
type IsEnd n = n -> Bool
data Adj n w = Adj
{ from :: n
, to :: [(w, n)] }
deriving (Show, Eq, Ord)
proxy :: Adj n w -> (w, n)
proxy = head . to
folls :: Adj n w -> [(w, n)]
folls = tail . to
type PQ n w = P.PSQ (Adj n w) (w, n)
minView :: (Ord n, Ord w) => PQ n w -> Maybe (Edge n w, PQ n w)
minView queue = do
(adj P.:-> (w, q), queue') <- P.minView queue
let p = from adj
e = (p, w, q)
return (e, push queue' p (folls adj))
push :: (Ord n, Ord w) => PQ n w -> n -> [(w, n)] -> PQ n w
push queue _ [] = queue
push queue p xs = insert (Adj p xs) queue
insert :: (Ord n, Ord w) => Adj n w -> PQ n w -> PQ n w
insert x = P.insert x (proxy x)
minPath :: (Ord n, Ord w, Num w, Fractional w)
=> w -> Edges n w -> IsEnd n -> n -> Maybe ([n], w)
minPath threshold edgesFrom isEnd beg =
shortest M.empty $ insert (Adj beg [(0, beg)]) P.empty
where
shortest visited queue = do
(edge, queue') <- minView queue
shortest' visited queue' edge
shortest' visited queue (p, w, q)
| isEnd q = Just (reverse (trace visited' q), w)
| q `M.member` visited = shortest visited queue
| otherwise = shortest visited' queue'
where
visited' = M.insert q p visited
queue' = push queue q $
takeWhile ((<= threshold) . fst)
[(w + u, s) | (u, s) <- edgesFrom q]
trace visited n
| m == n = [n]
| otherwise = n : trace visited m
where
m = visited M.! n