• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

msakai / data-interval / 101

20 Jan 2026 11:20PM UTC coverage: 87.081% (+0.3%) from 86.789%
101

push

github

Bodigrim
Restrict and delete interval keys for Data.Map and Data.Set

80 of 85 new or added lines in 2 files covered. (94.12%)

4 existing lines in 2 files now uncovered.

1065 of 1223 relevant lines covered (87.08%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.14
/src/Data/IntervalSet.hs
1
{-# OPTIONS_GHC -Wall #-}
2
{-# LANGUAGE CPP, LambdaCase, ScopedTypeVariables, TypeFamilies, MultiWayIf #-}
3
{-# LANGUAGE Trustworthy #-}
4
{-# LANGUAGE RoleAnnotations #-}
5
-----------------------------------------------------------------------------
6
-- |
7
-- Module      :  Data.IntervalSet
8
-- Copyright   :  (c) Masahiro Sakai 2016
9
-- License     :  BSD-style
10
--
11
-- Maintainer  :  masahiro.sakai@gmail.com
12
-- Stability   :  provisional
13
--
14
-- Interval datatype and interval arithmetic.
15
--
16
-----------------------------------------------------------------------------
17
module Data.IntervalSet
18
  (
19
  -- * IntervalSet type
20
    IntervalSet
21
  , module Data.ExtendedReal
22

23
  -- * Construction
24
  , whole
25
  , empty
26
  , singleton
27

28
  -- * Query
29
  , null
30
  , member
31
  , notMember
32
  , isSubsetOf
33
  , isProperSubsetOf
34
  , span
35

36
  -- * Construction
37
  , complement
38
  , insert
39
  , delete
40

41
  -- * Combine
42
  , union
43
  , unions
44
  , intersection
45
  , intersections
46
  , difference
47

48
  -- * Conversion
49

50
  -- ** List
51
  , fromList
52
  , toList
53

54
  -- ** Ordered list
55
  , toAscList
56
  , toDescList
57
  , fromAscList
58

59
  -- ** Folds
60
  , Data.IntervalSet.foldr
61
  , Data.IntervalSet.foldl'
62
  , Data.IntervalSet.foldMap
63

64
  -- * Operations on the keys of 'Data.Map'
65
  , restrictMapKeysToIntervalSet
66
  , withoutMapKeysFromIntervalSet
67

68
  -- * Operations on 'Data.Set'
69
  , intersectionSetAndIntervalSet
70
  , differenceSetAndIntervalSet
71
  )
72
  where
73

74
import Prelude hiding (Foldable(..), span)
75
#ifdef MIN_VERSION_lattices
76
import Algebra.Lattice
77
#endif
78
import Control.DeepSeq
79
import Data.Data
80
import Data.ExtendedReal
81
import qualified Data.Foldable as Foldable hiding (null, toList)
82
import Data.Function
83
import Data.Hashable
84
import Data.List (sortBy)
85
import Data.Map (Map)
86
import qualified Data.Map as Map
87
import Data.Set (Set)
88
import qualified Data.Set as Set
89
import Data.Maybe
90
import qualified Data.Semigroup as Semigroup
91
import Data.Interval (Interval, Boundary(..))
92
import qualified Data.Interval as Interval
93
import qualified GHC.Exts as GHCExts
94
-- | A set comprising zero or more non-empty, /disconnected/ intervals.
95
--
96
-- Any connected intervals are merged together, and empty intervals are ignored.
97
newtype IntervalSet r = IntervalSet (Map (Extended r) (Interval r))
98
  deriving
99
    ( Eq
1✔
100
    , Ord
×
101
      -- ^ Note that this Ord is derived and not semantically meaningful.
102
      -- The primary intended use case is to allow using 'IntervalSet'
103
      -- in maps and sets that require ordering.
104
    )
105

106

107
type role IntervalSet nominal
108

109
instance (Ord r, Show r) => Show (IntervalSet r) where
110
  showsPrec p (IntervalSet m) = showParen (p > appPrec) $
1✔
111
    showString "fromList " .
1✔
112
    showsPrec (appPrec+1) (Map.elems m)
×
113

114
instance (Ord r, Read r) => Read (IntervalSet r) where
115
  readsPrec p =
1✔
116
    (readParen (p > appPrec) $ \s0 -> do
1✔
117
      ("fromList",s1) <- lex s0
1✔
118
      (xs,s2) <- readsPrec (appPrec+1) s1
×
119
      return (fromList xs, s2))
1✔
120

121
appPrec :: Int
122
appPrec = 10
1✔
123

124
-- This instance preserves data abstraction at the cost of inefficiency.
125
-- We provide limited reflection services for the sake of data abstraction.
126

127
instance (Ord r, Data r) => Data (IntervalSet r) where
128
  gfoldl k z x   = z fromList `k` toList x
1✔
129
  toConstr _     = fromListConstr
×
130
  gunfold k z c  = case constrIndex c of
×
131
    1 -> k (z fromList)
×
132
    _ -> error "gunfold"
×
133
  dataTypeOf _   = setDataType
×
134
  dataCast1 f    = gcast1 f
×
135

136
fromListConstr :: Constr
137
fromListConstr = mkConstr setDataType "fromList" [] Prefix
×
138

139
setDataType :: DataType
140
setDataType = mkDataType "Data.IntervalSet.IntervalSet" [fromListConstr]
×
141

142
instance NFData r => NFData (IntervalSet r) where
143
  rnf (IntervalSet m) = rnf m
1✔
144

145
instance Hashable r => Hashable (IntervalSet r) where
146
  hashWithSalt s (IntervalSet m) = hashWithSalt s (Map.toList m)
1✔
147

148
#ifdef MIN_VERSION_lattices
149
instance (Ord r) => Lattice (IntervalSet r) where
150
  (\/) = union
1✔
151
  (/\) = intersection
1✔
152

153
instance (Ord r) => BoundedJoinSemiLattice (IntervalSet r) where
154
  bottom = empty
1✔
155

156
instance (Ord r) => BoundedMeetSemiLattice (IntervalSet r) where
157
  top = whole
1✔
158
#endif
159

160
instance Ord r => Monoid (IntervalSet r) where
161
  mempty = empty
1✔
162
  mappend = (Semigroup.<>)
×
163
  mconcat = unions
×
164

165
instance (Ord r) => Semigroup.Semigroup (IntervalSet r) where
166
  (<>)    = union
1✔
167
  stimes  = Semigroup.stimesIdempotentMonoid
×
168

169
lift1
170
  :: Ord r => (Interval r -> Interval r)
171
  -> IntervalSet r -> IntervalSet r
172
lift1 f as = fromList [f a | a <- toList as]
1✔
173

174
lift2
175
  :: Ord r => (Interval r -> Interval r -> Interval r)
176
  -> IntervalSet r -> IntervalSet r -> IntervalSet r
177
lift2 f as bs = fromList [f a b | a <- toList as, b <- toList bs]
1✔
178

179
instance (Num r, Ord r) => Num (IntervalSet r) where
180
  (+) = lift2 (+)
1✔
181

182
  (*) = lift2 (*)
1✔
183

184
  negate = lift1 negate
1✔
185

186
  abs = lift1 abs
1✔
187

188
  fromInteger i = singleton (fromInteger i)
×
189

190
  signum xs = fromList $ do
1✔
191
    x <- toList xs
1✔
192
    y <-
193
      [ if Interval.member 0 x
1✔
194
        then Interval.singleton 0
1✔
195
        else Interval.empty
1✔
196
      , if Interval.null ((0 Interval.<..< inf) `Interval.intersection` x)
1✔
197
        then Interval.empty
1✔
198
        else Interval.singleton 1
1✔
199
      , if Interval.null ((-inf Interval.<..< 0) `Interval.intersection` x)
1✔
200
        then Interval.empty
1✔
201
        else Interval.singleton (-1)
1✔
202
      ]
203
    return y
1✔
204

205
-- | @recip (recip xs) == delete 0 xs@
206
instance forall r. (Real r, Fractional r) => Fractional (IntervalSet r) where
207
  fromRational r = singleton (fromRational r)
1✔
208
  recip xs = lift1 recip (delete (Interval.singleton 0) xs)
1✔
209

210
instance Ord r => GHCExts.IsList (IntervalSet r) where
211
  type Item (IntervalSet r) = Interval r
212
  fromList = fromList
×
213
  toList = toList
×
214

215
-- -----------------------------------------------------------------------
216

217
-- | whole real number line (-∞, ∞)
218
whole :: Ord r => IntervalSet r
219
whole = singleton $ Interval.whole
1✔
220

221
-- | empty interval set
222
empty :: Ord r => IntervalSet r
223
empty = IntervalSet Map.empty
1✔
224

225
-- | single interval
226
singleton :: Ord r => Interval r -> IntervalSet r
227
singleton i
1✔
228
  | Interval.null i = empty
1✔
229
  | otherwise = IntervalSet $ Map.singleton (Interval.lowerBound i) i
1✔
230

231
-- -----------------------------------------------------------------------
232

233
-- | Is the interval set empty?
234
null :: IntervalSet r -> Bool
235
null (IntervalSet m) = Map.null m
1✔
236

237
-- | Is the element in the interval set?
238
member :: Ord r => r -> IntervalSet r -> Bool
239
member x (IntervalSet m) =
1✔
240
  case Map.lookupLE (Finite x) m of
1✔
241
    Nothing -> False
1✔
242
    Just (_,i) -> Interval.member x i
1✔
243

244
-- | Is the element not in the interval set?
245
notMember :: Ord r => r -> IntervalSet r -> Bool
246
notMember x is = not $ x `member` is
1✔
247

248
-- | Is this a subset?
249
-- @(is1 \``isSubsetOf`\` is2)@ tells whether @is1@ is a subset of @is2@.
250
isSubsetOf :: Ord r => IntervalSet r -> IntervalSet r -> Bool
251
isSubsetOf is1 is2 = all (\i1 -> f i1 is2) (toList is1)
1✔
252
  where
253
    f i1 (IntervalSet m) =
1✔
254
      case Map.lookupLE (Interval.lowerBound i1) m of
1✔
255
        Nothing -> False
1✔
256
        Just (_,i2) -> Interval.isSubsetOf i1 i2
1✔
257

258
-- | Is this a proper subset? (/i.e./ a subset but not equal).
259
isProperSubsetOf :: Ord r => IntervalSet r -> IntervalSet r -> Bool
260
isProperSubsetOf is1 is2 = isSubsetOf is1 is2 && is1 /= is2
1✔
261

262
-- | convex hull of a set of intervals.
263
span :: Ord r => IntervalSet r -> Interval r
264
span (IntervalSet m) =
1✔
265
  case Map.minView m of
1✔
266
    Nothing -> Interval.empty
1✔
267
    Just (i1, _) ->
268
      case Map.maxView m of
1✔
269
        Nothing -> Interval.empty
×
270
        Just (i2, _) ->
271
          Interval.interval (Interval.lowerBound' i1) (Interval.upperBound' i2)
1✔
272

273
-- -----------------------------------------------------------------------
274

275
-- | Complement the interval set.
276
complement :: Ord r => IntervalSet r -> IntervalSet r
277
complement (IntervalSet m) = fromAscList $ f (NegInf,Open) (Map.elems m)
×
278
  where
279
    f prev [] = [ Interval.interval prev (PosInf,Open) ]
×
280
    f prev (i : is) =
281
      case (Interval.lowerBound' i, Interval.upperBound' i) of
1✔
282
        ((lb, in1), (ub, in2)) ->
283
          Interval.interval prev (lb, notB in1) : f (ub, notB in2) is
1✔
284

285
-- | Insert a new interval into the interval set.
286
insert :: Ord r => Interval r -> IntervalSet r -> IntervalSet r
287
insert i is | Interval.null i = is
1✔
288
insert i (IntervalSet is) = IntervalSet $ Map.unions
1✔
289
  [ smaller'
1✔
290
  , case fromList $ i : maybeToList m0 ++ maybeToList m1 ++ maybeToList m2 of
1✔
291
      IntervalSet m -> m
1✔
292
  , larger
1✔
293
  ]
294
  where
295
    (smaller, m1, xs) = splitLookupLE (Interval.lowerBound i) is
1✔
296
    (_, m2, larger) = splitLookupLE (Interval.upperBound i) xs
1✔
297

298
    -- A tricky case is when an interval @i@ connects two adjacent
299
    -- members of IntervalSet, e. g., inserting {0} into (whole \\ {0}).
300
    (smaller', m0) = case Map.maxView smaller of
1✔
301
      Nothing -> (smaller, Nothing)
1✔
302
      Just (v, rest)
303
        | Interval.isConnected v i -> (rest, Just v)
1✔
304
      _ -> (smaller, Nothing)
1✔
305

306
-- | Delete an interval from the interval set.
307
delete :: Ord r => Interval r -> IntervalSet r -> IntervalSet r
308
delete i is | Interval.null i = is
1✔
309
delete i (IntervalSet is) = IntervalSet $
1✔
310
  case splitLookupLE (Interval.lowerBound i) is of
1✔
311
    (smaller, m1, xs) ->
312
      case splitLookupLE (Interval.upperBound i) xs of
1✔
313
        (_, m2, larger) ->
314
          Map.unions
1✔
315
          [ smaller
1✔
316
          , case m1 of
1✔
317
              Nothing -> Map.empty
1✔
318
              Just j -> Map.fromList
1✔
319
                [ (Interval.lowerBound k, k)
1✔
320
                | i' <- [upTo i, downTo i], let k = i' `Interval.intersection` j, not (Interval.null k)
1✔
321
                ]
322
          , if
1✔
323
            | Just j <- m2, j' <- downTo i `Interval.intersection` j, not (Interval.null j') ->
1✔
324
                Map.singleton (Interval.lowerBound j') j'
1✔
325
            | otherwise -> Map.empty
1✔
326
          , larger
1✔
327
          ]
328

329
-- | union of two interval sets
330
union :: Ord r => IntervalSet r -> IntervalSet r -> IntervalSet r
331
union is1@(IntervalSet m1) is2@(IntervalSet m2) =
1✔
332
  if Map.size m1 >= Map.size m2
1✔
333
  then Foldable.foldl' (\is i -> insert i is) is1 (toList is2)
1✔
334
  else Foldable.foldl' (\is i -> insert i is) is2 (toList is1)
1✔
335

336
-- | union of a list of interval sets
337
unions :: Ord r => [IntervalSet r] -> IntervalSet r
338
unions = Foldable.foldl' union empty
1✔
339

340
-- | intersection of two interval sets
341
intersection :: Ord r => IntervalSet r -> IntervalSet r -> IntervalSet r
342
intersection is1 is2 = difference is1 (complement is2)
1✔
343

344
-- | intersection of a list of interval sets
345
intersections :: Ord r => [IntervalSet r] -> IntervalSet r
346
intersections = Foldable.foldl' intersection whole
1✔
347

348
-- | difference of two interval sets
349
difference :: Ord r => IntervalSet r -> IntervalSet r -> IntervalSet r
350
difference is1 is2 =
1✔
351
  Foldable.foldl' (\is i -> delete i is) is1 (toList is2)
1✔
352

353
-- -----------------------------------------------------------------------
354

355
-- | Build a interval set from a list of intervals.
356
fromList :: Ord r => [Interval r] -> IntervalSet r
357
fromList = IntervalSet . fromAscList' . sortBy (compareLB `on` Interval.lowerBound')
1✔
358

359
-- | Build a map from an ascending list of intervals.
360
-- /The precondition is not checked./
361
fromAscList :: Ord r => [Interval r] -> IntervalSet r
362
fromAscList = IntervalSet . fromAscList'
1✔
363

364
fromAscList' :: Ord r => [Interval r] -> Map (Extended r) (Interval r)
365
fromAscList' = Map.fromDistinctAscList . map (\i -> (Interval.lowerBound i, i)) . f
1✔
366
  where
367
    f :: Ord r => [Interval r] -> [Interval r]
368
    f [] = []
1✔
369
    f (x : xs) = g x xs
1✔
370
    g x [] = [x | not (Interval.null x)]
1✔
371
    g x (y : ys)
372
      | Interval.null x = g y ys
1✔
373
      | Interval.isConnected x y = g (x `Interval.hull` y) ys
1✔
374
      | otherwise = x : g y ys
1✔
375

376
-- | Convert a interval set into a list of intervals.
377
toList :: Ord r => IntervalSet r -> [Interval r]
378
toList = toAscList
1✔
379

380
-- | Convert a interval set into a list of intervals in ascending order.
381
toAscList :: Ord r => IntervalSet r -> [Interval r]
382
toAscList (IntervalSet m) = Map.elems m
1✔
383

384
-- | Convert a interval set into a list of intervals in descending order.
385
toDescList :: Ord r => IntervalSet r -> [Interval r]
386
toDescList (IntervalSet m) = fmap snd $ Map.toDescList m
1✔
387

388
-- -----------------------------------------------------------------------
389

390
splitLookupLE :: Ord k => k -> Map k v -> (Map k v, Maybe v, Map k v)
391
splitLookupLE k m =
1✔
392
  case Map.spanAntitone (<= k) m of
1✔
393
    (lessOrEqual, greaterThan) ->
394
      case Map.maxView lessOrEqual of
1✔
395
        Just (v, lessOrEqual') -> (lessOrEqual', Just v, greaterThan)
1✔
396
        Nothing -> (lessOrEqual, Nothing, greaterThan)
1✔
397

398
compareLB :: Ord r => (Extended r, Boundary) -> (Extended r, Boundary) -> Ordering
399
compareLB (lb1, lb1in) (lb2, lb2in) =
1✔
400
  -- inclusive lower endpoint shuold be considered smaller
401
  (lb1 `compare` lb2) `mappend` (lb2in `compare` lb1in)
1✔
402

403
upTo :: Ord r => Interval r -> Interval r
404
upTo i =
1✔
405
  case Interval.lowerBound' i of
1✔
406
    (NegInf, _) -> Interval.empty
1✔
407
    (PosInf, _) -> Interval.whole
×
408
    (Finite lb, incl) ->
409
      Interval.interval (NegInf, Open) (Finite lb, notB incl)
×
410

411
downTo :: Ord r => Interval r -> Interval r
412
downTo i =
1✔
413
  case Interval.upperBound' i of
1✔
414
    (PosInf, _) -> Interval.empty
1✔
415
    (NegInf, _) -> Interval.whole
×
416
    (Finite ub, incl) ->
417
      Interval.interval (Finite ub, notB incl) (PosInf, Open)
×
418

419
notB :: Boundary -> Boundary
420
notB = \case
1✔
421
  Open   -> Closed
1✔
422
  Closed -> Open
1✔
423

424
------------------------------------------------------------------------------
425

426
foldr :: Ord r => (Interval r -> b -> b) -> b -> IntervalSet r -> b
427
foldr f z (IntervalSet m) = Map.foldr f z m
1✔
428

429
foldl' :: Ord r => (a -> Interval r -> a) -> a -> IntervalSet r -> a
NEW
430
foldl' f z (IntervalSet m) = Map.foldl' f z m
×
431

432
foldMap :: (Ord r, Monoid m) => (Interval r -> m) -> IntervalSet r -> m
NEW
433
foldMap f (IntervalSet m) = Map.foldr (\i acc -> f i `mappend` acc) mempty m
×
434

435
---------------------------------- Map.Map -----------------------------------
436

437
-- internal helper for restrictMapKeysToIntervalSet and withoutMapKeysFromIntervalSet
438
restrictMapKeysToIntervalSetFold :: Ord k => Map k a -> IntervalSet k -> Map k a
439
restrictMapKeysToIntervalSetFold m is = foldr f Map.empty is
1✔
440
  where f i acc = Map.union acc (Interval.restrictMapKeysToInterval m i)
1✔
441
{-# INLINE restrictMapKeysToIntervalSetFold #-}
442

443
-- the ratio size of the map / size of the interval set when
444
-- restrictMapKeysToIntervalSetFold and Map.filterWithKey have similar performance.
445
foldFilterMapRatio :: Int
446
foldFilterMapRatio = 4
1✔
447

448
-- | Restrict a 'Map' to the keys contained in a given 'Interval'.
449
--
450
-- >>> restrictMapKeysToIntervalSet m i == filterKeys (\k -> IntervalSet.member k i) m
451
--
452
-- [Usage:]
453
--
454
-- >>> m = Map.fromList [(-2.5,0),(3.1,1),(5,2), (8.5,3)] :: Map Rational Int
455
-- >>> restrictMapKeysToIntervalSet m (IntervalSet.fromList [ -inf <..<= 3, 8 <..< inf])
456
-- fromList [((-5) % 2,0),(17 % 2,3)]
457
--
458
-- [Performance:] \(O(\min (n \log i, i + m \log m)\), with \(n\), the size of
459
-- the input map, \(m\) the size of the output map, and \(i\) the number of
460
-- intervals in the IntervalSet.
461
--
462
--  This will always have better or similar performance than 'Map.filterKeys'.
463
--
464
restrictMapKeysToIntervalSet :: Ord k => Map k a -> IntervalSet k -> Map k a
465
restrictMapKeysToIntervalSet m is@(IntervalSet mi) =
1✔
466
  if foldFilterMapRatio * Map.size mi <= Map.size m then
1✔
467
    restrictMapKeysToIntervalSetFold m is
1✔
468
  else
469
    -- faster when the interval set is large compared to the map
470
    Map.filterWithKey (\k _ -> member k is) m
1✔
471
{-# INLINE restrictMapKeysToIntervalSet #-}
472

473
------------------------------  Complexity proof -------------------------------
474
--
475
-- The performance of restrictMapKeysToIntervalSet is O(i + m log m) with m the size
476
-- of the output map and i the number of intervals in the IntervalSet.
477
--
478
-- Since we swich to Map.filterWithKey when the size of the interval set is
479
-- larger then the map, we get the best of both worlds.
480
--
481
----- Proof:
482
--
483
-- Map.union is O(a log(b/a + 1)) where a <= b, and we know that all intevals
484
-- within the IntervalSet are disjoint.
485
--
486
-- At the kth step of the fold, when we add the new interval restriction result
487
-- to the accumulator, there are two cases:
488
--
489
-- 1. The interval restriction is empty, in which case the cost of the union is
490
--    O(1).
491
-- 2. The interval restriction has m_k keys, and the accumulator has a_k keys, in which case,
492
--    * if a_k < m_k, the cost of the Map.union is O(a_k log(m_k/a_k + 1))
493
--      But log(m_k/a_k + 1) <= m_k log(m + 1) because m_k <= m and a_k < m_k,
494
--      so the complexity is O(m_k log(m + 1))
495
--    * if m_k <= a_k, the cost of the Map.union is O(m_k log(a_k/m_k + 1))
496
--      But this means that m_k log(a_k/m_k + 1) <= m_k log(m + 1), because a_k
497
--      <= m.
498
--
499
-- So the cost of the steps for which m_k >= 1 (non-empty interval) is
500
--
501
-- O( Σ m_k log(m + 1) )
502
--
503
-- but m = Σ m_k, so the overall complexity is O( m log(m + 1) ) = O(m log m).
504
--
505
-- The number of steps for which m_k = 0 is at most i, the number of intervals
506
-- in the interval set.
507
--
508
-- So the overall complexity is O(i + m log m).
509
--
510
-- If the bound of Map.union is tight, then this bound is also tight. Indeed
511
-- consider the case where at each step, m_k <= 1, with i far bigger than m.
512
--
513
-- Then the cost of the non empty steps is
514
--
515
-- Θ( Σ log(k + 1) ) = Θ(m log m)
516
--
517
-- (see Stirling's approximation)
518
--
519
--------------------------------------------------------------------------------
520
-- Discussion:
521
--
522
-- In most cases, this function performs better than 'filterKeys'. There are three cases to consider
523
--
524
--  1. If the 'IntervalSet' covers only a small portion of the map (m is small
525
--     compared to n), then this function is technically logarithmic in n, while
526
--     'filterKeys' is linear in n.
527
--
528
--  2. If the 'IntervalSet' consists of few large intervals, then this function
529
--     performs a lot better than 'filterKeys'.
530
--
531
-- 3. If the 'IntervalSet' consists of many small intervals and covers a
532
--    significant portion of the map, 'filterKeys' outperforms
533
--    'restrictMapKeysToIntervalSet'.
534
--
535
-- Benchmark suggests that the break-even point is when the size of the
536
-- intervalSet is 1/4 the size of the map, which is why we switch
537
-- implementations when this happens.
538
--
539
--------------------------------------------------------------------------------
540

541

542
-- | Delete keys contained in a given 'IntervalSet' from a 'Map'.
543
--
544
-- >>> withoutMapKeysFromIntervalSet i m == filterKeys (\k -> not (IntervalSet.member k i)) m
545
--
546
-- [Usage:]
547
--
548
-- >>> m = Map.fromList [(-2.5,0),(3.1,1),(5,2), (8.5,3)] :: Map Rational Int
549
-- >>> withoutMapKeysFromIntervalSet (IntervalSet.fromList [ -inf <..<= 3, 8 <..< inf]) m
550
-- fromList [(31 % 10,1),(5 % 1,2)]
551
--
552
-- See performance note for 'restrictMapKeysToIntervalSet'.
553
withoutMapKeysFromIntervalSet :: Ord k => IntervalSet k -> Map k a -> Map k a
554
withoutMapKeysFromIntervalSet is@(IntervalSet mi) m =
1✔
555
  if foldFilterMapRatio * Map.size mi <= Map.size m then
1✔
556
    restrictMapKeysToIntervalSet m $ complement is
1✔
557
    -- This is faster in most cases than folding withoutKeysFromInterval. See
558
    -- benchmark.
559
  else
560
    Map.filterWithKey (\k _ -> notMember k is) m
1✔
561
{-# INLINE withoutMapKeysFromIntervalSet #-}
562

563
---------------------------------- Set.Set -----------------------------------
564

565
-- internal helper for intersectionSetAndIntervalSet and differenceSetAndIntervalSet
566
intersectionSetAndIntervalSetFold :: Ord k => Set k -> IntervalSet k -> Set k
567
intersectionSetAndIntervalSetFold s is = foldr f Set.empty is
1✔
568
  where f i acc = Set.union acc (Interval.intersectionSetAndInterval s i)
1✔
569
{-# INLINE intersectionSetAndIntervalSetFold #-}
570

571
-- the ratio size of the set / size of the interval set when
572
-- intersectionSetAndIntervalSetFold and Map.filterWithKey have similar performance.
573
foldFilterSetRatio :: Int
574
foldFilterSetRatio = 4
1✔
575

576
-- | Restrict a 'Set' to the keys contained in a given 'Interval'.
577
--
578
-- >>> intersectionSetAndIntervalSet s i == filter (\k -> IntervalSet.member k i) s
579
--
580
-- [Usage:]
581
--
582
-- >>> s = Set.fromList [-2.5, 3.1, 5 , 8.5] :: Set Rational
583
-- >>> intersectionSetAndIntervalSet s (IntervalSet.fromList [ -inf <..<= 3, 8 <..< inf])
584
-- fromList [(-5) % 2,17 % 2]
585
--
586
-- [Performance:] \(O(\min (n \log i, i + m \log m)\), with \(n\), the size of
587
-- the input set, \(m\) the size of the output set, and \(i\) the number of
588
-- intervals in the IntervalSet.
589
--
590
--  This will always have better or similar performance than 'Set.filter'.
591
intersectionSetAndIntervalSet :: Ord k => Set k -> IntervalSet k -> Set k
592
intersectionSetAndIntervalSet s is@(IntervalSet mi) =
1✔
593
  if foldFilterSetRatio * Map.size mi <= Set.size s then
1✔
594
    intersectionSetAndIntervalSetFold s is
1✔
595
  else
596
    Set.filter (\k -> member k is) s
1✔
597
{-# INLINE intersectionSetAndIntervalSet #-}
598

599
-- | Delete keys contained in a given 'Interval' from a 'Set'.
600
--
601
-- >>> differenceSetAndIntervalSet i s == filter (\k -> not (Interval.member k i)) s
602
--
603
-- [Usage:]
604
--
605
-- >>> s = Set.fromList [-2.5, 3.1, 5 , 8.5] :: Set Rational
606
-- >>> differenceSetAndIntervalSet (3 <=..< 8.5) s
607
-- fromList [(-5) % 2,17 % 2]
608
--
609
-- See performance note for 'intersectionSetAndIntervalSet'.
610
differenceSetAndIntervalSet :: Ord k => Set k -> IntervalSet k ->  Set k
611
differenceSetAndIntervalSet s is@(IntervalSet mi) =
1✔
612
  if foldFilterSetRatio * Map.size mi <= Set.size s then
1✔
613
    intersectionSetAndIntervalSetFold s (complement is)
1✔
614
  else
615
    Set.filter (\k -> notMember k is) s
1✔
616
{-# INLINE differenceSetAndIntervalSet #-}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc