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

msakai / data-interval / 78

07 Jun 2025 03:57PM UTC coverage: 86.439% (-0.3%) from 86.702%
78

Pull #46

github

Bodigrim
Introduce Data.RealFloatInterval
Pull Request #46: Introduce Data.RealFloatInterval

988 of 1143 relevant lines covered (86.44%)

0.86 hits per line

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

87.96
/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
-- Portability :  non-portable (CPP, ScopedTypeVariables, DeriveDataTypeable)
14
--
15
-- Interval datatype and interval arithmetic.
16
--
17
-- Unlike the intervals package (<http://hackage.haskell.org/package/intervals>),
18
-- this module provides both open and closed intervals and is intended to be used
19
-- with 'Rational'.
20
--
21
-- For the purpose of abstract interpretation, it might be convenient to use
22
-- 'Lattice' instance. See also lattices package
23
-- (<http://hackage.haskell.org/package/lattices>).
24
--
25
-----------------------------------------------------------------------------
26
module Data.Interval
27
  (
28
  -- * Interval type
29
    Interval
30
  , module Data.ExtendedReal
31
  , Boundary(..)
32

33
  -- * Construction
34
  , interval
35
  , (<=..<=)
36
  , (<..<=)
37
  , (<=..<)
38
  , (<..<)
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
  ) where
83

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

98
infix 5 <=..<=
99
infix 5 <..<=
100
infix 5 <=..<
101
infix 5 <..<
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
infix 4 /=??
120

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

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

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

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

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

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

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

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

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

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

216
-- | 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, Open) (ub, Open)
1✔
223

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

619
  negate = scaleInterval (-1)
1✔
620

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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