packages feed

queuelike-1.0.2: Data/Queue/TrieQueue.hs

{-# LANGUAGE PatternGuards, TypeFamilies, GeneralizedNewtypeDeriving #-}

-- | An experimental trie-based priority queue for lists.
module Data.Queue.TrieQueue (TrieQueue) where

import GHC.Exts
import Data.Queue.Class
import Control.Arrow(first)
import Control.Monad(liftM2)
import Data.Function
import Data.Maybe
import Data.List (sortBy, groupBy)
import Data.Word
import Data.Monoid
import Data.Ord
import Data.Sequence (Seq, viewl, ViewL(..), (><), (|>), (<|))
import qualified Data.Sequence as Seq
import Data.Map (Map, findMin)
import qualified Data.Map as Map
import qualified Data.Foldable as Fold (toList)
import Data.Queue.QueueHelpers

type Label e = Seq e
data Trie e = Empty | Leaf (Label e) {-# UNPACK #-} !Int | Edge (Label e) {-# UNPACK #-} !Int (Map e (Trie e)) deriving (Eq)
newtype TrieQueue e = TQ (MonoidQ (Trie e)) deriving (Eq, Show, Monoid)

instance Ord e => Monoid (Trie e) where
	mempty = Empty
	mappend = merger

instance Ord e => IQueue (TrieQueue e) where
	type QueueKey (TrieQueue e) = [e]
	empty = mempty
	merge = mappend
	mergeAll = mconcat

	singleton xs = TQ (HQ 1 (Leaf (Seq.fromList xs) 1))
	fromList = TQ . liftM2 HQ length trieFromList
	
	extract (TQ (HQ n t)) = fmap (\ (x, t) -> (Fold.toList x, TQ (HQ (n-1) t))) (extractMin' t)
	size (TQ (HQ n _)) = n

instance Show e => Show (Trie e) where
	show Empty = "Empty"
	show (Leaf xs xn) = "(" ++ show xn ++ "x" ++ show xs ++ ")"
	show (Edge xs xn m) = "==" ++ show xn ++ "x" ++ show (Fold.toList xs) ++ "==>" ++ show m

catTrie :: Ord e => Label e -> Trie e -> Trie e
catTrie _ Empty = Empty
catTrie xs (Leaf ys n) = Leaf (xs `mappend` ys) n
catTrie xs (Edge ys n m) = compactTrie (Edge (xs `mappend` ys) n m)

compactTrie :: Ord e => Trie e -> Trie e
compactTrie (Edge xs n m)
	| Map.null m		= compactTrie (Leaf xs n)
	| Map.size m == 1, (y, yT) <- findMin m
		= catTrie (xs |> y) yT
compactTrie (Leaf _ 0) = Empty
compactTrie t = t

extractMin' :: Ord e => Trie e -> Maybe (Label e, Trie e)
extractMin' (Leaf xs (n+1)) = Just (xs, compactTrie (Leaf xs n))
extractMin' (Edge xs (n+1) m) = Just (xs, compactTrie (Edge xs n m))
extractMin' (Edge xs 0 m) 
	| Just ((y, yT), m') <- Map.minViewWithKey m,
		Just (ys, yT') <- extractMin' yT
	= Just (xs >< (y <| ys), case yT' of
		Empty	-> compactTrie (Edge xs 0 m')
		_		-> Edge xs 0 (Map.insert y yT' m'))
extractMin' _ = Nothing

--extractMin :: Ord e => Trie e -> Maybe ([e], Trie e)
--extractMin = fmap (first toList) . extractMin'

type TailMaker e = Label e -> Trie e

{-# INLINE merge' #-}
merge' :: Ord e => Label e -> Label e -> TailMaker e -> TailMaker e -> (e -> TailMaker e) -> (e -> TailMaker e) -> Trie e -> Trie e
merge' xs0 ys0 xTail yTail xCons yCons xy =
	let merge'' n xs ys = case (viewl xs, viewl ys) of
		(x :< xs1, y :< ys1) -> let xT = xTail xs1; yT = yTail ys1; in case x `compare` y of
			LT	-> Edge (Seq.take n xs0) 0 $ Map.fromDistinctAscList [(x, xT), (y, yT)]
			EQ	-> merge'' (n+1) xs1 ys1
			GT	-> Edge (Seq.take n xs0) 0 $ Map.fromDistinctAscList [(y, yT), (x, xT)]
		(EmptyL, y :< ys1)
			-> xCons y ys1
		(x :< xs1, EmptyL)
			-> yCons x xs1
		(EmptyL, EmptyL)
			-> xy
		in merge'' 0 xs0 ys0
	
merger :: Ord e => Trie e -> Trie e -> Trie e	
Leaf xs0 xn `merger` Leaf ys0 yn =
	inline merge' xs0 ys0 (flip Leaf xn) (flip Leaf yn) (edger xs0 xn yn) (edger ys0 yn xn) (Leaf xs0 (xn + yn))
	where	edger xs xn yn y ys = Edge xs xn $ Map.singleton y (Leaf ys yn)
Leaf xs0 xn `merger` Edge ys0 yn yM =
	inline merge' xs0 ys0 (flip Leaf xn) (\ ys -> Edge ys yn yM) (\ y ys -> Edge xs0 xn $ Map.singleton y (Edge ys yn yM))
		(\ x xs -> Edge ys0 yn $ Map.insertWith merger x (Leaf xs xn) yM) (Edge xs0 (xn + yn) yM)
x@Edge{} `merger` y@Leaf{} = merger y x
Edge xs0 xn xM `merger` Edge ys0 yn yM
	= inline merge' xs0 ys0 (edger xn xM) (edger yn yM) (cons xs0 xn yn yM) (cons ys0 yn xn xM)
			(Edge xs0 (xn + yn) $ Map.unionWith merger xM yM)
	where	edger n m l = Edge l n m
		cons xs xn yn yM y = Edge xs xn . Map.singleton y . edger yn yM
Empty `merger` trie = trie
trie `merger` Empty = trie

trieFromList :: Ord e => [[e]] -> Trie e
trieFromList = extractCommon mempty
	where	extractCommon pfx xs = case groupBy ((==) `on` listToMaybe) (sortBy (comparing listToMaybe) xs) of
			[]			-> Empty
			(empties@([]:_):xs')	-> Edge pfx (fromIntegral $ length empties) (fromGroups xs')
			[(y:ys):yss]		-> extractCommon (pfx |> y) (ys:map tail yss)
			xs'			-> Edge pfx 0 (fromGroups xs')
		fromGroups xss = Map.fromDistinctAscList [(y, trieFromList $ ys : map tail yss) | ((y:ys):yss) <- xss]