{-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE DeriveDataTypeable #-}
#if __GLASGOW_HASKELL__ >= 704
{-# LANGUAGE DeriveGeneric #-}
#endif
module Numeric.Interval.Kaucher
( Interval(..)
, (...)
, interval
, whole
, empty
, null
, singleton
, member
, notMember
, elem
, notElem
, inf
, sup
, singular
, width
, midpoint
, intersection
, hull
, bisect
, magnitude
, mignitude
, distance
, inflate, deflate
, scale, symmetric
, contains
, isSubsetOf
, certainly, (<!), (<=!), (==!), (>=!), (>!)
, possibly, (<?), (<=?), (==?), (>=?), (>?)
, clamp
, idouble
, ifloat
, iquot
, irem
, idiv
, imod
) where
import Control.Applicative hiding (empty)
import Control.Exception as Exception
import Data.Data
import Data.Distributive
import Data.Foldable hiding (minimum, maximum, elem, notElem
#if __GLASGOW_HASKELL__ >= 710
, null
#endif
)
import Data.Function (on)
import Data.Monoid
import Data.Traversable
#if __GLASGOW_HASKELL__ >= 704
import GHC.Generics
#endif
import Numeric.Interval.Exception
import Prelude hiding (null, elem, notElem)
data Interval a = I !a !a deriving
( Eq, Ord
, Data
, Typeable
#if __GLASGOW_HASKELL__ >= 704
, Generic
#if __GLASGOW_HASKELL__ >= 706
, Generic1
#endif
#endif
)
instance Functor Interval where
fmap f (I a b) = I (f a) (f b)
{-# INLINE fmap #-}
instance Foldable Interval where
foldMap f (I a b) = f a `mappend` f b
{-# INLINE foldMap #-}
instance Traversable Interval where
traverse f (I a b) = I <$> f a <*> f b
{-# INLINE traverse #-}
instance Applicative Interval where
pure a = I a a
{-# INLINE pure #-}
I f g <*> I a b = I (f a) (g b)
{-# INLINE (<*>) #-}
instance Monad Interval where
return a = I a a
{-# INLINE return #-}
I a b >>= f = I a' b' where
I a' _ = f a
I _ b' = f b
{-# INLINE (>>=) #-}
instance Distributive Interval where
distribute f = fmap inf f ... fmap sup f
{-# INLINE distribute #-}
infix 3 ...
negInfinity :: Fractional a => a
negInfinity = (-1)/0
{-# INLINE negInfinity #-}
posInfinity :: Fractional a => a
posInfinity = 1/0
{-# INLINE posInfinity #-}
nan :: Fractional a => a
nan = 0/0
fmod :: RealFrac a => a -> a -> a
fmod a b = a - q*b where
q = realToFrac (truncate $ a / b :: Integer)
{-# INLINE fmod #-}
(...) :: a -> a -> Interval a
(...) = I
{-# INLINE (...) #-}
interval :: Ord a => a -> a -> Maybe (Interval a)
interval a b
| a <= b = Just $ I a b
| otherwise = Nothing
whole :: Fractional a => Interval a
whole = negInfinity ... posInfinity
{-# INLINE whole #-}
empty :: Fractional a => Interval a
empty = nan ... nan
{-# INLINE empty #-}
null :: Ord a => Interval a -> Bool
null x = not (inf x <= sup x)
{-# INLINE null #-}
singleton :: a -> Interval a
singleton a = a ... a
{-# INLINE singleton #-}
inf :: Interval a -> a
inf (I a _) = a
{-# INLINE inf #-}
sup :: Interval a -> a
sup (I _ b) = b
{-# INLINE sup #-}
singular :: Ord a => Interval a -> Bool
singular x = not (null x) && inf x == sup x
{-# INLINE singular #-}
instance Show a => Show (Interval a) where
showsPrec n (I a b) =
showParen (n > 3) $
showsPrec 3 a .
showString " ... " .
showsPrec 3 b
width :: Num a => Interval a -> a
width (I a b) = b - a
{-# INLINE width #-}
magnitude :: (Num a, Ord a) => Interval a -> a
magnitude = sup . abs
{-# INLINE magnitude #-}
mignitude :: (Num a, Ord a) => Interval a -> a
mignitude = inf . abs
{-# INLINE mignitude #-}
distance :: (Num a, Ord a) => Interval a -> Interval a -> a
distance i1 i2 = mignitude (i1 - i2)
inflate :: (Num a, Ord a) => a -> Interval a -> Interval a
inflate x y = symmetric x + y
deflate :: Fractional a => a -> Interval a -> Interval a
deflate x (I a b) = I a' b'
where
a' = a + x
b' = b - x
scale :: Fractional a => a -> Interval a -> Interval a
scale x i = I a b where
h = x * width i / 2
mid = midpoint i
a = mid - h
b = mid + h
symmetric :: Num a => a -> Interval a
symmetric x = negate x ... x
instance (Num a, Ord a) => Num (Interval a) where
I a b + I a' b' = (a + a') ... (b + b')
{-# INLINE (+) #-}
I a b - I a' b' = (a - b') ... (b - a')
{-# INLINE (-) #-}
I a b * I a' b' =
minimum [a * a', a * b', b * a', b * b']
...
maximum [a * a', a * b', b * a', b * b']
{-# INLINE (*) #-}
abs x@(I a b)
| a >= 0 = x
| b <= 0 = negate x
| b > 0 && a < 0 = 0 ... max (- a) b
| otherwise = x
{-# INLINE abs #-}
signum = increasing signum
{-# INLINE signum #-}
fromInteger i = singleton (fromInteger i)
{-# INLINE fromInteger #-}
bisect :: Fractional a => Interval a -> (Interval a, Interval a)
bisect x = (inf x ... m, m ... sup x) where m = midpoint x
{-# INLINE bisect #-}
midpoint :: Fractional a => Interval a -> a
midpoint x = inf x + (sup x - inf x) / 2
{-# INLINE midpoint #-}
member :: Ord a => a -> Interval a -> Bool
member x (I a b) = x >= a && x <= b
{-# INLINE member #-}
notMember :: Ord a => a -> Interval a -> Bool
notMember x xs = not (member x xs)
{-# INLINE notMember #-}
elem :: Ord a => a -> Interval a -> Bool
elem = member
{-# INLINE elem #-}
{-# DEPRECATED elem "Use `member` instead." #-}
notElem :: Ord a => a -> Interval a -> Bool
notElem = notMember
{-# INLINE notElem #-}
{-# DEPRECATED notElem "Use `notMember` instead." #-}
instance Real a => Real (Interval a) where
toRational x
| null x = Exception.throw EmptyInterval
| otherwise = a + (b - a) / 2
where
a = toRational (inf x)
b = toRational (sup x)
{-# INLINE toRational #-}
divNonZero :: (Fractional a, Ord a) => Interval a -> Interval a -> Interval a
divNonZero (I a b) (I a' b') =
minimum [a / a', a / b', b / a', b / b']
...
maximum [a / a', a / b', b / a', b / b']
divPositive :: (Fractional a, Ord a) => Interval a -> a -> Interval a
divPositive x@(I a b) y
| a == 0 && b == 0 = x
| b < 0 = negInfinity ... ( b / y)
| a < 0 = whole
| otherwise = (a / y) ... posInfinity
{-# INLINE divPositive #-}
divNegative :: (Fractional a, Ord a) => Interval a -> a -> Interval a
divNegative x@(I a b) y
| a == 0 && b == 0 = - x
| b < 0 = (b / y) ... posInfinity
| a < 0 = whole
| otherwise = negInfinity ... (a / y)
{-# INLINE divNegative #-}
divZero :: (Fractional a, Ord a) => Interval a -> Interval a
divZero x
| inf x == 0 && sup x == 0 = x
| otherwise = whole
{-# INLINE divZero #-}
instance (Fractional a, Ord a) => Fractional (Interval a) where
x / y
| 0 `notElem` y = divNonZero x y
| iz && sz = empty
| iz = divPositive x (inf y)
| sz = divNegative x (sup y)
| otherwise = divZero x
where
iz = inf y == 0
sz = sup y == 0
recip (I a b) = on min recip a b ... on max recip a b
{-# INLINE recip #-}
fromRational r = let r' = fromRational r in r' ... r'
{-# INLINE fromRational #-}
instance RealFrac a => RealFrac (Interval a) where
properFraction x = (b, x - fromIntegral b)
where
b = truncate (midpoint x)
{-# INLINE properFraction #-}
ceiling x = ceiling (sup x)
{-# INLINE ceiling #-}
floor x = floor (inf x)
{-# INLINE floor #-}
round x = round (midpoint x)
{-# INLINE round #-}
truncate x = truncate (midpoint x)
{-# INLINE truncate #-}
instance (RealFloat a, Ord a) => Floating (Interval a) where
pi = singleton pi
{-# INLINE pi #-}
exp = increasing exp
{-# INLINE exp #-}
log (I a b) = (if a > 0 then log a else negInfinity) ... log b
{-# INLINE log #-}
cos x
| null x = empty
| width t >= pi = (-1) ... 1
| inf t >= pi = - cos (t - pi)
| sup t <= pi = decreasing cos t
| sup t <= 2 * pi = (-1) ... cos ((pi * 2 - sup t) `min` inf t)
| otherwise = (-1) ... 1
where
t = fmod x (pi * 2)
{-# INLINE cos #-}
sin x
| null x = empty
| otherwise = cos (x - pi / 2)
{-# INLINE sin #-}
tan x
| null x = empty
| inf t' <= - pi / 2 || sup t' >= pi / 2 = whole
| otherwise = increasing tan x
where
t = x `fmod` pi
t' | t >= pi / 2 = t - pi
| otherwise = t
{-# INLINE tan #-}
asin x@(I a b)
| null x || b < -1 || a > 1 = empty
| otherwise =
(if a <= -1 then -halfPi else asin a)
...
(if b >= 1 then halfPi else asin b)
where
halfPi = pi / 2
{-# INLINE asin #-}
acos x@(I a b)
| null x || b < -1 || a > 1 = empty
| otherwise =
(if b >= 1 then 0 else acos b)
...
(if a < -1 then pi else acos a)
{-# INLINE acos #-}
atan = increasing atan
{-# INLINE atan #-}
sinh = increasing sinh
{-# INLINE sinh #-}
cosh x@(I a b)
| null x = empty
| b < 0 = decreasing cosh x
| a >= 0 = increasing cosh x
| otherwise = I 0 $ cosh $ if - a > b
then a
else b
{-# INLINE cosh #-}
tanh = increasing tanh
{-# INLINE tanh #-}
asinh = increasing asinh
{-# INLINE asinh #-}
acosh x@(I a b)
| null x || b < 1 = empty
| otherwise = I lo $ acosh b
where lo | a <= 1 = 0
| otherwise = acosh a
{-# INLINE acosh #-}
atanh x@(I a b)
| null x || b < -1 || a > 1 = empty
| otherwise =
(if a <= - 1 then negInfinity else atanh a)
...
(if b >= 1 then posInfinity else atanh b)
{-# INLINE atanh #-}
increasing :: (a -> b) -> Interval a -> Interval b
increasing f (I a b) = f a ... f b
decreasing :: (a -> b) -> Interval a -> Interval b
decreasing f (I a b) = f b ... f a
instance RealFloat a => RealFloat (Interval a) where
floatRadix = floatRadix . midpoint
floatDigits = floatDigits . midpoint
floatRange = floatRange . midpoint
decodeFloat = decodeFloat . midpoint
encodeFloat m e = singleton (encodeFloat m e)
exponent = exponent . midpoint
significand x = min a b ... max a b
where
(_ ,em) = decodeFloat (midpoint x)
(mi,ei) = decodeFloat (inf x)
(ms,es) = decodeFloat (sup x)
a = encodeFloat mi (ei - em - floatDigits x)
b = encodeFloat ms (es - em - floatDigits x)
scaleFloat n x = scaleFloat n (inf x) ... scaleFloat n (sup x)
isNaN x = isNaN (inf x) || isNaN (sup x)
isInfinite x = isInfinite (inf x) || isInfinite (sup x)
isDenormalized x = isDenormalized (inf x) || isDenormalized (sup x)
isNegativeZero x = not (inf x > 0)
&& not (sup x < 0)
&& ( (sup x == 0 && (inf x < 0 || isNegativeZero (inf x)))
|| (inf x == 0 && isNegativeZero (inf x))
|| (inf x < 0 && sup x >= 0))
isIEEE x = isIEEE (inf x) && isIEEE (sup x)
atan2 = error "unimplemented"
intersection :: (Fractional a, Ord a) => Interval a -> Interval a -> Interval a
intersection x@(I a b) y@(I a' b')
| x /=! y = empty
| otherwise = max a a' ... min b b'
{-# INLINE intersection #-}
hull :: Ord a => Interval a -> Interval a -> Interval a
hull x@(I a b) y@(I a' b')
| null x = y
| null y = x
| otherwise = min a a' ... max b b'
{-# INLINE hull #-}
(<!) :: Ord a => Interval a -> Interval a -> Bool
x <! y = sup x < inf y
{-# INLINE (<!) #-}
(<=!) :: Ord a => Interval a -> Interval a -> Bool
x <=! y = sup x <= inf y
{-# INLINE (<=!) #-}
(==!) :: Eq a => Interval a -> Interval a -> Bool
x ==! y = sup x == inf y && inf x == sup y
{-# INLINE (==!) #-}
(/=!) :: Ord a => Interval a -> Interval a -> Bool
x /=! y = sup x < inf y || inf x > sup y
{-# INLINE (/=!) #-}
(>!) :: Ord a => Interval a -> Interval a -> Bool
x >! y = inf x > sup y
{-# INLINE (>!) #-}
(>=!) :: Ord a => Interval a -> Interval a -> Bool
x >=! y = inf x >= sup y
{-# INLINE (>=!) #-}
certainly :: Ord a => (forall b. Ord b => b -> b -> Bool) -> Interval a -> Interval a -> Bool
certainly cmp l r
| lt && eq && gt = True
| lt && eq = l <=! r
| lt && gt = l /=! r
| lt = l <! r
| eq && gt = l >=! r
| eq = l ==! r
| gt = l >! r
| otherwise = False
where
lt = cmp LT EQ
eq = cmp EQ EQ
gt = cmp GT EQ
{-# INLINE certainly #-}
contains :: Ord a => Interval a -> Interval a -> Bool
contains x y = null y
|| (not (null x) && inf x <= inf y && sup y <= sup x)
{-# INLINE contains #-}
isSubsetOf :: Ord a => Interval a -> Interval a -> Bool
isSubsetOf = flip contains
{-# INLINE isSubsetOf #-}
(<?) :: Ord a => Interval a -> Interval a -> Bool
x <? y = inf x < sup y
{-# INLINE (<?) #-}
(<=?) :: Ord a => Interval a -> Interval a -> Bool
x <=? y = inf x <= sup y
{-# INLINE (<=?) #-}
(==?) :: Ord a => Interval a -> Interval a -> Bool
x ==? y = inf x <= sup y && sup x >= inf y
{-# INLINE (==?) #-}
(/=?) :: Eq a => Interval a -> Interval a -> Bool
x /=? y = inf x /= sup y || sup x /= inf y
{-# INLINE (/=?) #-}
(>?) :: Ord a => Interval a -> Interval a -> Bool
x >? y = sup x > inf y
{-# INLINE (>?) #-}
(>=?) :: Ord a => Interval a -> Interval a -> Bool
x >=? y = sup x >= inf y
{-# INLINE (>=?) #-}
possibly :: Ord a => (forall b. Ord b => b -> b -> Bool) -> Interval a -> Interval a -> Bool
possibly cmp l r
| lt && eq && gt = True
| lt && eq = l <=? r
| lt && gt = l /=? r
| lt = l <? r
| eq && gt = l >=? r
| eq = l ==? r
| gt = l >? r
| otherwise = False
where
lt = cmp LT EQ
eq = cmp EQ EQ
gt = cmp GT EQ
{-# INLINE possibly #-}
clamp :: Ord a => Interval a -> a -> a
clamp (I a b) x | x < a = a
| x > b = b
| otherwise = x
idouble :: Interval Double -> Interval Double
idouble = id
ifloat :: Interval Float -> Interval Float
ifloat = id
default (Integer,Double)
iquot :: Integral a => Interval a -> Interval a -> Interval a
iquot (I l u) (I l' u') =
if l' <= 0 && 0 <= u' then throw DivideByZero else I
(minimum [a `quot` b | a <- [l,u], b <- [l',u']])
(maximum [a `quot` b | a <- [l,u], b <- [l',u']])
irem :: Integral a => Interval a -> Interval a -> Interval a
irem (I l u) (I l' u') =
if l' <= 0 && 0 <= u' then throw DivideByZero else I
(minimum [0, signum l * (abs u' - 1), signum l * (abs l' - 1)])
(maximum [0, signum u * (abs u' - 1), signum u * (abs l' - 1)])
idiv :: Integral a => Interval a -> Interval a -> Interval a
idiv (I l u) (I l' u') =
if l' <= 0 && 0 <= u' then throw DivideByZero else I
(min (l `Prelude.div` max 1 l') (u `Prelude.div` min (-1) u'))
(max (u `Prelude.div` max 1 l') (l `Prelude.div` min (-1) u'))
imod :: Integral a => Interval a -> Interval a -> Interval a
imod _ (I l' u') =
if l' <= 0 && 0 <= u' then throw DivideByZero else
I (min (l'+1) 0) (max 0 (u'-1))