1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE NamedFieldPuns #-}
4 -- | This module implements a tree of 'Data.Map.Map'.
5 module Hcompta.Lib.TreeMap where
7 import Control.Applicative ((<$>), (<*>), pure)
8 import Data.Data (Data)
9 import Data.Foldable (Foldable(..))
10 import qualified Data.List
11 import qualified Data.List.NonEmpty
12 import Data.List.NonEmpty (NonEmpty(..))
13 import qualified Data.Map
14 import Data.Monoid (Monoid(..))
15 import Data.Traversable (Traversable(..))
16 import Data.Typeable (Typeable)
20 -- | A 'Path' is a non-empty list.
21 type Path k = NonEmpty k
23 path :: k -> [k] -> Path k
27 list = Data.List.NonEmpty.toList
29 rev :: Path k -> Path k
30 rev = Data.List.NonEmpty.reverse
32 -- * The 'TreeMap' type
34 type TreeMap k x = Data.Map.Map k (Node k x)
35 data Ord k => Node k x
37 { node_size :: Int -- ^ The number of non-'Nothing' 'node_content's reachable from this 'Node'.
38 , node_content :: Maybe x -- ^ Some content, or 'Nothing' if this 'Node' is intermediary.
39 , node_descendants :: TreeMap k x -- ^ Descendants 'Node's.
40 } deriving (Data, Eq, Read, Show, Typeable)
42 instance (Ord k, Monoid v) => Monoid (Node k v) where
45 { node_content = Nothing
47 , node_descendants = mempty
50 Node{node_content=x0, node_descendants=m0}
51 Node{node_content=x1, node_descendants=m1} =
52 let m = union const m0 m1 in
53 let x = x0 `mappend` x1 in
56 , node_size = size m + maybe 0 (const 1) x
57 , node_descendants = union const m0 m1
59 -- mconcat = Data.List.foldr mappend mempty
61 instance Ord k => Functor (Node k) where
62 fmap f Node{node_content=x, node_descendants=m, node_size} =
64 { node_content = fmap f x
65 , node_descendants = Hcompta.Lib.TreeMap.map f m
69 instance Ord k => Foldable (Node k) where
70 foldMap f Node{node_content=Nothing, node_descendants=m} =
72 foldMap f Node{node_content=Just x, node_descendants=m} =
73 f x `mappend` foldMap (foldMap f) m
75 instance Ord k => Traversable (Node k) where
76 traverse f Node{node_content=Nothing, node_descendants=m, node_size} =
77 Node node_size <$> pure Nothing <*> traverse (traverse f) m
78 traverse f Node{node_content=Just x, node_descendants=m, node_size} =
79 Node node_size <$> (Just <$> f x) <*> traverse (traverse f) m
84 empty = Data.Map.empty
86 singleton :: Ord k => Path k -> x -> TreeMap k x
87 singleton ks x = insert const ks x Data.Map.empty
89 leaf :: Ord k => x -> Node k x
92 { node_content = Just x
93 , node_descendants = Data.Map.empty
97 -- | Return the given 'TreeMap' associating the given 'Path' with the given content,
98 -- merging contents if the given 'TreeMap' already associates the given 'Path'
99 -- with a non-'Nothing' 'node_content'.
100 insert :: Ord k => (x -> x -> x) -> Path k -> x -> TreeMap k x -> TreeMap k x
101 insert merge (k:|[]) x m =
103 (\_ Node{node_content=x1, node_descendants=m1, node_size=s1} ->
105 { node_content = maybe (Just x) (Just . merge x) x1
106 , node_descendants = m1
107 , node_size = maybe (s1 + 1) (const s1) x1
110 insert merge (k:|k':ks) x m =
112 (\_ Node{node_content=x1, node_descendants=m1} ->
113 let m' = insert merge (path k' ks) x m1 in
114 Node{node_content=x1, node_descendants=m', node_size=size m' + maybe 0 (const 1) x1})
117 { node_content = Nothing
118 , node_descendants = insert merge (path k' ks) x Data.Map.empty
123 -- | Return a 'TreeMap' associating the given 'Path' to the given content,
124 -- merging content of identical 'Path's (in respective order).
125 from_List :: Ord k => (x -> x -> x) -> [(Path k, x)] -> TreeMap k x
126 from_List merge = Data.List.foldl (\acc (p, x) -> insert merge p x acc) empty
128 -- | Return a 'TreeMap' associating the same 'Path's as both given 'TreeMap's,
129 -- merging contents (in respective order) when a 'Path' leads
130 -- to a non-'Nothing' 'node_content' in both given 'TreeMap's.
131 union :: Ord k => (x -> x -> x) -> TreeMap k x -> TreeMap k x -> TreeMap k x
134 (\Node{node_content=x0, node_descendants=m0}
135 Node{node_content=x1, node_descendants=m1} ->
136 let m = union merge m0 m1 in
137 let x = maybe x1 (\x0' -> maybe (Just x0') (Just . merge x0') x1) x0 in
140 , node_descendants = m
141 , node_size = size m + maybe 0 (const 1) x
144 -- | Return the 'union' of the given 'TreeMap's.
146 -- NOTE: use 'Data.List.foldl'' to reduce demand on the control-stack.
147 unions :: Ord k => (x -> x -> x) -> [TreeMap k x] -> TreeMap k x
148 unions merge ts = Data.List.foldl' (union merge) empty ts
150 -- foldl' :: (a -> b -> a) -> a -> [b] -> a
154 -- go z (x:xs) = z `seq` go (f z x) xs
156 -- | Return the given 'TreeMap' with each non-'Nothing' 'node_content'
157 -- mapped by the given function.
158 map :: Ord k => (x -> y) -> TreeMap k x -> TreeMap k y
161 (\n@Node{node_content=x, node_descendants=m} ->
162 n{ node_content=maybe Nothing (Just . f) x
163 , node_descendants=Hcompta.Lib.TreeMap.map f m
166 -- | Return the given 'TreeMap' with each 'node_content'
167 -- mapped by the given function supplied with
168 -- the already mapped 'node_descendants' of the current 'Node'.
169 depth_first_map :: Ord k => (TreeMap k y -> Maybe x -> y) -> TreeMap k x -> TreeMap k y
172 (\n@Node{node_content, node_descendants} ->
173 let m = depth_first_map f node_descendants in
174 let x = f m node_content in
175 n{ node_content = Just x
176 , node_descendants = m
177 , node_size = size m + 1
182 -- | Return the number of non-'Nothing' 'node_content's in the given 'TreeMap'.
183 size :: Ord k => TreeMap k x -> Int
184 size = Data.Map.foldr ((+) . node_size) 0
186 -- | Return the content (if any) associated with the given 'Path'.
187 find :: Ord k => Path k -> TreeMap k x -> Maybe x
188 find (k:|[]) m = maybe Nothing node_content $ Data.Map.lookup k m
190 maybe Nothing (find (path k' ks) . node_descendants) $
193 -- | Return the given accumulator folded by the given function
194 -- applied on non-'Nothing' 'node_content's
195 -- from left to right through the given 'TreeMap'.
196 foldlWithKey :: Ord k => (a -> Path k -> x -> a) -> a -> TreeMap k x -> a
201 => [k] -> (a -> Path k -> x -> a)
202 -> a -> TreeMap k x -> a
204 Data.Map.foldlWithKey
205 (\acc k Node{node_content, node_descendants} ->
207 let acc' = maybe acc (fct acc (rev p')) node_content in
208 foldp (k:p) fct acc' node_descendants)
210 -- | Return the given accumulator folded by the given function
211 -- applied on non-'Nothing' 'node_content's
212 -- from right to left through the given 'TreeMap'.
213 foldrWithKey :: Ord k => (Path k -> x -> a -> a) -> a -> TreeMap k x -> a
218 => [k] -> (Path k -> x -> a -> a)
219 -> a -> TreeMap k x -> a
221 Data.Map.foldrWithKey
222 (\k Node{node_content, node_descendants} acc ->
224 let acc' = foldp (k:p) fct acc node_descendants in
225 maybe acc' (\x -> fct (rev p') x acc') node_content)
227 -- | Return a 'Data.Map.Map' associating each 'Path'
228 -- leading to a non-'Nothing' 'node_content' in the given 'TreeMap',
229 -- with its content mapped by the given function.
230 flatten :: Ord k => (x -> y) -> TreeMap k x -> Data.Map.Map (Path k) y
237 -> Data.Map.Map (Path k) y
241 Data.Map.mapKeysMonotonic (rev . flip path p) $
242 Data.Map.mapMaybe (\Node{node_content=x} -> f <$> x) m
244 Data.Map.foldrWithKey
245 (\k -> (:) . flat_map (k:p) f . node_descendants)