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

msakai / data-interval / 80

30 Aug 2025 08:23PM UTC coverage: 86.789% (+0.09%) from 86.702%
80

push

github

Bodigrim
Fix warnings

1 of 4 new or added lines in 4 files covered. (25.0%)

87 existing lines in 4 files now uncovered.

992 of 1143 relevant lines covered (86.79%)

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
  , 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
  ) where
82

83
#ifdef MIN_VERSION_lattices
84
import Algebra.Lattice
85
#endif
86
import Control.Exception (assert)
87
import Control.Monad hiding (join)
88
import Data.ExtendedReal
89
import Data.Foldable hiding (null)
90
import Data.Interval.Internal
91
import Data.IntervalRelation
92
import Data.Maybe
93
import Data.Monoid
94
import Data.Ratio
95
import Prelude hiding (Foldable(..))
96

97
infix 5 <=..<=
98
infix 5 <..<=
99
infix 5 <=..<
100
infix 5 <..<
101
infix 4 <!
102
infix 4 <=!
103
infix 4 ==!
104
infix 4 >=!
105
infix 4 >!
106
infix 4 /=!
107
infix 4 <?
108
infix 4 <=?
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

120
#ifdef MIN_VERSION_lattices
121
instance (Ord r) => Lattice (Interval r) where
122
  (\/) = hull
1✔
123
  (/\) = intersection
1✔
124

125
instance (Ord r) => BoundedJoinSemiLattice (Interval r) where
126
  bottom = empty
1✔
127

128
instance (Ord r) => BoundedMeetSemiLattice (Interval r) where
129
  top = whole
1✔
130
#endif
131

132
instance (Ord r, Show r) => Show (Interval r) where
133
  showsPrec _ x | null x = showString "empty"
1✔
134
  showsPrec p i =
135
    showParen (p > rangeOpPrec) $
1✔
136
      showsPrec (rangeOpPrec+1) lb .
1✔
137
      showChar ' ' . showString op . showChar ' ' .
1✔
138
      showsPrec (rangeOpPrec+1) ub
1✔
139
    where
140
      (lb, in1) = lowerBound' i
1✔
141
      (ub, in2) = upperBound' i
1✔
142
      op = sign in1 ++ ".." ++ sign in2
1✔
143
      sign = \case
1✔
144
        Open   -> "<"
1✔
145
        Closed -> "<="
1✔
146

147
instance (Ord r, Read r) => Read (Interval r) where
148
  readsPrec p r =
1✔
149
    (readParen (p > appPrec) $ \s0 -> do
1✔
150
      ("interval",s1) <- lex s0
1✔
UNCOV
151
      (lb,s2) <- readsPrec (appPrec+1) s1
×
152
      (ub,s3) <- readsPrec (appPrec+1) s2
×
153
      return (interval lb ub, s3)) r
1✔
154
    ++
155
    (readParen (p > rangeOpPrec) $ \s0 -> do
1✔
156
      (do (l,s1) <- readsPrec (rangeOpPrec+1) s0
1✔
157
          (op',s2) <- lex s1
1✔
158
          op <-
159
            case op' of
1✔
160
              "<=..<=" -> return (<=..<=)
1✔
161
              "<..<="  -> return (<..<=)
1✔
162
              "<=..<"  -> return (<=..<)
1✔
163
              "<..<"   -> return (<..<)
1✔
UNCOV
164
              _ -> []
×
165
          (u,s3) <- readsPrec (rangeOpPrec+1) s2
1✔
166
          return (op l u, s3))) r
1✔
167
    ++
168
    (do ("empty", s) <- lex r
1✔
169
        return (empty, s))
1✔
170

171
-- | Lower endpoint (/i.e./ greatest lower bound)  of the interval.
172
--
173
-- * 'lowerBound' of the empty interval is 'PosInf'.
174
--
175
-- * 'lowerBound' of a left unbounded interval is 'NegInf'.
176
--
177
-- * 'lowerBound' of an interval may or may not be a member of the interval.
178
lowerBound :: Interval r -> Extended r
179
lowerBound = fst . lowerBound'
1✔
180

181
-- | Upper endpoint (/i.e./ least upper bound) of the interval.
182
--
183
-- * 'upperBound' of the empty interval is 'NegInf'.
184
--
185
-- * 'upperBound' of a right unbounded interval is 'PosInf'.
186
--
187
-- * 'upperBound' of an interval may or may not be a member of the interval.
188
upperBound :: Interval r -> Extended r
189
upperBound = fst . upperBound'
1✔
190

191
-- | closed interval [@l@,@u@]
192
(<=..<=)
193
  :: (Ord r)
194
  => Extended r -- ^ lower bound @l@
195
  -> Extended r -- ^ upper bound @u@
196
  -> Interval r
197
(<=..<=) lb ub = interval (lb, Closed) (ub, Closed)
1✔
198

199
-- | left-open right-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, Open) (ub, Closed)
1✔
206

207
-- | left-closed right-open 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, Closed) (ub, Open)
1✔
214

215
-- | 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, Open) (ub, Open)
1✔
222

223
-- | whole real number line (-∞, ∞)
224
whole :: Ord r => Interval r
UNCOV
225
whole = interval (NegInf, Open) (PosInf, Open)
×
226

227
-- | singleton set [x,x]
228
singleton :: Ord r => r -> Interval r
229
singleton x = interval (Finite x, Closed) (Finite x, Closed)
1✔
230

231
-- | intersection of two intervals
232
intersection :: forall r. Ord r => Interval r -> Interval r -> Interval r
233
intersection i1 i2 = interval
1✔
234
  (maxLB (lowerBound' i1) (lowerBound' i2))
1✔
235
  (minUB (upperBound' i1) (upperBound' i2))
1✔
236
  where
237
    maxLB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
238
    maxLB (x1,in1) (x2,in2) =
1✔
239
      ( max x1 x2
1✔
240
      , case x1 `compare` x2 of
1✔
241
          EQ -> in1 `min` in2
1✔
242
          LT -> in2
1✔
243
          GT -> in1
1✔
244
      )
245
    minUB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
246
    minUB (x1,in1) (x2,in2) =
1✔
247
      ( min x1 x2
1✔
248
      , case x1 `compare` x2 of
1✔
249
          EQ -> in1 `min` in2
1✔
250
          LT -> in1
1✔
251
          GT -> in2
1✔
252
      )
253

254
-- | intersection of a list of intervals.
255
--
256
-- @since 0.6.0
257
intersections :: Ord r => [Interval r] -> Interval r
258
intersections = foldl' intersection whole
1✔
259

260
-- | convex hull of two intervals
261
hull :: forall r. Ord r => Interval r -> Interval r -> Interval r
262
hull x1 x2
1✔
263
  | null x1 = x2
1✔
264
  | null x2 = x1
1✔
265
hull i1 i2 = interval
1✔
266
  (minLB (lowerBound' i1) (lowerBound' i2))
1✔
267
  (maxUB (upperBound' i1) (upperBound' i2))
1✔
268
  where
269
    maxUB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
270
    maxUB (x1,in1) (x2,in2) =
1✔
271
      ( max x1 x2
1✔
272
      , case x1 `compare` x2 of
1✔
273
          EQ -> in1 `max` in2
1✔
274
          LT -> in2
1✔
275
          GT -> in1
1✔
276
      )
277
    minLB :: (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
278
    minLB (x1,in1) (x2,in2) =
1✔
279
      ( min x1 x2
1✔
280
      , case x1 `compare` x2 of
1✔
281
          EQ -> in1 `max` in2
1✔
282
          LT -> in1
1✔
283
          GT -> in2
1✔
284
      )
285

286
-- | convex hull of a list of intervals.
287
--
288
-- @since 0.6.0
289
hulls :: Ord r => [Interval r] -> Interval r
290
hulls = foldl' hull empty
1✔
291

292
-- | Is the interval empty?
293
null :: Ord r => Interval r -> Bool
294
null i =
1✔
295
  case x1 `compare` x2 of
1✔
UNCOV
296
    EQ -> assert (in1 == Closed && in2 == Closed) False
×
297
    LT -> False
1✔
298
    GT -> True
1✔
299
  where
300
    (x1, in1) = lowerBound' i
1✔
301
    (x2, in2) = upperBound' i
1✔
302

303
-- | Is the interval single point?
304
--
305
-- @since 2.0.0
306
isSingleton :: Ord r => Interval r -> Bool
307
isSingleton = isJust . extractSingleton
1✔
308

309
-- | If the interval is a single point, return this point.
310
--
311
-- @since 2.1.0
312
extractSingleton :: Ord r => Interval r -> Maybe r
313
extractSingleton i = case (lowerBound' i, upperBound' i) of
1✔
314
  ((Finite l, Closed), (Finite u, Closed))
UNCOV
315
    | l == u -> Just l
×
316
  _ -> Nothing
1✔
317

318
-- | Is the element in the interval?
319
member :: Ord r => r -> Interval r -> Bool
320
member x i = condLB && condUB
1✔
321
  where
322
    (x1, in1) = lowerBound' i
1✔
323
    (x2, in2) = upperBound' i
1✔
324
    condLB = case in1 of
1✔
325
      Open   -> x1 <  Finite x
1✔
326
      Closed -> x1 <= Finite x
1✔
327
    condUB = case in2 of
1✔
328
      Open   -> Finite x <  x2
1✔
329
      Closed -> Finite x <= x2
1✔
330

331
-- | Is the element not in the interval?
332
notMember :: Ord r => r -> Interval r -> Bool
333
notMember a i = not $ member a i
1✔
334

335
-- | Is this a subset?
336
-- @(i1 \``isSubsetOf`\` i2)@ tells whether @i1@ is a subset of @i2@.
337
isSubsetOf :: Ord r => Interval r -> Interval r -> Bool
338
isSubsetOf i1 i2 = testLB (lowerBound' i1) (lowerBound' i2) && testUB (upperBound' i1) (upperBound' i2)
1✔
339
  where
340
    testLB (x1,in1) (x2,in2) =
1✔
341
      case x1 `compare` x2 of
1✔
342
        GT -> True
1✔
343
        LT -> False
1✔
344
        EQ -> in1 <= in2
1✔
345
    testUB (x1,in1) (x2,in2) =
1✔
346
      case x1 `compare` x2 of
1✔
347
        LT -> True
1✔
348
        GT -> False
1✔
349
        EQ -> in1 <= in2
1✔
350

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

355
-- | Does the union of two range form a connected set?
356
--
357
-- @since 1.3.0
358
isConnected :: Ord r => Interval r -> Interval r -> Bool
359
isConnected x y
1✔
360
  | null x = True
1✔
361
  | null y = True
1✔
362
  | otherwise = x ==? y || (lb1==ub2 && (lb1in == Closed || ub2in == Closed)) || (ub1==lb2 && (ub1in == Closed || lb2in == Closed))
1✔
363
  where
364
    (lb1,lb1in) = lowerBound' x
1✔
365
    (lb2,lb2in) = lowerBound' y
1✔
366
    (ub1,ub1in) = upperBound' x
1✔
367
    (ub2,ub2in) = upperBound' y
1✔
368

369
-- | Width of a interval. Width of an unbounded interval is @undefined@.
370
width :: (Num r, Ord r) => Interval r -> r
371
width x
1✔
372
  | null x = 0
1✔
373
  | otherwise = case (fst (lowerBound' x), fst (upperBound' x)) of
1✔
374
    (Finite l, Finite u) -> u - l
1✔
UNCOV
375
    _ -> error "Data.Interval.width: unbounded interval"
×
376

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

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

420
-- | @mapMonotonic f i@ is the image of @i@ under @f@, where @f@ must be a strict monotone function,
421
-- preserving negative and positive infinities.
422
mapMonotonic :: (Ord a, Ord b) => (a -> b) -> Interval a -> Interval b
423
mapMonotonic f i = interval (fmap f lb, in1) (fmap f ub, in2)
1✔
424
  where
425
    (lb, in1) = lowerBound' i
1✔
426
    (ub, in2) = upperBound' i
1✔
427

428
mapAntiMonotonic :: (Ord a, Ord b) => (a -> b) -> Interval a -> Interval b
429
mapAntiMonotonic f i
1✔
430
  | null i = empty
1✔
431
  | otherwise = interval (fmap f ub, in2) (fmap f lb, in1)
1✔
432
  where
433
    (lb, in1) = lowerBound' i
1✔
434
    (ub, in2) = upperBound' i
1✔
435

436
-- | For all @x@ in @X@, @y@ in @Y@. @x '<' y@?
437
(<!) :: Ord r => Interval r -> Interval r -> Bool
438
a <! b =
1✔
439
  case ub_a `compare` lb_b of
1✔
440
    LT -> True
1✔
441
    GT -> False
1✔
442
    EQ ->
443
      case ub_a of
1✔
444
        NegInf   -> True -- a is empty, so it holds vacuously
1✔
445
        PosInf   -> True -- b is empty, so it holds vacuously
1✔
446
        Finite _ -> in1 == Open || in2 == Open
1✔
447
  where
448
    (ub_a, in1) = upperBound' a
1✔
449
    (lb_b, in2) = lowerBound' b
1✔
450

451
-- | For all @x@ in @X@, @y@ in @Y@. @x '<=' y@?
452
(<=!) :: Ord r => Interval r -> Interval r -> Bool
453
a <=! b = upperBound a <= lowerBound b
1✔
454

455
-- | For all @x@ in @X@, @y@ in @Y@. @x '==' y@?
456
(==!) :: Ord r => Interval r -> Interval r -> Bool
457
a ==! b = a <=! b && a >=! b
1✔
458

459
-- | For all @x@ in @X@, @y@ in @Y@. @x '/=' y@?
460
--
461
-- @since 1.0.1
462
(/=!) :: Ord r => Interval r -> Interval r -> Bool
463
a /=! b = null $ a `intersection` b
1✔
464

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

469
-- | For all @x@ in @X@, @y@ in @Y@. @x '>' y@?
470
(>!) :: Ord r => Interval r -> Interval r -> Bool
471
(>!) = flip (<!)
1✔
472

473
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<' y@?
474
(<?) :: Ord r => Interval r -> Interval r -> Bool
475
a <? b = lb_a < ub_b
1✔
476
  where
477
    lb_a = lowerBound a
1✔
478
    ub_b = upperBound b
1✔
479

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

499
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<=' y@?
500
(<=?) :: Ord r => Interval r -> Interval r -> Bool
501
a <=? b =
1✔
502
  case lb_a `compare` ub_b of
1✔
503
    LT -> True
1✔
504
    GT -> False
1✔
505
    EQ ->
506
      case lb_a of
1✔
507
        NegInf -> False -- b is empty
1✔
508
        PosInf -> False -- a is empty
1✔
509
        Finite _ -> in1 == Closed && in2 == Closed
1✔
510
  where
511
    (lb_a, in1) = lowerBound' a
1✔
512
    (ub_b, in2) = upperBound' b
1✔
513

514
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<=' y@?
515
--
516
-- @since 1.0.0
517
(<=??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
518
a <=?? b =
1✔
519
  case pickup (intersection a b) of
1✔
520
    Just x -> return (x,x)
1✔
521
    Nothing -> do
1✔
522
      guard $ upperBound a <= lowerBound b
1✔
523
      x <- pickup a
1✔
524
      y <- pickup b
1✔
525
      return (x,y)
1✔
526

527
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '==' y@?
528
--
529
-- @since 1.0.0
530
(==?) :: Ord r => Interval r -> Interval r -> Bool
531
a ==? b = not $ null $ intersection a b
1✔
532

533
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '==' y@?
534
--
535
-- @since 1.0.0
536
(==??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
537
a ==?? b = do
1✔
538
  x <- pickup (intersection a b)
1✔
539
  return (x,x)
1✔
540

541
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '/=' y@?
542
--
543
-- @since 1.0.1
544
(/=?) :: Ord r => Interval r -> Interval r -> Bool
545
a /=? b = not (null a) && not (null b) && not (a == b && isSingleton a)
1✔
546

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

564
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>=' y@?
565
(>=?) :: Ord r => Interval r -> Interval r -> Bool
566
(>=?) = flip (<=?)
1✔
567

568
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>' y@?
569
(>?) :: Ord r => Interval r -> Interval r -> Bool
570
(>?) = flip (<?)
1✔
571

572
-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>=' y@?
573
--
574
-- @since 1.0.0
575
(>=??) :: (Real r, Fractional r) => Interval r -> Interval r -> Maybe (r,r)
UNCOV
576
(>=??) = flip (<=??)
×
577

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

584
appPrec :: Int
585
appPrec = 10
1✔
586

587
rangeOpPrec :: Int
588
rangeOpPrec = 5
1✔
589

590
scaleInterval :: (Num r, Ord r) => r -> Interval r -> Interval r
591
scaleInterval c x
1✔
592
  | null x = empty
1✔
593
  | otherwise = case compare c 0 of
1✔
UNCOV
594
    EQ -> singleton 0
×
595
    LT -> interval (scaleInf' c ub) (scaleInf' c lb)
1✔
UNCOV
596
    GT -> interval (scaleInf' c lb) (scaleInf' c ub)
×
597
  where
598
    lb = lowerBound' x
1✔
599
    ub = upperBound' x
1✔
600

601
-- | When results of 'abs' or 'signum' do not form a connected interval,
602
-- a convex hull is returned instead.
603
instance (Num r, Ord r) => Num (Interval r) where
604
  a + b
1✔
605
    | null a || null b = empty
1✔
606
    | otherwise = interval (f (lowerBound' a) (lowerBound' b)) (g (upperBound' a) (upperBound' b))
1✔
607
    where
608
      f (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 `min` in2)
1✔
UNCOV
609
      f (NegInf,_) _ = (-inf, Open)
×
610
      f _ (NegInf,_) = (-inf, Open)
×
611
      f _ _ = error "Interval.(+) should not happen"
×
612

613
      g (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 `min` in2)
1✔
UNCOV
614
      g (PosInf,_) _ = (inf, Open)
×
615
      g _ (PosInf,_) = (inf, Open)
×
616
      g _ _ = error "Interval.(+) should not happen"
×
617

618
  negate = scaleInterval (-1)
1✔
619

620
  fromInteger i = singleton (fromInteger i)
1✔
621

622
  abs x = (x `intersection` nonneg) `hull` (negate x `intersection` nonneg)
1✔
623
    where
624
      nonneg = 0 <=..< inf
1✔
625

626
  signum x = zero `hull` pos `hull` neg
1✔
627
    where
628
      zero = if member 0 x then singleton 0 else empty
1✔
629
      pos = if null $ (0 <..< inf) `intersection` x
1✔
630
            then empty
1✔
631
            else singleton 1
1✔
632
      neg = if null $ (-inf <..< 0) `intersection` x
1✔
633
            then empty
1✔
634
            else singleton (-1)
1✔
635

636
  a * b
1✔
637
    | null a || null b = empty
1✔
638
    | otherwise = interval lb3 ub3
1✔
639
    where
640
      xs = [ mulInf' x1 x2 | x1 <- [lowerBound' a, upperBound' a], x2 <- [lowerBound' b, upperBound' b] ]
1✔
641
      ub3 = maximumBy cmpUB xs
1✔
642
      lb3 = minimumBy cmpLB xs
1✔
643

644
-- | 'recip' returns 'whole' when 0 is an interior point.
645
-- Otherwise @recip (recip xs)@ equals to @xs@ without 0.
646
instance forall r. (Real r, Fractional r) => Fractional (Interval r) where
647
  fromRational r = singleton (fromRational r)
1✔
648
  recip a
1✔
649
    | null a = empty
1✔
650
    | a == 0 = empty
1✔
651
    | 0 `member` a && 0 /= lowerBound a && 0 /= upperBound a = whole
1✔
652
    | otherwise = interval lb3 ub3
1✔
653
    where
654
      ub3 = maximumBy cmpUB xs
1✔
655
      lb3 = minimumBy cmpLB xs
1✔
656
      xs = [recipLB (lowerBound' a), recipUB (upperBound' a)]
1✔
657

658
-- | When results of 'tan' or '**' do not form a connected interval,
659
-- a convex hull is returned instead.
660
instance (RealFrac r, Floating r) => Floating (Interval r) where
661
  pi = singleton pi
1✔
662

663
  exp = intersection (0 <..< PosInf) . mapMonotonic exp
1✔
664
  log a = interval (logB (lowerBound' b)) (logB (upperBound' b))
1✔
665
    where
666
      b = intersection (0 <..< PosInf) a
1✔
667

668
  sqrt = mapMonotonic sqrt . intersection (0 <=..< PosInf)
1✔
669

670
  a ** b = hulls (posBase : negBasePosPower : negBaseNegPower : zeroPower ++ zeroBase)
1✔
671
    where
672
      posBase = exp (log a * b)
1✔
673
      zeroPower = [ 1 | 0 `member` b, not (null a) ]
1✔
674
      zeroBase  = [ 0 | 0 `member` a, not (null (b `intersection` (0 <..< PosInf))) ]
1✔
675
      negBasePosPower = positiveIntegralPowersOfNegativeValues
1✔
676
        (a `intersection` (NegInf <..< 0))
1✔
677
        (b `intersection` (0 <..< PosInf))
1✔
678
      negBaseNegPower = positiveIntegralPowersOfNegativeValues
1✔
679
        (recip  (a `intersection` (NegInf <..< 0)))
1✔
680
        (negate (b `intersection` (NegInf <..< 0)))
1✔
681

682
  cos a = case lowerBound' a of
1✔
683
    (NegInf, _) -> -1 <=..<= 1
1✔
684
    (PosInf, _) -> empty
1✔
685
    (Finite lb, in1) -> case upperBound' a of
1✔
UNCOV
686
      (NegInf, _) -> empty
×
687
      (PosInf, _) -> -1 <=..<= 1
1✔
688
      (Finite ub, in2)
689
        | ub - lb > 2 * pi                                             -> -1 <=..<= 1
1✔
UNCOV
690
        | clb == -1 && ub - lb == 2 * pi && in1 == Open && in2 == Open -> -1 <..<= 1
×
691
        | clb ==  1 && ub - lb == 2 * pi && in1 == Open && in2 == Open -> -1 <=..< 1
×
692
        | ub - lb == 2 * pi                                            -> -1 <=..<= 1
×
693

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

715
  acos = mapAntiMonotonic acos . intersection (-1 <=..<= 1)
1✔
716

717
  sin a = cos (pi / 2 - a)
1✔
718
  asin = mapMonotonic asin . intersection (-1 <=..<= 1)
1✔
719

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

739
  atan = intersection (Finite (-pi / 2) <=..<= Finite (pi / 2)) . mapMonotonic atan
1✔
740

741
  sinh  = mapMonotonic sinh
1✔
742
  asinh = mapMonotonic asinh
1✔
743

744
  cosh  = mapMonotonic cosh . abs
1✔
745
  acosh = mapMonotonic acosh . intersection (1 <=..< PosInf)
1✔
746

747
  tanh  = intersection (-1 <..< 1) . mapMonotonic tanh
1✔
748
  atanh a = interval (atanhB (lowerBound' b)) (atanhB (upperBound' b))
1✔
749
    where
750
      b = intersection (-1 <..< 1) a
1✔
751

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

780
cmpUB, cmpLB :: Ord r => (Extended r, Boundary) -> (Extended r, Boundary) -> Ordering
781
cmpUB (x1,in1) (x2,in2) = compare x1 x2 `mappend` compare in1 in2
1✔
782
cmpLB (x1,in1) (x2,in2) = compare x1 x2 `mappend` compare in2 in1
1✔
783

784
scaleInf' :: (Num r, Ord r) => r -> (Extended r, Boundary) -> (Extended r, Boundary)
785
scaleInf' a (x1, in1) = (scaleEndPoint a x1, in1)
1✔
786

787
scaleEndPoint :: (Num r, Ord r) => r -> Extended r -> Extended r
788
scaleEndPoint a e =
1✔
789
  case a `compare` 0 of
1✔
UNCOV
790
    EQ -> 0
×
791
    GT ->
UNCOV
792
      case e of
×
793
        NegInf   -> NegInf
×
794
        Finite b -> Finite (a*b)
×
795
        PosInf   -> PosInf
×
796
    LT ->
797
      case e of
1✔
798
        NegInf   -> PosInf
1✔
799
        Finite b -> Finite (a*b)
1✔
800
        PosInf   -> NegInf
1✔
801

802
mulInf' :: (Num r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary) -> (Extended r, Boundary)
803
mulInf' (0, Closed) _ = (0, Closed)
1✔
804
mulInf' _ (0, Closed) = (0, Closed)
1✔
805
mulInf' (x1,in1) (x2,in2) = (x1*x2, in1 `min` in2)
1✔
806

807
recipLB :: (Fractional r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
UNCOV
808
recipLB (0, _) = (PosInf, Open)
×
809
recipLB (x1, in1) = (recip x1, in1)
1✔
810

811
recipUB :: (Fractional r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
UNCOV
812
recipUB (0, _) = (NegInf, Open)
×
813
recipUB (x1, in1) = (recip x1, in1)
1✔
814

815
logB :: (Floating r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
UNCOV
816
logB (NegInf, in1) = (Finite $ log (log 0), in1)
×
817
logB (Finite 0, _) = (NegInf, Open)
×
818
logB (Finite x1, in1) = (Finite $ log x1, in1)
1✔
UNCOV
819
logB (PosInf, in1) = (PosInf, in1)
×
820

821
atanhB :: (Floating r, Ord r) => (Extended r, Boundary) -> (Extended r, Boundary)
822
atanhB (NegInf, in1) = (Finite $ atanh (-1/0), in1)
1✔
UNCOV
823
atanhB (Finite (-1), _) = (NegInf, Open)
×
824
atanhB (Finite 1, _) = (PosInf, Open)
×
825
atanhB (Finite x1, in1) = (Finite $ atanh x1, in1)
1✔
826
atanhB (PosInf, in1) = (Finite $ atanh (1/0), in1)
1✔
827

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