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

msakai / data-interval / 104

03 Feb 2026 10:03PM UTC coverage: 87.261% (+0.2%) from 87.081%
104

push

github

Bodigrim
Add fromUnorderedBounds constructor

30 of 33 new or added lines in 1 file covered. (90.91%)

2 existing lines in 2 files now uncovered.

1096 of 1256 relevant lines covered (87.26%)

0.87 hits per line

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

88.94
/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
  , fromUnorderedBounds
39
  , whole
40
  , empty
41
  , singleton
42

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

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

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

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

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

73
  -- * Map
74
  , mapMonotonic
75

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

80
  -- * Intervals relation
81
  , relate
82

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

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

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

106
infix 5 <=..<=
107
infix 5 <..<=
108
infix 5 <=..<
109
infix 5 <..<
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
infix 4 /=??
128

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

627
  negate = scaleInterval (-1)
1✔
628

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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