module Interval (Ivl, (+/-), lBound, uBound, inIvl) where -- Ivl exported abstractly. We enforce the invariant that for -- any interval "Ivl l u", l <= u by only allowing construction of -- intervals that satisfy the invariant, and by ensuring that -- all operations on intervals preserve the invariant. We also -- enforce that intervals must be bounded. -- See the solution notes for a discussion on a more general approach -- allowing for unbounded intervals and handling of NaNs. data Ivl = Ivl Double Double deriving (Show, Eq) (+/-) :: Double -> Double -> Ivl x +/- e | isNaN x || isNaN e = error "Invalid interval (NaN)" | isInfinite x || isInfinite e = error "Unbounded interval" | e < 0 = error "Negative error bound" | otherwise = Ivl (x - e) (x + e) lBound :: Ivl -> Double lBound (Ivl l _) = l uBound :: Ivl -> Double uBound (Ivl _ u) = u inIvl :: Double -> Ivl -> Bool x `inIvl` (Ivl l u) = l <= x && x <= u instance Num Ivl where (Ivl lx ux) + (Ivl ly uy) = Ivl (lx + ly) (ux + uy) (Ivl lx ux) - (Ivl ly uy) = Ivl (lx - uy) (ux - ly) -- Broken (*) (Ivl lx ux) * (Ivl ly uy) = Ivl (minimum bs) (maximum bs) where bs = [lx * ly, lx * uy, ux * lx, ux * uy] {- (Ivl lx ux) * (Ivl ly uy) = Ivl (minimum bs) (maximum bs) where bs = [lx * ly, lx * uy, ux * ly, ux * uy] -} -- Broken abs abs ivl@(Ivl l u) = Ivl (min al au) (max al au) where al = abs l au = abs u {- -- Broken abs 2 -- abs _ = Ivl ((-1.0)/0) (1/0) abs _ = Ivl (-10000.0) (10000.0) -} {- abs ivl@(Ivl l u) | l >= 0 = ivl | u < 0 = Ivl (-u) (-l) | otherwise = Ivl 0 (max (abs l) (abs u)) -} signum (Ivl l u) = Ivl (signum l) (signum u) -- A version that spells out the different cases. Equivalent to the above. -- Might be helpful to see what is going on. -- signum (Ivl lx ux) | lx > 0 = 1 -- | ux < 0 = -1 -- | lx == 0 && ux == 0 = 0 -- | lx == 0 && ux > 0 = Ivl 0 1 -- | lx < 0 && ux == 0 = Ivl (-1) 0 -- | otherwise = Ivl (-1) 1 fromInteger x = Ivl (fromInteger x) (fromInteger x) -- We opt to disallow division by intervals including 0. See the solution -- notes for a discussion on a more general approach. instance Fractional Ivl where (Ivl lx ux) / (Ivl ly uy) | uy < 0 || ly > 0 = Ivl (minimum bs) (maximum bs) | otherwise = error "Division by zero" where bs = [lx / ly, lx / uy, ux / ly, ux / uy] recip (Ivl lx ux) | ux < 0 || lx > 0 = Ivl (1/ux) (1/lx) | otherwise = error "Division by zero" fromRational x = Ivl (fromRational x) (fromRational x)