Skip to content

Commit 2562a65

Browse files
committed
Make rigidify non-recursive
`rigidify` would previously call itself at most once before producing a constructor. This made it somewhat hard to see that it had no infinite loops, and increased the number of tests required. Improve internal documentation. Rename the small immediate indexing benchmark to make some kind of sense. Add more and better immediate indexing benchmarks. Benchmarks: <*>/ix500/1000^2 -13.81% 1.40e-06 <*>/ix500000/1000^2 -38.91% 5.14e-06 <*>/ixBIG -17.96% 1.61e-05 <*>/nf100/2500/rep +0.23% 8.58e-03 <*>/nf100/2500/ff -1.82% 2.37e-02 <*>/nf500/500/rep +0.01% 8.48e-03 <*>/nf500/500/ff -0.94% 2.46e-02 <*>/nf2500/100/rep -0.46% 8.53e-03 <*>/nf2500/100/ff -1.22% 2.45e-02
1 parent c2b2048 commit 2562a65

File tree

2 files changed

+61
-54
lines changed

2 files changed

+61
-54
lines changed

Data/Sequence.hs

Lines changed: 55 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -314,11 +314,16 @@ data Rigidified a = RigidEmpty
314314
deriving Show
315315
#endif
316316

317+
-- | A finger tree whose top level has only Two and/or Three digits, and whose
318+
-- other levels have only One and Two digits. A Rigid tree is precisely what one
319+
-- gets by unzipping/inverting a 2-3 tree, so it is precisely what we need to
320+
-- turn a finger tree into in order to transform it into a 2-3 tree.
317321
data Rigid a = Rigid {-# UNPACK #-} !Int !(Digit23 a) (Thin (Node a)) !(Digit23 a)
318322
#ifdef TESTING
319323
deriving Show
320324
#endif
321325

326+
-- | A finger tree whose digits are all ones and twos
322327
data Thin a = EmptyTh
323328
| SingleTh a
324329
| DeepTh {-# UNPACK #-} !Int !(Digit12 a) (Thin (Node a)) !(Digit12 a)
@@ -331,16 +336,23 @@ data Digit12 a = One12 a | Two12 a a
331336
deriving Show
332337
#endif
333338

339+
-- | Sometimes, we want to emphasize that we are viewing a node as a top-level
340+
-- digit of a 'Rigid' tree.
334341
type Digit23 a = Node a
335342

336-
-- | 'aptyMiddle' does most of the hard work of computing @fs<*>xs@.
337-
-- It produces the center part of a finger tree, with a prefix corresponding
338-
-- to the prefix of @xs@ and a suffix corresponding to the suffix of @xs@
339-
-- omitted; the missing suffix and prefix are added by the caller.
340-
-- For the recursive call, it squashes the prefix and the suffix into
341-
-- the center tree. Once it gets to the bottom, it turns the tree into
342-
-- a 2-3 tree, applies 'mapMulFT' to produce the main body, and glues all
343-
-- the pieces together.
343+
-- | 'aptyMiddle' does most of the hard work of computing @fs<*>xs@. It
344+
-- produces the center part of a finger tree, with a prefix corresponding to
345+
-- the prefix of @xs@ and a suffix corresponding to the suffix of @xs@ omitted;
346+
-- the missing suffix and prefix are added by the caller. For the recursive
347+
-- call, it squashes the prefix and the suffix into the center tree. Once it
348+
-- gets to the bottom, it turns the tree into a 2-3 tree, applies 'mapMulFT' to
349+
-- produce the main body, and glues all the pieces together.
350+
--
351+
-- 'map23' itself is a bit horrifying because of the nested types involved. Its
352+
-- job is to map over the *elements* of a 2-3 tree, rather than the subtrees.
353+
-- If we used a higher-order nested type with MPTC, we could probably use a
354+
-- class, but as it is we have to build up 'map23' explicitly through the
355+
-- recursion.
344356
aptyMiddle
345357
:: Sized c =>
346358
(c -> d)
@@ -440,67 +452,57 @@ mapMulNode :: Int -> (a -> b) -> Node a -> Node b
440452
mapMulNode mul f (Node2 s a b) = Node2 (mul * s) (f a) (f b)
441453
mapMulNode mul f (Node3 s a b c) = Node3 (mul * s) (f a) (f b) (f c)
442454

443-
444-
-- rigidify :: Seq a -> Rigidified (Elem a)
445-
-- rigidify (Seq xs) = rigidify' xs
446-
447455
-- | /O(log n)/ (incremental) Takes the extra flexibility out of a 'FingerTree'
448456
-- to make it a genuine 2-3 finger tree. The result of 'rigidify' will have
449-
-- only 'Two' and 'Three' digits at the top level and only 'One' and 'Two'
450-
-- digits elsewhere. It gives an error if the tree has fewer than four
451-
-- elements.
457+
-- only two and three digits at the top level and only one and two
458+
-- digits elsewhere. If the tree has fewer than four elements, 'rigidify'
459+
-- will simply extract them, and will not build a tree.
452460
rigidify :: FingerTree (Elem a) -> Rigidified (Elem a)
453-
-- Note that 'rigidify' may call itself, but it will do so at most
454-
-- once: each call to 'rigidify' will either fix the whole tree or fix one digit
455-
-- and leave the other alone. The patterns below just fix up the top level of
456-
-- the tree; 'rigidify' delegates the hard work to 'thin'.
457-
458-
-- The top of the tree is fine.
459-
460-
rigidify (Deep s (Two a b) m (Three c d e)) =
461-
RigidFull $ Rigid s (node2 a b) (thin m) (node3 c d e)
462-
rigidify (Deep s (Three a b c) m (Three d e f)) =
463-
RigidFull $ Rigid s (node3 a b c) (thin m) (node3 d e f)
464-
rigidify (Deep s (Two a b) m (Two c d)) =
465-
RigidFull $ Rigid s (node2 a b) (thin m) (node2 c d)
466-
rigidify (Deep s (Three a b c) m (Two d e)) =
467-
RigidFull $ Rigid s (node3 a b c) (thin m) (node2 d e)
468-
469-
-- One of the Digits is a Four.
470-
rigidify (Deep s (Four a b c d) m sf) =
471-
rigidify $ Deep s (Two a b) (node2 c d `consTree` m) sf
472-
473-
rigidify (Deep s pr m (Four a b c d)) =
474-
rigidify $ Deep s pr (m `snocTree` node2 a b) (Two c d)
475-
476-
-- One of the Digits is a One.
461+
-- The patterns below just fix up the top level of the tree; 'rigidify'
462+
-- delegates the hard work to 'thin'.
463+
464+
rigidify Empty = RigidEmpty
465+
466+
rigidify (Single q) = RigidOne q
467+
468+
-- The left digit is Two or Three
469+
rigidify (Deep s (Two a b) m sf) = rigidifyRight s (node2 a b) m sf
470+
rigidify (Deep s (Three a b c) m sf) = rigidifyRight s (node3 a b c) m sf
471+
472+
-- The left digit is Four
473+
rigidify (Deep s (Four a b c d) m sf) = rigidifyRight s (node2 a b) (node2 c d `consTree` m) sf
474+
475+
-- The left digit is One
477476
rigidify (Deep s (One a) m sf) = case viewLTree m of
478-
Just2 (Node2 _ b c) m' -> rigidify $ Deep s (Three a b c) m' sf
479-
Just2 (Node3 _ b c d) m' -> rigidify $ Deep s (Two a b) (node2 c d `consTree` m') sf
477+
Just2 (Node2 _ b c) m' -> rigidifyRight s (node3 a b c) m' sf
478+
Just2 (Node3 _ b c d) m' -> rigidifyRight s (node2 a b) (node2 c d `consTree` m') sf
480479
Nothing2 -> case sf of
481480
One b -> RigidTwo a b
482481
Two b c -> RigidThree a b c
483482
Three b c d -> RigidFull $ Rigid s (node2 a b) EmptyTh (node2 c d)
484483
Four b c d e -> RigidFull $ Rigid s (node3 a b c) EmptyTh (node2 d e)
485484

486-
rigidify (Deep s pr m (One e)) = case viewRTree m of
487-
Just2 m' (Node2 _ a b) -> rigidify $ Deep s pr m' (Three a b e)
488-
Just2 m' (Node3 _ a b c) -> rigidify $ Deep s pr (m' `snocTree` node2 a b) (Two c e)
489-
Nothing2 -> case pr of
490-
One a -> RigidTwo a e
491-
Two a b -> RigidThree a b e
492-
Three a b c -> RigidFull $ Rigid s (node2 a b) EmptyTh (node2 c e)
493-
Four a b c d -> RigidFull $ Rigid s (node3 a b c) EmptyTh (node2 d e)
485+
-- | /O(log n)/ (incremental) Takes a tree whose left side has been rigidified
486+
-- and finishes the job.
487+
rigidifyRight :: Int -> Digit23 (Elem a) -> FingerTree (Node (Elem a)) -> Digit (Elem a) -> Rigidified (Elem a)
494488

495-
rigidify Empty = RigidEmpty
496-
497-
rigidify (Single q) = RigidOne q
489+
-- The right digit is Two, Three, or Four
490+
rigidifyRight s pr m (Two a b) = RigidFull $ Rigid s pr (thin m) (node2 a b)
491+
rigidifyRight s pr m (Three a b c) = RigidFull $ Rigid s pr (thin m) (node3 a b c)
492+
rigidifyRight s pr m (Four a b c d) = RigidFull $ Rigid s pr (thin $ m `snocTree` node2 a b) (node2 c d)
498493

494+
-- The right digit is One
495+
rigidifyRight s pr m (One e) = case viewRTree m of
496+
Just2 m' (Node2 _ a b) -> RigidFull $ Rigid s pr (thin m') (node3 a b e)
497+
Just2 m' (Node3 _ a b c) -> RigidFull $ Rigid s pr (thin $ m' `snocTree` node2 a b) (node2 c e)
498+
Nothing2 -> case pr of
499+
Node2 _ a b -> RigidThree a b e
500+
Node3 _ a b c -> RigidFull $ Rigid s (node2 a b) EmptyTh (node2 c e)
499501

500502
-- | /O(log n)/ (incremental) Rejigger a finger tree so the digits are all ones
501503
-- and twos.
502504
thin :: Sized a => FingerTree a -> Thin a
503-
-- Note that 'thin12' will produce a 'Deep' constructor immediately before
505+
-- Note that 'thin12' will produce a 'DeepTh' constructor immediately before
504506
-- recursively calling 'thin'.
505507
thin Empty = EmptyTh
506508
thin (Single a) = SingleTh a

benchmarks/Sequence.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ main = do
4747
, bench "nf10000" $ nf (\s -> S.fromFunction s (+1)) 10000
4848
]
4949
, bgroup "<*>"
50-
[ bench "ix1000/500000" $
50+
[ bench "ix500/1000^2" $
5151
nf (\s -> ((+) <$> s <*> s) `S.index` (S.length s `div` 2)) (S.fromFunction 1000 (+1))
52+
, bench "ix500000/1000^2" $
53+
nf (\s -> ((+) <$> s <*> s) `S.index` (S.length s * S.length s `div` 2)) (S.fromFunction 1000 (+1))
54+
, bench "ixBIG" $
55+
nf (\s -> ((+) <$> s <*> s) `S.index` (S.length s * S.length s `div` 2))
56+
(S.fromFunction (floor (sqrt $ fromIntegral (maxBound::Int))-10) (+1))
5257
, bench "nf100/2500/rep" $
5358
nf (\(s,t) -> (,) <$> replicate s () <*> replicate t ()) (100,2500)
5459
, bench "nf100/2500/ff" $

0 commit comments

Comments
 (0)