77{-# LANGUAGE ViewPatterns #-}
88module Data.RandomAccessList.SkewBinary
99 ( RAList (Cons ,Nil )
10+ , contIndexZero
11+ , contIndexOne
1012 , safeIndexZero
1113 , unsafeIndexZero
1214 , safeIndexOne
13- , safeIndexOneCont
1415 , unsafeIndexOne
1516 , Data.RandomAccessList.SkewBinary. null
1617 , uncons
@@ -48,7 +49,7 @@ data RAList a = BHead
4849null :: RAList a -> Bool
4950null Nil = True
5051null _ = False
51- {-# INLINABLE null #-}
52+ {-# INLINE null #-}
5253
5354{-# complete Cons, Nil #-}
5455{-# complete BHead, Nil #-}
@@ -63,6 +64,7 @@ cons :: a -> RAList a -> RAList a
6364cons x = \ case
6465 (BHead w1 t1 (BHead w2 t2 ts')) | w1 == w2 -> BHead (2 * w1+ 1 ) (Node x t1 t2) ts'
6566 ts -> BHead 1 (Leaf x) ts
67+ {-# INLINE cons #-}
6668
6769-- /O(1)/
6870uncons :: RAList a -> Maybe (a , RAList a )
@@ -74,122 +76,94 @@ uncons = \case
7476 -- split the node in two)
7577 in Just (x, BHead halfSize t1 $ BHead halfSize t2 ts)
7678 Nil -> Nothing
79+ {-# INLINE uncons #-}
7780
78- -- 0-based
79- unsafeIndexZero :: RAList a -> Word64 -> a
80- unsafeIndexZero Nil _ = error " out of bounds"
81- unsafeIndexZero (BHead w t ts) ! i =
82- if i < w
83- then indexTree w i t
84- else unsafeIndexZero ts (i- w)
85- where
86- indexTree :: Word64 -> Word64 -> Tree a -> a
87- indexTree 1 0 (Leaf x) = x
88- indexTree _ _ (Leaf _) = error " out of bounds"
89- indexTree _ 0 (Node x _ _) = x
90- indexTree treeSize offset (Node _ t1 t2) =
91- let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
92- in if offset <= halfSize
93- then indexTree halfSize (offset - 1 ) t1
94- else indexTree halfSize (offset - 1 - halfSize) t2
95-
96- -- 0-based
97- safeIndexZero :: RAList a -> Word64 -> Maybe a
98- safeIndexZero Nil _ = Nothing
99- safeIndexZero (BHead w t ts) ! i =
100- if i < w
101- then indexTree w i t
102- else safeIndexZero ts (i- w)
103- where
104- indexTree :: Word64 -> Word64 -> Tree a -> Maybe a
105- indexTree 1 0 (Leaf x) = Just x
106- indexTree _ _ (Leaf _) = Nothing
107- indexTree _ 0 (Node x _ _) = Just x
108- indexTree treeSize offset (Node _ t1 t2) =
109- let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
110- in if offset <= halfSize
111- then indexTree halfSize (offset - 1 ) t1
112- else indexTree halfSize (offset - 1 - halfSize) t2
113-
114- -- 1-based
115- unsafeIndexOne :: RAList a -> Word64 -> a
116- unsafeIndexOne Nil _ = error " out of bounds"
117- unsafeIndexOne (BHead w t ts) ! i =
118- if i <= w
119- then indexTree w i t
120- else unsafeIndexOne ts (i- w)
121- where
122- indexTree :: Word64 -> Word64 -> Tree a -> a
123- indexTree _ 0 _ = error " index zero"
124- indexTree 1 1 (Leaf x) = x
125- indexTree _ _ (Leaf _) = error " out of bounds"
126- indexTree _ 1 (Node x _ _) = x
127- indexTree treeSize offset (Node _ t1 t2) =
128- let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
129- offset' = offset - 1
130- in if offset' <= halfSize
131- then indexTree halfSize offset' t1
132- else indexTree halfSize (offset' - halfSize) t2
133-
134- {- Note [Optimizations of safeIndexOneCont]
135- Bangs in the local definitions of 'safeIndexOneCont' are needed to tell GHC that the functions are
81+ {- Note [Optimizations of contIndexZero]
82+ Bangs in the local definitions of 'contIndexZero' are needed to tell GHC that the functions are
13683strict in the 'Word64' argument, so that GHC produces workers operating on @Word64#@.
13784
13885The function itself is CPS-ed, so that the arguments force the local definitions to be retained
139- within 'safeIndexOneCont ' instead of being pulled out via full-laziness or some other optimization
140- pass. This ensures that when 'safeIndexOneCont ' gets inlined, the local definitions appear directly
141- in the GHC Core, allowing GHC to inline the arguments of 'safeIndexOneCont ' and transform the whole
86+ within 'contIndexZero ' instead of being pulled out via full-laziness or some other optimization
87+ pass. This ensures that when 'contIndexZero ' gets inlined, the local definitions appear directly
88+ in the GHC Core, allowing GHC to inline the arguments of 'contIndexZero ' and transform the whole
14289thing into a beautiful recursive join point full of @Word64#@s, i.e. allocating very little if
14390anything at all.
14491-}
14592
146- -- See Note [Optimizations of safeIndexOneCont ].
147- safeIndexOneCont :: forall a b . b -> (a -> b ) -> RAList a -> Word64 -> b
148- safeIndexOneCont z f = findTree where
93+ -- See Note [Optimizations of contIndexZero ].
94+ contIndexZero :: forall a b . b -> (a -> b ) -> RAList a -> Word64 -> b
95+ contIndexZero z f = findTree where
14996 findTree :: RAList a -> Word64 -> b
150- -- See Note [Optimizations of safeIndexOneCont ].
97+ -- See Note [Optimizations of contIndexZero ].
15198 findTree Nil ! _ = z
15299 findTree (BHead w t ts) i =
153- if i <= w
100+ if i < w
154101 then indexTree w i t
155102 else findTree ts (i- w)
156103
157104 indexTree :: Word64 -> Word64 -> Tree a -> b
158- -- See Note [Optimizations of safeIndexOneCont ].
159- indexTree ! w 1 t = case t of
105+ -- See Note [Optimizations of contIndexZero ].
106+ indexTree ! w 0 t = case t of
160107 Node x _ _ -> f x
161108 Leaf x -> if w == 1 then f x else z
162- indexTree _ 0 _ = z -- "index zero"
163109 indexTree _ _ (Leaf _) = z
164110 indexTree treeSize offset (Node _ t1 t2) =
165111 let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
166- offset' = offset - 1
167- in if offset' <= halfSize
168- then indexTree halfSize offset' t1
169- else indexTree halfSize (offset' - halfSize) t2
170- {-# INLINE safeIndexOneCont #-}
112+ in if offset <= halfSize
113+ then indexTree halfSize (offset - 1 ) t1
114+ else indexTree halfSize (offset - 1 - halfSize) t2
115+ {-# INLINE contIndexZero #-}
116+
117+ contIndexOne :: forall a b . b -> (a -> b ) -> RAList a -> Word64 -> b
118+ contIndexOne z _ _ 0 = z
119+ contIndexOne z f t n = contIndexZero z f t (n - 1 )
120+ {-# INLINE contIndexOne #-}
121+
122+ -- 0-based
123+ unsafeIndexZero :: RAList a -> Word64 -> a
124+ unsafeIndexZero = contIndexZero (error " out of bounds" ) id
125+ {-# INLINE unsafeIndexZero #-}
126+
127+ -- 0-based
128+ safeIndexZero :: RAList a -> Word64 -> Maybe a
129+ safeIndexZero = contIndexZero Nothing Just
130+ {-# INLINE safeIndexZero #-}
131+
132+ -- 1-based
133+ unsafeIndexOne :: RAList a -> Word64 -> a
134+ unsafeIndexOne = contIndexOne (error " out of bounds" ) id
135+ {-# INLINE unsafeIndexOne #-}
171136
172137-- 1-based
173138safeIndexOne :: RAList a -> Word64 -> Maybe a
174- safeIndexOne = safeIndexOneCont Nothing Just
139+ safeIndexOne = contIndexOne Nothing Just
140+ {-# INLINE safeIndexOne #-}
175141
176142instance RAL. RandomAccessList (RAList a ) where
177143 type Element (RAList a ) = a
178144
179- {-# INLINABLE empty #-}
180145 empty = Nil
181- {-# INLINABLE cons #-}
146+ {-# INLINE empty #-}
147+
182148 cons = Cons
183- {-# INLINABLE uncons #-}
149+ {-# INLINE cons #-}
150+
184151 uncons = uncons
185- {-# INLINABLE length #-}
186- length Nil = 0
187- length (BHead sz _ tl) = sz + RAL. length tl
188- {-# INLINABLE indexZero #-}
152+ {-# INLINE uncons #-}
153+
154+ length = go 0 where
155+ go ! acc Nil = acc
156+ go ! acc (BHead sz _ tl) = go (acc + sz) tl
157+ {-# INLINE length #-}
158+
189159 indexZero = safeIndexZero
190- {-# INLINABLE indexOne #-}
160+ {-# INLINE indexZero #-}
161+
191162 indexOne = safeIndexOne
192- {-# INLINABLE unsafeIndexZero #-}
163+ {-# INLINE indexOne #-}
164+
193165 unsafeIndexZero = unsafeIndexZero
194- {-# INLINABLE unsafeIndexOne #-}
166+ {-# INLINE unsafeIndexZero #-}
167+
195168 unsafeIndexOne = unsafeIndexOne
169+ {-# INLINE unsafeIndexOne #-}
0 commit comments