{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Numeric.Rounded.Hardware.Interval.NonEmpty
( Interval(..)
, increasing
, maxI
, minI
, powInt
, null
, inf
, sup
, width
, hull
) where
import Control.DeepSeq (NFData (..))
import Control.Monad
import Control.Monad.ST
import qualified Data.Array.Base as A
import Data.Coerce
import Data.Ix
import Data.Primitive
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
import GHC.Float (log1p, expm1)
import GHC.Generics (Generic)
import Numeric.Rounded.Hardware.Internal
import qualified Numeric.Rounded.Hardware.Interval.Class as C
import qualified Numeric.Rounded.Hardware.Interval.ElementaryFunctions as C
import Prelude hiding (null)
data Interval a
= I !(Rounded 'TowardNegInf a) !(Rounded 'TowardInf a)
deriving (Show,Generic)
instance NFData a => NFData (Interval a)
increasing :: (forall r. Rounding r => Rounded r a -> Rounded r a) -> Interval a -> Interval a
increasing f (I a b) = I (f a) (f b)
negateI :: (Num a, RoundedRing a) => Interval a -> Interval a
negateI (I a b) = I (negate (coerce b)) (negate (coerce a))
{-# INLINE [0] negateI #-}
addI, subI, mulI :: (Num a, RoundedRing a) => Interval a -> Interval a -> Interval a
I a b `addI` I a' b' = case intervalAdd a b a' b' of
(a'', b'') -> I a'' b''
I a b `subI` I a' b' = case intervalSub a b a' b' of
(a'', b'') -> I a'' b''
I a b `mulI` I a' b' = case intervalMul a b a' b' of
(a'', b'') -> I a'' b''
mulAddI :: (Num a, RoundedRing a) => Interval a -> Interval a -> Interval a -> Interval a
mulAddI (I a b) (I a' b') (I a'' b'') = case intervalMulAdd a b a' b' a'' b'' of
(x, y) -> I x y
normalizeDivisor :: (Ord a, Num a) => Interval a -> Interval a
normalizeDivisor x@(I (Rounded a) (Rounded b))
| 0 < a || b < 0 = x
| a == 0 && 0 < b = I (Rounded 0) (Rounded b)
| a < 0 && b == 0 = I (Rounded a) (Rounded (-0))
| otherwise = error "divide by zero"
divI :: (Num a, RoundedFractional a) => Interval a -> Interval a -> Interval a
I a b `divI` y = let I a' b' = normalizeDivisor y
(z, z') = intervalDiv a b a' b'
in I z z'
divAddI :: (Num a, RoundedFractional a) => Interval a -> Interval a -> Interval a -> Interval a
divAddI (I a b) y (I a'' b'') = let I a' b' = normalizeDivisor y
(z, z') = intervalDivAdd a b a' b' a'' b''
in I z z'
{-# INLINE [0] addI #-}
{-# INLINE [0] subI #-}
{-# INLINE [0] mulI #-}
{-# INLINE [0] divI #-}
{-# INLINE mulAddI #-}
{-# INLINE divAddI #-}
{-# RULES
"Interval.NonEmpty/x*y+z" forall x y z. addI (mulI x y) z = mulAddI x y z
"Interval.NonEmpty/z+x*y" forall x y z. addI z (mulI x y) = mulAddI x y z
"Interval.NonEmpty/x*y-z" forall x y z. subI (mulI x y) z = mulAddI x y (negateI z)
"Interval.NonEmpty/z-x*y" forall x y z. subI z (mulI x y) = negateI (mulAddI x y (negateI z))
"Interval.NonEmpty/x/y+z" forall x y z. addI (divI x y) z = divAddI x y z
"Interval.NonEmpty/z+x/y" forall x y z. addI z (divI x y) = divAddI x y z
"Interval.NonEmpty/x/y-z" forall x y z. subI (divI x y) z = divAddI x y (negateI z)
"Interval.NonEmpty/z-x/y" forall x y z. subI z (divI x y) = negateI (divAddI x y (negateI z))
"Interval.NonEmpty/negate-negate" forall x. negateI (negateI x) = x
"Interval.NonEmpty/x+(-y)" forall x y. addI x (negateI y) = subI x y
"Interval.NonEmpty/(-y)+x" forall x y. addI (negateI y) x = subI x y
"Interval.NonEmpty/x-(-y)" forall x y. subI x (negateI y) = addI x y
#-}
instance (Num a, RoundedRing a) => Num (Interval a) where
(+) = addI
(-) = subI
(*) = mulI
negate = negateI
abs x@(I a b)
| a >= 0 = x
| b <= 0 = negate x
| otherwise = I 0 (max (negate (coerce a)) b)
signum = increasing signum
fromInteger x = case intervalFromInteger x of
(y, y') -> I y y'
{-# INLINE (+) #-}
{-# INLINE (-) #-}
{-# INLINE negate #-}
{-# INLINE (*) #-}
{-# INLINE abs #-}
{-# INLINE signum #-}
{-# INLINE fromInteger #-}
instance (Num a, RoundedFractional a) => Fractional (Interval a) where
recip x = let I a b = normalizeDivisor x
(y, y') = intervalRecip a b
in I y y'
(/) = divI
fromRational x = case intervalFromRational x of
(y, y') -> I y y'
{-# INLINE recip #-}
{-# INLINE (/) #-}
{-# INLINE fromRational #-}
maxI :: Ord a => Interval a -> Interval a -> Interval a
maxI (I a a') (I b b') = I (max a b) (max a' b')
{-# INLINE maxI #-}
minI :: Ord a => Interval a -> Interval a -> Interval a
minI (I a a') (I b b') = I (min a b) (min a' b')
{-# INLINE minI #-}
powInt :: (Ord a, Num a, RoundedRing a) => Interval a -> Int -> Interval a
powInt (I a a') n | odd n || 0 <= a = I (a^n) (a'^n)
| a' <= 0 = I ((coerce (abs a'))^n) ((coerce (abs a))^n)
| otherwise = I 0 (max ((coerce (abs a))^n) (a'^n))
{-# SPECIALIZE powInt :: Interval Float -> Int -> Interval Float #-}
{-# SPECIALIZE powInt :: Interval Double -> Int -> Interval Double #-}
null :: Interval a -> Bool
null _ = False
inf :: Interval a -> Rounded 'TowardNegInf a
inf (I x _) = x
sup :: Interval a -> Rounded 'TowardInf a
sup (I _ y) = y
width :: (Num a, RoundedRing a) => Interval a -> Rounded 'TowardInf a
width (I x y) = y - coerce x
hull :: RoundedRing a => Interval a -> Interval a -> Interval a
hull (I x y) (I x' y') = I (min x x') (max y y')
{-# SPECIALIZE C.expP :: Double -> Interval Double #-}
{-# SPECIALIZE C.expI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.expm1P :: Double -> Interval Double #-}
{-# SPECIALIZE C.expm1I :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.logP :: Double -> Interval Double #-}
{-# SPECIALIZE C.logI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.log1pP :: Double -> Interval Double #-}
{-# SPECIALIZE C.log1pI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.sin_small :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.cos_small :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.sinP :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.cosP :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.sinI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.cosI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.tanI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.atan_small :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.atanP :: Double -> Interval Double #-}
{-# SPECIALIZE C.atanI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.asinP :: Double -> Interval Double #-}
{-# SPECIALIZE C.asinI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.acosP :: Double -> Interval Double #-}
{-# SPECIALIZE C.acosI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.sinhP :: Double -> Interval Double #-}
{-# SPECIALIZE C.sinhI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.coshP :: Double -> Interval Double #-}
{-# SPECIALIZE C.coshI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.tanhP :: Double -> Interval Double #-}
{-# SPECIALIZE C.tanhI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.asinhP :: Double -> Interval Double #-}
{-# SPECIALIZE C.asinhI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.acoshP :: Double -> Interval Double #-}
{-# SPECIALIZE C.acoshI :: Interval Double -> Interval Double #-}
{-# SPECIALIZE C.atanhP :: Double -> Interval Double #-}
{-# SPECIALIZE C.atanhI :: Interval Double -> Interval Double #-}
instance (Num a, RoundedFractional a, RoundedSqrt a, Eq a, RealFloat a, RealFloatConstants a) => Floating (Interval a) where
pi = I pi_down pi_up
exp = C.expI
log = C.logI
sqrt = C.sqrtI
sin = C.sinI
cos = C.cosI
tan = C.tanI
asin = C.asinI
acos = C.acosI
atan = C.atanI
sinh = C.sinhI
cosh = C.coshI
tanh = C.tanhI
asinh = C.asinhI
acosh = C.acoshI
atanh = C.atanhI
log1p = C.log1pI
expm1 = C.expm1I
{-# SPECIALIZE instance Floating (Interval Float) #-}
{-# SPECIALIZE instance Floating (Interval Double) #-}
instance (RealFloat a, RoundedRing a) => C.IsInterval (Interval a) where
type EndPoint (Interval a) = a
makeInterval = I
width = width
withEndPoints f (I x y) = f x y
hull = hull
intersection (I x y) (I x' y') | getRounded x'' <= getRounded y'' = I x'' y''
| otherwise = error "empty intersection"
where x'' = max x x'
y'' = min y y'
maybeIntersection (I x y) (I x' y') | getRounded x'' <= getRounded y'' = Just (I x'' y'')
| otherwise = Nothing
where x'' = max x x'
y'' = min y y'
equalAsSet (I x y) (I x' y') = x == x' && y == y'
subset (I x y) (I x' y') = x' <= x && y <= y'
weaklyLess (I x y) (I x' y') = x <= x' && y <= y'
precedes (I _ y) (I x' _) = getRounded y <= getRounded x'
interior (I x y) (I x' y') = getRounded x' <# getRounded x && getRounded y <# getRounded y'
where s <# t = s < t || (s == t && isInfinite s)
strictLess (I x y) (I x' y') = getRounded x <# getRounded x' && getRounded y <# getRounded y'
where s <# t = s < t || (s == t && isInfinite s)
strictPrecedes (I _ y) (I x' _) = getRounded y < getRounded x'
disjoint (I x y) (I x' y') = getRounded y < getRounded x' || getRounded y' < getRounded x
{-# INLINE makeInterval #-}
{-# INLINE width #-}
{-# INLINE withEndPoints #-}
{-# INLINE hull #-}
{-# INLINE intersection #-}
{-# INLINE maybeIntersection #-}
{-# INLINE equalAsSet #-}
{-# INLINE subset #-}
{-# INLINE weaklyLess #-}
{-# INLINE precedes #-}
{-# INLINE interior #-}
{-# INLINE strictLess #-}
{-# INLINE strictPrecedes #-}
{-# INLINE disjoint #-}
newtype instance VUM.MVector s (Interval a) = MV_Interval (VUM.MVector s (a, a))
newtype instance VU.Vector (Interval a) = V_Interval (VU.Vector (a, a))
intervalToPair :: Fractional a => Interval a -> (a, a)
intervalToPair (I (Rounded x) (Rounded y)) = (x, y)
{-# INLINE intervalToPair #-}
pairToInterval :: Ord a => (a, a) -> Interval a
pairToInterval (x, y) = I (Rounded x) (Rounded y)
{-# INLINE pairToInterval #-}
instance (VU.Unbox a, Ord a, Fractional a) => VGM.MVector VUM.MVector (Interval a) where
basicLength (MV_Interval mv) = VGM.basicLength mv
basicUnsafeSlice i l (MV_Interval mv) = MV_Interval (VGM.basicUnsafeSlice i l mv)
basicOverlaps (MV_Interval mv) (MV_Interval mv') = VGM.basicOverlaps mv mv'
basicUnsafeNew l = MV_Interval <$> VGM.basicUnsafeNew l
basicInitialize (MV_Interval mv) = VGM.basicInitialize mv
basicUnsafeReplicate i x = MV_Interval <$> VGM.basicUnsafeReplicate i (intervalToPair x)
basicUnsafeRead (MV_Interval mv) i = pairToInterval <$> VGM.basicUnsafeRead mv i
basicUnsafeWrite (MV_Interval mv) i x = VGM.basicUnsafeWrite mv i (intervalToPair x)
basicClear (MV_Interval mv) = VGM.basicClear mv
basicSet (MV_Interval mv) x = VGM.basicSet mv (intervalToPair x)
basicUnsafeCopy (MV_Interval mv) (MV_Interval mv') = VGM.basicUnsafeCopy mv mv'
basicUnsafeMove (MV_Interval mv) (MV_Interval mv') = VGM.basicUnsafeMove mv mv'
basicUnsafeGrow (MV_Interval mv) n = MV_Interval <$> VGM.basicUnsafeGrow mv n
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicOverlaps #-}
{-# INLINE basicUnsafeNew #-}
{-# INLINE basicInitialize #-}
{-# INLINE basicUnsafeReplicate #-}
{-# INLINE basicUnsafeRead #-}
{-# INLINE basicUnsafeWrite #-}
{-# INLINE basicClear #-}
{-# INLINE basicSet #-}
{-# INLINE basicUnsafeCopy #-}
{-# INLINE basicUnsafeMove #-}
{-# INLINE basicUnsafeGrow #-}
instance (VU.Unbox a, Ord a, Fractional a) => VG.Vector VU.Vector (Interval a) where
basicUnsafeFreeze (MV_Interval mv) = V_Interval <$> VG.basicUnsafeFreeze mv
basicUnsafeThaw (V_Interval v) = MV_Interval <$> VG.basicUnsafeThaw v
basicLength (V_Interval v) = VG.basicLength v
basicUnsafeSlice i l (V_Interval v) = V_Interval (VG.basicUnsafeSlice i l v)
basicUnsafeIndexM (V_Interval v) i = pairToInterval <$> VG.basicUnsafeIndexM v i
basicUnsafeCopy (MV_Interval mv) (V_Interval v) = VG.basicUnsafeCopy mv v
elemseq (V_Interval _) x y = x `seq` y
{-# INLINE basicUnsafeFreeze #-}
{-# INLINE basicUnsafeThaw #-}
{-# INLINE basicLength #-}
{-# INLINE basicUnsafeSlice #-}
{-# INLINE basicUnsafeIndexM #-}
{-# INLINE basicUnsafeCopy #-}
{-# INLINE elemseq #-}
instance (VU.Unbox a, Ord a, Fractional a) => VU.Unbox (Interval a)
instance (Prim a, Ord a, Fractional a) => A.MArray (A.STUArray s) (Interval a) (ST s) where
getBounds (A.STUArray l u _ _) = return (l, u)
getNumElements (A.STUArray _ _ n _) = return n
unsafeNewArray_ = A.newArray_
newArray_ bounds@(l,u) = do
let n = rangeSize bounds
arr@(MutableByteArray arr_) <- newByteArray (2 * sizeOf (undefined :: a) * n)
setByteArray arr 0 (2 * n) (0 :: a)
return (A.STUArray l u n arr_)
unsafeRead (A.STUArray _ _ _ byteArr) i = do
x <- readByteArray (MutableByteArray byteArr) (2 * i)
y <- readByteArray (MutableByteArray byteArr) (2 * i + 1)
return (pairToInterval (x, y))
unsafeWrite (A.STUArray _ _ _ byteArr) i e = do
let (x, y) = intervalToPair e
writeByteArray (MutableByteArray byteArr) (2 * i) x
writeByteArray (MutableByteArray byteArr) (2 * i + 1) y
instance (Prim a, Ord a, Fractional a) => A.IArray A.UArray (Interval a) where
bounds (A.UArray l u _ _) = (l,u)
numElements (A.UArray _ _ n _) = n
unsafeArray bounds el = runST $ do
marr <- A.newArray_ bounds
forM_ el $ \(i,e) -> A.unsafeWrite marr i e
A.unsafeFreezeSTUArray marr
unsafeAt (A.UArray _ _ _ byteArr) i =
let x = indexByteArray (ByteArray byteArr) (2 * i)
y = indexByteArray (ByteArray byteArr) (2 * i + 1)
in pairToInterval (x, y)