• 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

88.45
/src/Data/Interval.hs
1
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
2
{-# LANGUAGE CPP, LambdaCase, ScopedTypeVariables #-}
3
{-# LANGUAGE Safe #-}
4
{-# LANGUAGE RoleAnnotations #-}
5
-----------------------------------------------------------------------------
6
-- |
7
-- Module      :  Data.Interval
8
-- Copyright   :  (c) Masahiro Sakai 2011-2013, Andrew Lelechenko 2020
9
-- License     :  BSD-style
10
--
11
-- Maintainer  :  masahiro.sakai@gmail.com
12
-- Stability   :  provisional
13
--
14
-- Interval datatype and interval arithmetic.
15
--
16
-- Unlike the intervals package (<http://hackage.haskell.org/package/intervals>),
17
-- this module provides both open and closed intervals and is intended to be used
18
-- with 'Rational'.
19
--
20
-- For the purpose of abstract interpretation, it might be convenient to use
21
-- 'Lattice' instance. See also lattices package
22
-- (<http://hackage.haskell.org/package/lattices>).
23
--
24
-----------------------------------------------------------------------------
25
module Data.Interval
26
  (
27
  -- * Interval type
28
    Interval
29
  , module Data.ExtendedReal
30
  , Boundary(..)
31

32
  -- * Construction
33
  , interval
34
  , (<=..<=)
35
  , (<..<=)
36
  , (<=..<)
37
  , (<..<)
38
  , whole
39
  , empty
40
  , singleton
41

42
  -- * Query
43
  , null
44
  , isSingleton
45
  , extractSingleton
46
  , member
47
  , notMember
48
  , isSubsetOf
49
  , isProperSubsetOf
50
  , isConnected
51
  , lowerBound
52
  , upperBound
53
  , lowerBound'
54
  , upperBound'
55
  , width
56

57
  -- * Universal comparison operators
58
  , (<!), (<=!), (==!), (>=!), (>!), (/=!)
59

60
  -- * Existential comparison operators
61
  , (<?), (<=?), (==?), (>=?), (>?), (/=?)
62

63
  -- * Existential comparison operators that produce witnesses (experimental)
64
  , (<??), (<=??), (==??), (>=??), (>??), (/=??)
65

66
  -- * Combine
67
  , intersection
68
  , intersections
69
  , hull
70
  , hulls
71

72
  -- * Map
73
  , mapMonotonic
74

75
  -- * Operations
76
  , pickup
77
  , simplestRationalWithin
78

79
  -- * Intervals relation
80
  , relate
81

82
  -- * Operations on the keys of 'Data.Map'
83
  , restrictMapKeysToInterval
84
  , withoutMapKeysFromInterval
85

86
  -- * Operations on 'Data.Set'
87
  , intersectionSetAndInterval
88
  , differenceSetAndInterval
89
  ) where
90

91
#ifdef MIN_VERSION_lattices
92
import Algebra.Lattice
93
#endif
94
import Control.Exception (assert)
95
import Control.Monad hiding (join)
96
import Data.ExtendedReal
97
import Data.Foldable hiding (null)
98
import Data.Interval.Internal
99
import Data.IntervalRelation
100
import Data.Maybe
101
import Data.Monoid
102
import Data.Ratio
103
import Prelude hiding (Foldable(..))
104

105
infix 5 <=..<=
106
infix 5 <..<=
107
infix 5 <=..<
108
infix 5 <..<
109
infix 4 <!
110
infix 4 <=!
111
infix 4 ==!
112
infix 4 >=!
113
infix 4 >!
114
infix 4 /=!
115
infix 4 <?
116
infix 4 <=?
117
infix 4 ==?
118
infix 4 >=?
119
infix 4 >?
120
infix 4 /=?
121
infix 4 <??
122
infix 4 <=??
123
infix 4 ==??
124
infix 4 >=??
125
infix 4 >??
126
infix 4 /=??
127

128
#ifdef MIN_VERSION_lattices
129
instance (Ord r) => Lattice (Interval r) where
130
  (\/) = hull
1✔
131
  (/\) = intersection
1✔
132

133
instance (Ord r) => BoundedJoinSemiLattice (Interval r) where
134
  bottom = empty
1✔
135

136
instance (Ord r) => BoundedMeetSemiLattice (Interval r) where
137
  top = whole
1✔
138
#endif
139

140
instance (Ord r, Show r) => Show (Interval r) where
141
  showsPrec _ x | null x = showString "empty"
1✔
142
  showsPrec p i =
143
    showParen (p > rangeOpPrec) $
1✔
144
      showsPrec (rangeOpPrec+1) lb .
1✔
145
      showChar ' ' . showString op . showChar ' ' .
1✔
146
      showsPrec (rangeOpPrec+1) ub
1✔
147
    where
148
      (lb, in1) = lowerBound' i
1✔
149
      (ub, in2) = upperBound' i
1✔
150
      op = sign in1 ++ ".." ++ sign in2
1✔
151
      sign = \case
1✔
152
        Open   -> "<"
1✔
153
        Closed -> "<="
1✔
154

155
instance (Ord r, Read r) => Read (Interval r) where
156
  readsPrec p r =
1✔
157
    (readParen (p > appPrec) $ \s0 -> do
1✔
158
      ("interval",s1) <- lex s0
1✔
159
      (lb,s2) <- readsPrec (appPrec+1) s1
×
160
      (ub,s3) <- readsPrec (appPrec+1) s2
×
161
      return (interval lb ub, s3)) r
1✔
162
    ++
163
    (readParen (p > rangeOpPrec) $ \s0 -> do
1✔
164
      (do (l,s1) <- readsPrec (rangeOpPrec+1) s0
1✔
165
          (op',s2) <- lex s1
1✔
166
          op <-
167
            case op' of
1✔
168
              "<=..<=" -> return (<=..<=)
1✔
169
              "<..<="  -> return (<..<=)
1✔
170
              "<=..<"  -> return (<=..<)
1✔
171
              "<..<"   -> return (<..<)
1✔
172
              _ -> []
×
173
          (u,s3) <- readsPrec (rangeOpPrec+1) s2
1✔
174
          return (op l u, s3))) r
1✔
175
    ++
176
    (do ("empty", s) <- lex r
1✔
177
        return (empty, s))
1✔
178

179
-- | Lower endpoint (/i.e./ greatest lower bound)  of the interval.
180
--
181
-- * 'lowerBound' of the empty interval is 'PosInf'.
182
--
183
-- * 'lowerBound' of a left unbounded interval is 'NegInf'.
184
--
185
-- * 'lowerBound' of an interval may or may not be a member of the interval.
186
lowerBound :: Interval r -> Extended r
187
lowerBound = fst . lowerBound'
1✔
188

189
-- | Upper endpoint (/i.e./ least upper bound) of the interval.
190
--
191
-- * 'upperBound' of the empty interval is 'NegInf'.
192
--
193
-- * 'upperBound' of a right unbounded interval is 'PosInf'.
194
--
195
-- * 'upperBound' of an interval may or may not be a member of the interval.
196
upperBound :: Interval r -> Extended r
197
upperBound = fst . upperBound'
1✔
198

199
-- | closed interval [@l@,@u@]
200
(<=..<=)
201
  :: (Ord r)
202
  => Extended r -- ^ lower bound @l@
203
  -> Extended r -- ^ upper bound @u@
204
  -> Interval r
205
(<=..<=) lb ub = interval (lb, Closed) (ub, Closed)
1✔
206

207
-- | left-open right-closed interval (@l@,@u@]
208
(<..<=)
209
  :: (Ord r)
210
  => Extended r -- ^ lower bound @l@
211
  -> Extended r -- ^ upper bound @u@
212
  -> Interval r
213
(<..<=) lb ub = interval (lb, Open) (ub, Closed)
1✔
214

215
-- | left-closed right-open interval [@l@, @u@)
216
(<=..<)
217
  :: (Ord r)
218
  => Extended r -- ^ lower bound @l@
219
  -> Extended r -- ^ upper bound @u@
220
  -> Interval r
221
(<=..<) lb ub = interval (lb, Closed) (ub, Open)
1✔
222

223
-- | open interval (@l@, @u@)
224
(<..<)
225
  :: (Ord r)
226
  => Extended r -- ^ lower bound @l@
227
  -> Extended r -- ^ upper bound @u@
228
  -> Interval r
229
(<..<) lb ub = interval (lb, Open) (ub, Open)
1✔
230

231
-- | whole real number line (-∞, ∞)
232
whole :: Ord r => Interval r
233
whole = interval (NegInf, Open) (PosInf, Open)
×
234

235
-- | singleton set [x,x]
236
singleton :: Ord r => r -> Interval r
237
singleton x = interval (Finite x, Closed) (Finite x, Closed)
1✔
238

239
-- | intersection of two intervals
240
intersection :: forall r. Ord r => Interval r -> Interval r -> Interval r
241
intersection i1 i2 = interval
1✔
242
  (maxLB (lowerBound' i1) (lowerBound' i2))
1✔
243
  (minUB (upperBound' i1) (upperBound' i2))
1✔
244
  where
245
    maxLB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
246
    maxLB (x1,in1) (x2,in2) =
1✔
247
      ( max x1 x2
1✔
248
      , case x1 `compare` x2 of
1✔
249
          EQ -> in1 `min` in2
1✔
250
          LT -> in2
1✔
251
          GT -> in1
1✔
252
      )
253
    minUB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
254
    minUB (x1,in1) (x2,in2) =
1✔
255
      ( min x1 x2
1✔
256
      , case x1 `compare` x2 of
1✔
257
          EQ -> in1 `min` in2
1✔
258
          LT -> in1
1✔
259
          GT -> in2
1✔
260
      )
261

262
-- | intersection of a list of intervals.
263
--
264
-- @since 0.6.0
265
intersections :: Ord r => [Interval r] -> Interval r
266
intersections = foldl' intersection whole
1✔
267

268
-- | convex hull of two intervals
269
hull :: forall r. Ord r => Interval r -> Interval r -> Interval r
270
hull x1 x2
1✔
271
  | null x1 = x2
1✔
272
  | null x2 = x1
1✔
273
hull i1 i2 = interval
1✔
274
  (minLB (lowerBound' i1) (lowerBound' i2))
1✔
275
  (maxUB (upperBound' i1) (upperBound' i2))
1✔
276
  where
277
    maxUB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
278
    maxUB (x1,in1) (x2,in2) =
1✔
279
      ( max x1 x2
1✔
280
      , case x1 `compare` x2 of
1✔
281
          EQ -> in1 `max` in2
1✔
282
          LT -> in2
1✔
283
          GT -> in1
1✔
284
      )
285
    minLB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
286
    minLB (x1,in1) (x2,in2) =
1✔
287
      ( min x1 x2
1✔
288
      , case x1 `compare` x2 of
1✔
289
          EQ -> in1 `max` in2
1✔
290
          LT -> in1
1✔
291
          GT -> in2
1✔
292
      )
293

294
-- | convex hull of a list of intervals.
295
--
296
-- @since 0.6.0
297
hulls :: Ord r => [Interval r] -> Interval r
298
hulls = foldl' hull empty
1✔
299

300
-- | Is the interval empty?
301
null :: Ord r => Interval r -> Bool
302
null i =
1✔
303
  case x1 `compare` x2 of
1✔
304
    EQ -> assert (in1 == Closed && in2 == Closed) False
×
305
    LT -> False
1✔
306
    GT -> True
1✔
307
  where
308
    (x1, in1) = lowerBound' i
1✔
309
    (x2, in2) = upperBound' i
1✔
310

311
-- | Is the interval single point?
312
--
313
-- @since 2.0.0
314
isSingleton :: Ord r => Interval r -> Bool
315
isSingleton = isJust . extractSingleton
1✔
316

317
-- | If the interval is a single point, return this point.
318
--
319
-- @since 2.1.0
320
extractSingleton :: Ord r => Interval r -> Maybe r
321
extractSingleton i = case (lowerBound' i, upperBound' i) of
1✔
322
  ((Finite l, Closed), (Finite u, Closed))
323
    | l == u -> Just l
×
324
  _ -> Nothing
1✔
325

326
-- | Is the element in the interval?
327
member :: Ord r => r -> Interval r -> Bool
328
member x i = condLB && condUB
1✔
329
  where
330
    (x1, in1) = lowerBound' i
1✔
331
    (x2, in2) = upperBound' i
1✔
332
    condLB = case in1 of
1✔
333
      Open   -> x1 <  Finite x
1✔
334
      Closed -> x1 <= Finite x
1✔
335
    condUB = case in2 of
1✔
336
      Open   -> Finite x <  x2
1✔
337
      Closed -> Finite x <= x2
1✔
338

339
-- | Is the element not in the interval?
340
notMember :: Ord r => r -> Interval r -> Bool
341
notMember a i = not $ member a i
1✔
342

343
-- | Is this a subset?
344
-- @(i1 \``isSubsetOf`\` i2)@ tells whether @i1@ is a subset of @i2@.
345
isSubsetOf :: Ord r => Interval r -> Interval r -> Bool
346
isSubsetOf i1 i2 = testLB (lowerBound' i1) (lowerBound' i2) && testUB (upperBound' i1) (upperBound' i2)
1✔
347
  where
348
    testLB (x1,in1) (x2,in2) =
1✔
349
      case x1 `compare` x2 of
1✔
350
        GT -> True
1✔
351
        LT -> False
1✔
352
        EQ -> in1 <= in2
1✔
353
    testUB (x1,in1) (x2,in2) =
1✔
354
      case x1 `compare` x2 of
1✔
355
        LT -> True
1✔
356
        GT -> False
1✔
357
        EQ -> in1 <= in2
1✔
358

359
-- | Is this a proper subset? (/i.e./ a subset but not equal).
360
isProperSubsetOf :: Ord r => Interval r -> Interval r -> Bool
361
isProperSubsetOf i1 i2 = i1 /= i2 && i1 `isSubsetOf` i2
1✔
362

363
-- | Does the union of two range form a connected set?
364
--
365
-- @since 1.3.0
366
isConnected :: Ord r => Interval r -> Interval r -> Bool
367
isConnected x y
1✔
368
  | null x = True
1✔
369
  | null y = True
1✔
370
  | otherwise = x ==? y || (lb1==ub2 && (lb1in == Closed || ub2in == Closed)) || (ub1==lb2 && (ub1in == Closed || lb2in == Closed))
1✔
371
  where
372
    (lb1,lb1in) = lowerBound' x
1✔
373
    (lb2,lb2in) = lowerBound' y
1✔
374
    (ub1,ub1in) = upperBound' x
1✔
375
    (ub2,ub2in) = upperBound' y
1✔
376

377
-- | Width of a interval. Width of an unbounded interval is @undefined@.
378
width :: (Num r, Ord r) => Interval r -> r
379
width x
1✔
380
  | null x = 0
1✔
381
  | otherwise = case (fst (lowerBound' x), fst (upperBound' x)) of
1✔
382
    (Finite l, Finite u) -> u - l
1✔
383
    _ -> error "Data.Interval.width: unbounded interval"
×
384

385
-- | pick up an element from the interval if the interval is not empty.
386
pickup :: (Real r, Fractional r) => Interval r -> Maybe r
387
pickup i = case (lowerBound' i, upperBound' i) of
1✔
388
  ((NegInf,_), (PosInf,_))             -> Just 0
1✔
389
  ((Finite x1, in1), (PosInf,_))       -> Just $ case in1 of
1✔
390
    Open   -> x1 + 1
1✔
391
    Closed -> x1
1✔
392
  ((NegInf,_), (Finite x2, in2))       -> Just $ case in2 of
1✔
393
    Open   -> x2 - 1
1✔
394
    Closed -> x2
1✔
395
  ((Finite x1, in1), (Finite x2, in2)) ->
396
    case x1 `compare` x2 of
1✔
397
      GT -> Nothing
×
398
      LT -> Just $ (x1+x2) / 2
1✔
399
      EQ -> if in1 == Closed && in2 == Closed then Just x1 else Nothing
×
400
  _ -> Nothing
1✔
401

402
-- | 'simplestRationalWithin' returns the simplest rational number within the interval.
403
--
404
-- A rational number @y@ is said to be /simpler/ than another @y'@ if
405
--
406
-- * @'abs' ('numerator' y) <= 'abs' ('numerator' y')@, and
407
--
408
-- * @'denominator' y <= 'denominator' y'@.
409
--
410
-- (see also 'approxRational')
411
--
412
-- @since 0.4.0
413
simplestRationalWithin :: RealFrac r => Interval r -> Maybe Rational
414
simplestRationalWithin i | null i = Nothing
1✔
415
simplestRationalWithin i
416
  | 0 <! i    = Just $ go i
1✔
417
  | i <! 0    = Just $ - go (- i)
1✔
418
  | otherwise = assert (0 `member` i) $ Just 0
×
419
  where
420
    go j
1✔
421
      | fromInteger lb_floor       `member` j = fromInteger lb_floor
1✔
422
      | fromInteger (lb_floor + 1) `member` j = fromInteger (lb_floor + 1)
1✔
423
      | otherwise = fromInteger lb_floor + recip (go (recip (j - singleton (fromInteger lb_floor))))
1✔
424
      where
425
        Finite lb = lowerBound j
1✔
426
        lb_floor  = floor lb
1✔
427

428
-- | @mapMonotonic f i@ is the image of @i@ under @f@, where @f@ must be a strict monotone function,
429
-- preserving negative and positive infinities.
430
mapMonotonic :: (Ord a, Ord b) => (a -> b) -> Interval a -> Interval b
431
mapMonotonic f i = interval (fmap f lb, in1) (fmap f ub, in2)
1✔
432
  where
433
    (lb, in1) = lowerBound' i
1✔
434
    (ub, in2) = upperBound' i
1✔
435

436
mapAntiMonotonic :: (Ord a, Ord b) => (a -> b) -> Interval a -> Interval b
437
mapAntiMonotonic f i
1✔
438
  | null i = empty
1✔
439
  | otherwise = interval (fmap f ub, in2) (fmap f lb, in1)
1✔
440
  where
441
    (lb, in1) = lowerBound' i
1✔
442
    (ub, in2) = upperBound' i
1✔
443

444
-- | For all @x@ in @X@, @y@ in @Y@. @x '<' y@?
445
(<!) :: Ord r => Interval r -> Interval r -> Bool
446
a <! b =
1✔
447
  case ub_a `compare` lb_b of
1✔
448
    LT -> True
1✔
449
    GT -> False
1✔
450
    EQ ->
451
      case ub_a of
1✔
452
        NegInf   -> True -- a is empty, so it holds vacuously
1✔
453
        PosInf   -> True -- b is empty, so it holds vacuously
1✔
454
        Finite _ -> in1 == Open || in2 == Open
1✔
455
  where
456
    (ub_a, in1) = upperBound' a
1✔
457
    (lb_b, in2) = lowerBound' b
1✔
458

459
-- | For all @x@ in @X@, @y@ in @Y@. @x '<=' y@?
460
(<=!) :: Ord r => Interval r -> Interval r -> Bool
461
a <=! b = upperBound a <= lowerBound b
1✔
462

463
-- | For all @x@ in @X@, @y@ in @Y@. @x '==' y@?
464
(==!) :: Ord r => Interval r -> Interval r -> Bool
465
a ==! b = a <=! b && a >=! b
1✔
466

467
-- | For all @x@ in @X@, @y@ in @Y@. @x '/=' y@?
468
--
469
-- @since 1.0.1
470
(/=!) :: Ord r => Interval r -> Interval r -> Bool
471
a /=! b = null $ a `intersection` b
1✔
472

473
-- | For all @x@ in @X@, @y@ in @Y@. @x '>=' y@?
474
(>=!) :: Ord r => Interval r -> Interval r -> Bool
475
(>=!) = flip (<=!)
1✔
476

477
-- | For all @x@ in @X@, @y@ in @Y@. @x '>' y@?
478
(>!) :: Ord r => Interval r -> Interval r -> Bool
479
(>!) = flip (<!)
1✔
480

481
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<' y@?
482
(<?) :: Ord r => Interval r -> Interval r -> Bool
483
a <? b = lb_a < ub_b
1✔
484
  where
485
    lb_a = lowerBound a
1✔
486
    ub_b = upperBound b
1✔
487

488
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<' y@?
489
--
490
-- @since 1.0.0
491
(<??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
492
a <?? b = do
1✔
493
  guard $ lowerBound a < upperBound b
1✔
494
  let c = intersection a b
1✔
495
  case pickup c of
1✔
496
    Nothing -> do
1✔
497
      x <- pickup a
1✔
498
      y <- pickup b
1✔
499
      return (x,y)
1✔
500
    Just z -> do
1✔
501
      let x:y:_ = take 2 $
1✔
502
                    maybeToList (pickup (intersection a (-inf <..< Finite z))) ++
1✔
503
                    [z] ++
1✔
504
                    maybeToList (pickup (intersection b (Finite z <..< inf)))
1✔
505
      return (x,y)
1✔
506

507
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<=' y@?
508
(<=?) :: Ord r => Interval r -> Interval r -> Bool
509
a <=? b =
1✔
510
  case lb_a `compare` ub_b of
1✔
511
    LT -> True
1✔
512
    GT -> False
1✔
513
    EQ ->
514
      case lb_a of
1✔
515
        NegInf -> False -- b is empty
1✔
516
        PosInf -> False -- a is empty
1✔
517
        Finite _ -> in1 == Closed && in2 == Closed
1✔
518
  where
519
    (lb_a, in1) = lowerBound' a
1✔
520
    (ub_b, in2) = upperBound' b
1✔
521

522
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<=' y@?
523
--
524
-- @since 1.0.0
525
(<=??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
526
a <=?? b =
1✔
527
  case pickup (intersection a b) of
1✔
528
    Just x -> return (x,x)
1✔
529
    Nothing -> do
1✔
530
      guard $ upperBound a <= lowerBound b
1✔
531
      x <- pickup a
1✔
532
      y <- pickup b
1✔
533
      return (x,y)
1✔
534

535
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '==' y@?
536
--
537
-- @since 1.0.0
538
(==?) :: Ord r => Interval r -> Interval r -> Bool
539
a ==? b = not $ null $ intersection a b
1✔
540

541
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '==' y@?
542
--
543
-- @since 1.0.0
544
(==??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
545
a ==?? b = do
1✔
546
  x <- pickup (intersection a b)
1✔
547
  return (x,x)
1✔
548

549
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '/=' y@?
550
--
551
-- @since 1.0.1
552
(/=?) :: Ord r => Interval r -> Interval r -> Bool
553
a /=? b = not (null a) && not (null b) && not (a == b && isSingleton a)
1✔
554

555
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '/=' y@?
556
--
557
-- @since 1.0.1
558
(/=??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
559
a /=?? b = do
1✔
560
  guard $ not $ null a
1✔
561
  guard $ not $ null b
1✔
562
  guard $ not $ a == b && isSingleton a
×
563
  if not (isSingleton b)
1✔
564
    then f a b
1✔
565
    else liftM (\(y,x) -> (x,y)) $ f b a
1✔
566
  where
567
    f i j = do
1✔
568
      x <- pickup i
1✔
569
      y <- msum [pickup (j `intersection` c) | c <- [-inf <..< Finite x, Finite x <..< inf]]
1✔
570
      return (x,y)
1✔
571

572
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>=' y@?
573
(>=?) :: Ord r => Interval r -> Interval r -> Bool
574
(>=?) = flip (<=?)
1✔
575

576
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>' y@?
577
(>?) :: Ord r => Interval r -> Interval r -> Bool
578
(>?) = flip (<?)
1✔
579

580
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>=' y@?
581
--
582
-- @since 1.0.0
583
(>=??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
584
(>=??) = flip (<=??)
×
585

586
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>' y@?
587
--
588
-- @since 1.0.0
589
(>??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
590
(>??) = flip (<??)
×
591

592
appPrec :: Int
593
appPrec = 10
1✔
594

595
rangeOpPrec :: Int
596
rangeOpPrec = 5
1✔
597

598
scaleInterval :: (Num r, Ord r) => r -> Interval r -> Interval r
599
scaleInterval c x
1✔
600
  | null x = empty
1✔
601
  | otherwise = case compare c 0 of
1✔
602
    EQ -> singleton 0
×
603
    LT -> interval (scaleInf' c ub) (scaleInf' c lb)
1✔
604
    GT -> interval (scaleInf' c lb) (scaleInf' c ub)
×
605
  where
606
    lb = lowerBound' x
1✔
607
    ub = upperBound' x
1✔
608

609
-- | When results of 'abs' or 'signum' do not form a connected interval,
610
-- a convex hull is returned instead.
611
instance (Num r, Ord r) => Num (Interval r) where
612
  a + b
1✔
613
    | null a || null b = empty
1✔
614
    | otherwise = interval (f (lowerBound' a) (lowerBound' b)) (g (upperBound' a) (upperBound' b))
1✔
615
    where
616
      f (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 `min` in2)
1✔
617
      f (NegInf,_) _ = (-inf, Open)
×
618
      f _ (NegInf,_) = (-inf, Open)
×
619
      f _ _ = error "Interval.(+) should not happen"
×
620

621
      g (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 `min` in2)
1✔
622
      g (PosInf,_) _ = (inf, Open)
×
623
      g _ (PosInf,_) = (inf, Open)
×
624
      g _ _ = error "Interval.(+) should not happen"
×
625

626
  negate = scaleInterval (-1)
1✔
627

628
  fromInteger i = singleton (fromInteger i)
1✔
629

630
  abs x = (x `intersection` nonneg) `hull` (negate x `intersection` nonneg)
1✔
631
    where
632
      nonneg = 0 <=..< inf
1✔
633

634
  signum x = zero `hull` pos `hull` neg
1✔
635
    where
636
      zero = if member 0 x then singleton 0 else empty
1✔
637
      pos = if null $ (0 <..< inf) `intersection` x
1✔
638
            then empty
1✔
639
            else singleton 1
1✔
640
      neg = if null $ (-inf <..< 0) `intersection` x
1✔
641
            then empty
1✔
642
            else singleton (-1)
1✔
643

644
  a * b
1✔
645
    | null a || null b = empty
1✔
646
    | otherwise = interval lb3 ub3
1✔
647
    where
648
      xs = [ mulInf' x1 x2 | x1 <- [lowerBound' a, upperBound' a], x2 <- [lowerBound' b, upperBound' b] ]
1✔
649
      ub3 = maximumBy cmpUB xs
1✔
650
      lb3 = minimumBy cmpLB xs
1✔
651

652
-- | 'recip' returns 'whole' when 0 is an interior point.
653
-- Otherwise @recip (recip xs)@ equals to @xs@ without 0.
654
instance forall r. (Real r, Fractional r) => Fractional (Interval r) where
655
  fromRational r = singleton (fromRational r)
1✔
656
  recip a
1✔
657
    | null a = empty
1✔
658
    | a == 0 = empty
1✔
659
    | 0 `member` a && 0 /= lowerBound a && 0 /= upperBound a = whole
1✔
660
    | otherwise = interval lb3 ub3
1✔
661
    where
662
      ub3 = maximumBy cmpUB xs
1✔
663
      lb3 = minimumBy cmpLB xs
1✔
664
      xs = [recipLB (lowerBound' a), recipUB (upperBound' a)]
1✔
665

666
-- | When results of 'tan' or '**' do not form a connected interval,
667
-- a convex hull is returned instead.
668
instance (RealFrac r, Floating r) => Floating (Interval r) where
669
  pi = singleton pi
1✔
670

671
  exp = intersection (0 <..< PosInf) . mapMonotonic exp
1✔
672
  log a = interval (logB (lowerBound' b)) (logB (upperBound' b))
1✔
673
    where
674
      b = intersection (0 <..< PosInf) a
1✔
675

676
  sqrt = mapMonotonic sqrt . intersection (0 <=..< PosInf)
1✔
677

678
  a ** b = hulls (posBase : negBasePosPower : negBaseNegPower : zeroPower ++ zeroBase)
1✔
679
    where
680
      posBase = exp (log a * b)
1✔
681
      zeroPower = [ 1 | 0 `member` b, not (null a) ]
1✔
682
      zeroBase  = [ 0 | 0 `member` a, not (null (b `intersection` (0 <..< PosInf))) ]
1✔
683
      negBasePosPower = positiveIntegralPowersOfNegativeValues
1✔
684
        (a `intersection` (NegInf <..< 0))
1✔
685
        (b `intersection` (0 <..< PosInf))
1✔
686
      negBaseNegPower = positiveIntegralPowersOfNegativeValues
1✔
687
        (recip  (a `intersection` (NegInf <..< 0)))
1✔
688
        (negate (b `intersection` (NegInf <..< 0)))
1✔
689

690
  cos a = case lowerBound' a of
1✔
691
    (NegInf, _) -> -1 <=..<= 1
1✔
692
    (PosInf, _) -> empty
1✔
693
    (Finite lb, in1) -> case upperBound' a of
1✔
694
      (NegInf, _) -> empty
×
695
      (PosInf, _) -> -1 <=..<= 1
1✔
696
      (Finite ub, in2)
697
        | ub - lb > 2 * pi                                             -> -1 <=..<= 1
1✔
698
        | clb == -1 && ub - lb == 2 * pi && in1 == Open && in2 == Open -> -1 <..<= 1
×
699
        | clb ==  1 && ub - lb == 2 * pi && in1 == Open && in2 == Open -> -1 <=..< 1
×
700
        | ub - lb == 2 * pi                                            -> -1 <=..<= 1
×
701

702
        | lbNorth, ubNorth, clb >= cub -> interval (cub, in2) (clb, in1)
1✔
703
        | lbNorth, ubNorth -> -1 <=..<= 1
1✔
704
        | lbNorth -> interval (-1, Closed) $ case clb `compare` cub of
1✔
705
          LT -> (cub, in2)
1✔
706
          EQ -> (cub, in1 `max` in2)
×
707
          GT -> (clb, in1)
1✔
708
        | ubNorth -> (`interval` (1, Closed)) $ case clb `compare` cub of
1✔
709
          LT -> (clb, in1)
1✔
710
          EQ -> (clb, in1 `max` in2)
×
UNCOV
711
          GT -> (cub, in2)
×
712
        | clb > cub -> -1 <=..<= 1
1✔
713
        | otherwise -> interval (clb, in1) (cub, in2)
1✔
714
        where
715
          mod2pi x = let y = x / (2 * pi) in y - fromInteger (floor y)
1✔
716
          -- is lower bound in the northern half-plane [0,pi)?
717
          lbNorth = (mod2pi lb, in1) < (1 / 2, Closed)
×
718
          -- is upper bound in the northern half-plane [0,pi)?
719
          ubNorth = (mod2pi ub, in2) < (1 / 2, Closed)
1✔
720
          clb = Finite (cos lb)
1✔
721
          cub = Finite (cos ub)
1✔
722

723
  acos = mapAntiMonotonic acos . intersection (-1 <=..<= 1)
1✔
724

725
  sin a = cos (pi / 2 - a)
1✔
726
  asin = mapMonotonic asin . intersection (-1 <=..<= 1)
1✔
727

728
  tan a = case lowerBound' a of
1✔
729
    (NegInf, _) -> whole
1✔
730
    (PosInf, _) -> empty
1✔
731
    (Finite lb, in1) -> case upperBound' a of
1✔
732
      (NegInf, _) -> empty
×
733
      (PosInf, _) -> whole
1✔
734
      (Finite ub, in2)
735
        | ub - lb > pi -> whole
1✔
736
        -- the next case corresponds to (tan lb, +inf) + (-inf, tan ub)
737
        -- with tan lb == tan ub, but a convex hull is returned instead
738
        | ub - lb == pi && in1 == Open && in2 == Open && modpi lb /= 1/2 -> whole
×
739
        | ub - lb == pi -> whole
×
740
        | tan lb <= tan ub -> interval (Finite $ tan lb, in1) (Finite $ tan ub, in2)
1✔
741
        -- the next case corresponds to (tan lb, +inf) + (-inf, tan ub),
742
        -- but a convex hull is returned instead
743
        | otherwise -> whole
1✔
744
        where
745
          modpi x = let y = x / pi in y - fromInteger (floor y)
×
746

747
  atan = intersection (Finite (-pi / 2) <=..<= Finite (pi / 2)) . mapMonotonic atan
1✔
748

749
  sinh  = mapMonotonic sinh
1✔
750
  asinh = mapMonotonic asinh
1✔
751

752
  cosh  = mapMonotonic cosh . abs
1✔
753
  acosh = mapMonotonic acosh . intersection (1 <=..< PosInf)
1✔
754

755
  tanh  = intersection (-1 <..< 1) . mapMonotonic tanh
1✔
756
  atanh a = interval (atanhB (lowerBound' b)) (atanhB (upperBound' b))
1✔
757
    where
758
      b = intersection (-1 <..< 1) a
1✔
759

760
positiveIntegralPowersOfNegativeValues
761
  :: RealFrac r => Interval r -> Interval r -> Interval r
762
positiveIntegralPowersOfNegativeValues a b
1✔
763
  | null a || null b         = empty
1✔
UNCOV
764
  | Just ub <- mub, lb > ub  = empty
×
765
  | Just ub <- mub, lb == ub = a ^ lb
1✔
766
  -- cases below connects two intervals (a ^ k, 0) + (0, a ^ k'))
767
  -- into a single convex hull
768
  | lowerBound a >= -1       = hull (a ^ lb) (a ^ (lb + 1))
1✔
769
  | Just ub <- mub           = hull (a ^ ub) (a ^ (ub - 1))
1✔
770
  | Nothing <- mub           = whole
1✔
771
  where
772
    -- Similar to Data.IntegerInterval.fromIntervalUnder
773
    lb :: Integer
774
    lb = case lowerBound' b of
1✔
775
      (Finite x, Open)
776
        | fromInteger (ceiling x) == x
1✔
777
        -> ceiling x + 1
1✔
778
      (Finite x, _) -> ceiling x
1✔
779
      _ -> 0 -- PosInf is not expected, because b is not null
×
780
    mub :: Maybe Integer
781
    mub = case upperBound' b of
1✔
782
      (Finite x, Open)
783
        | fromInteger (floor x) == x
1✔
784
        -> Just $ floor x - 1
1✔
785
      (Finite x, _) -> Just $ floor x
1✔
786
      _ -> Nothing -- NegInf is not expected, because b is not null
1✔
787

788
cmpUB, cmpLB :: Ord r => (Extended r, Boundary) -> (Extended r, Boundary) -> Ordering
789
cmpUB (x1,in1) (x2,in2) = compare x1 x2 `mappend` compare in1 in2
1✔
790
cmpLB (x1,in1) (x2,in2) = compare x1 x2 `mappend` compare in2 in1
1✔
791

792
scaleInf' :: (Num r, Ord r) => r -> (Extended r, Boundary) -> (Extended r, Boundary)
793
scaleInf' a (x1, in1) = (scaleEndPoint a x1, in1)
1✔
794

795
scaleEndPoint :: (Num r, Ord r) => r -> Extended r -> Extended r
796
scaleEndPoint a e =
1✔
797
  case a `compare` 0 of
1✔
798
    EQ -> 0
×
799
    GT ->
800
      case e of
×
801
        NegInf   -> NegInf
×
802
        Finite b -> Finite (a*b)
×
803
        PosInf   -> PosInf
×
804
    LT ->
805
      case e of
1✔
806
        NegInf   -> PosInf
1✔
807
        Finite b -> Finite (a*b)
1✔
808
        PosInf   -> NegInf
1✔
809

810
mulInf' :: (Num r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
811
mulInf' (0, Closed) _ = (0, Closed)
1✔
812
mulInf' _ (0, Closed) = (0, Closed)
1✔
813
mulInf' (x1,in1) (x2,in2) = (x1*x2, in1 `min` in2)
1✔
814

815
recipLB :: (Fractional r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
816
recipLB (0, _) = (PosInf, Open)
×
817
recipLB (x1, in1) = (recip x1, in1)
1✔
818

819
recipUB :: (Fractional r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
820
recipUB (0, _) = (NegInf, Open)
×
821
recipUB (x1, in1) = (recip x1, in1)
1✔
822

823
logB :: (Floating r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
824
logB (NegInf, in1) = (Finite $ log (log 0), in1)
×
825
logB (Finite 0, _) = (NegInf, Open)
×
826
logB (Finite x1, in1) = (Finite $ log x1, in1)
1✔
827
logB (PosInf, in1) = (PosInf, in1)
×
828

829
atanhB :: (Floating r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
830
atanhB (NegInf, in1) = (Finite $ atanh (-1/0), in1)
1✔
831
atanhB (Finite (-1), _) = (NegInf, Open)
×
832
atanhB (Finite 1, _) = (PosInf, Open)
×
833
atanhB (Finite x1, in1) = (Finite $ atanh x1, in1)
1✔
834
atanhB (PosInf, in1) = (Finite $ atanh (1/0), in1)
1✔
835

836
-- | Computes how two intervals are related according to the @`Data.IntervalRelation.Relation`@ classification
837
relate :: Ord r => Interval r -> Interval r -> Relation
838
relate i1 i2 =
1✔
839
  case (i1 `isSubsetOf` i2, i2 `isSubsetOf` i1) of
1✔
840
    -- 'i1' ad 'i2' are equal
841
    (True , True ) -> Equal
1✔
842
    -- 'i1' is strictly contained in `i2`
843
    (True , False) | compareBound (lowerBound' i1) (lowerBound' i2) == EQ -> Starts
1✔
844
                   | compareBound (upperBound' i1) (upperBound' i2) == EQ -> Finishes
1✔
845
                   | otherwise                                            -> During
1✔
846
    -- 'i2' is strictly contained in `i1`
847
    (False, True ) | compareBound (lowerBound' i1) (lowerBound' i2) == EQ -> StartedBy
1✔
848
                   | compareBound (upperBound' i1) (upperBound' i2) == EQ -> FinishedBy
1✔
849
                   | otherwise                                            -> Contains
1✔
850
    -- neither `i1` nor `i2` is contained in the other
851
    (False, False) -> case ( null (i1 `intersection` i2)
1✔
852
                           , compareBound (upperBound' i1) (upperBound' i2) <= EQ
1✔
853
                           , i1 `isConnected` i2
1✔
854
                           ) of
855
      (True , True , True ) -> JustBefore
1✔
856
      (True , True , False) -> Before
1✔
857
      (True , False, True ) -> JustAfter
1✔
858
      (True , False, False) -> After
1✔
859
      (False, True , _    ) -> Overlaps
1✔
860
      (False, False, _    ) -> OverlappedBy
1✔
861
  where
862
    compareBound :: Ord r => (Extended r, Boundary) -> (Extended r, Boundary) -> Ordering
863
    compareBound (PosInf, _) (PosInf, _) = EQ
1✔
864
    compareBound (PosInf, _) _           = GT
1✔
865
    compareBound (NegInf, _) (NegInf, _) = EQ
1✔
866
    compareBound (NegInf, _) _           = LT
1✔
867
    compareBound a           b           = compare a b
1✔
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