Skip to content

Commit 3e619ce

Browse files
authored
Update compareSize implementations (#1139)
The previous implementations went straight down the left spine, which does not match the documented complexity. We must decrement and potentially return at each step to meet O(min(n,c)). Also add some benchmarks for size and compareSize.
1 parent c83961f commit 3e619ce

File tree

4 files changed

+25
-13
lines changed

4 files changed

+25
-13
lines changed

containers-tests/benchmarks/IntMap.hs

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ main = do
7979
whnf (uncurry M.withoutKeys) (m_random, s_random2)
8080
, bench "restrictKeys:random" $ -- large keys, no overlap
8181
whnf (uncurry M.restrictKeys) (m_random, s_random2)
82+
, bench "size" $ whnf M.size m
83+
, bench "compareSize:2" $ whnf (flip M.compareSize 2) m
84+
, bench "compareSize:n" $ whnf (flip M.compareSize bound) m
8285
, bgroup "folds" $ foldBenchmarks M.foldr M.foldl M.foldr' M.foldl' foldMap m
8386
, bgroup "folds with key" $
8487
foldWithKeyBenchmarks M.foldrWithKey M.foldlWithKey M.foldrWithKey' M.foldlWithKey' M.foldMapWithKey m

containers-tests/benchmarks/IntSet.hs

+6
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ main = do
7272
, bench "eq" $ whnf (\s' -> s' == s') s -- worst case, compares everything
7373
, bench "compare:dense" $ whnf (\s' -> compare s' s') s -- worst case, compares everything
7474
, bench "compare:sparse" $ whnf (\s' -> compare s' s') s_sparse -- worst case, compares everything
75+
, bench "size" $ whnf IS.size s
76+
, bench "size:sparse" $ whnf IS.size s_sparse
77+
, bench "compareSize:2" $ whnf (flip IS.compareSize 2) s
78+
, bench "compareSize:sparse:2" $ whnf (flip IS.compareSize 2) s_sparse
79+
, bench "compareSize:n" $ whnf (flip IS.compareSize bound) s
80+
, bench "compareSize:sparse:n" $ whnf (flip IS.compareSize bound) s_sparse
7581
, bgroup "folds:dense" $ foldBenchmarks IS.foldr IS.foldl IS.foldr' IS.foldl' IS.foldMap s
7682
, bgroup "folds:sparse" $ foldBenchmarks IS.foldr IS.foldl IS.foldr' IS.foldl' IS.foldMap s_sparse
7783
]

containers/src/Data/IntMap/Internal.hs

+6-5
Original file line numberDiff line numberDiff line change
@@ -598,16 +598,17 @@ size = go 0
598598
--
599599
-- @since FIXME
600600
compareSize :: IntMap a -> Int -> Ordering
601-
compareSize !_ c0 | c0 < 0 = GT
602601
compareSize Nil c0 = compare 0 c0
603-
compareSize t c0 = compare 0 (go t c0)
602+
compareSize _ c0 | c0 <= 0 = GT
603+
compareSize t c0 = compare 0 (go t (c0 - 1))
604604
where
605+
go (Bin _ _ _) 0 = -1
605606
go (Bin _ l r) c
606-
| c' <= 0 = -1
607+
| c' < 0 = c'
607608
| otherwise = go r c'
608609
where
609-
c' = go l c
610-
go _ c = c - 1 -- Must be Tip (invariant)
610+
c' = go l (c - 1)
611+
go _ c = c
611612

612613
-- | \(O(\min(n,W))\). Is the key a member of the map?
613614
--

containers/src/Data/IntSet/Internal.hs

+10-8
Original file line numberDiff line numberDiff line change
@@ -377,16 +377,18 @@ size = go 0
377377
--
378378
-- @since FIXME
379379
compareSize :: IntSet -> Int -> Ordering
380-
compareSize !_ c0 | c0 < 0 = GT
381-
compareSize t c0 = compare 0 (go t c0)
380+
compareSize Nil c0 = compare 0 c0
381+
compareSize _ c0 | c0 <= 0 = GT
382+
compareSize t c0 = compare 0 (go t (c0 - 1))
382383
where
384+
go (Bin _ _ _) 0 = -1
383385
go (Bin _ l r) c
384-
| c' <= 0 = -1
385-
| otherwise = go r c'
386-
where
387-
c' = go l c
388-
go (Tip _ bm) c = c - popCount bm
389-
go Nil c = c
386+
| c' < 0 = c'
387+
| otherwise = go r c'
388+
where
389+
c' = go l (c - 1)
390+
go (Tip _ bm) c = c + 1 - popCount bm
391+
go Nil !_ = error "compareSize.go: Nil"
390392

391393
-- | \(O(\min(n,W))\). Is the value a member of the set?
392394

0 commit comments

Comments
 (0)