```{-# LANGUAGE BangPatterns #-}
fromBallA, fromBall, make, makeA,
below, contains, includes,
intersectA, intersect,
neg, add, mul, sub, div, sqrt, exp, log,
compareI, maxI, minI,
toString)
where

import qualified Data.Number.Ball as B

import Data.Order

import Prelude hiding (div, sqrt, exp, log)
import Data.Word(Word)

-- | A wrapper around Ball allowing the results of operations like division
-- by interval containing zero to be represented and do not cause errors.
--
-- Nothing represents undefined interval.
type Interval = Maybe B.Ball

data Inclusion = Above | Below | NoInclusion

-- | Make an interval from a ball and normalize it to specified precision.
fromBallA    :: D.Precision -> B.Ball -> Interval
fromBallA p b = Just (B.normalizeBall p b)

-- | Just make an interval from a ball.
fromBall :: B.Ball -> Interval
fromBall b = Just b

-- | Make an interval from two endpoints.
makeA       :: D.Precision -- ^ precision of the interval's center
-> D.Dyadic -- ^ left endpoint
-> D.Dyadic -- ^ right endpoint
-> Interval
makeA p l u = Just (B.makeA p l u)

-- | Make an interval from two endpoints so that no precision is lost.
make l u = Just (B.make l u)

-- | Checks if second interval is inside the first. _|_ is above all.
below                            :: Interval -> Interval -> Bool
below (Just b) (Just b') = B.below b b'
below Nothing _                = True
below _ Nothing                = False

-- | Checks if interval contains dyadic. _|_ contains everything.
contains                :: Interval -> D.Dyadic -> Bool
contains Nothing _    = True
contains (Just b) d = B.contains b d

-- | Returns Below if second interval is inside first, Above if converse, NoInclusion otherwise.
includes                   :: Interval -> Interval -> Inclusion
includes i i' | below i i' = Below
| below i' i = Above
| otherwise  = NoInclusion

-- | Return the intersection of two intervals. The resulting interval's center has specified precision.
--
--  If one of the intervals is _|_ then just return the other (even if it is _|_).
intersectA                      :: D.Precision -> Interval -> Interval -> Interval
intersectA p (Just b) (Just b') = B.intersectA p b b'
intersectA _ Nothing i          = i
intersectA _ i Nothing          = i

-- | Return the intersection of two intervals so that no precision is lost.
intersect                    :: Interval -> Interval -> Interval
intersect (Just b) (Just b') = B.intersect b b'
intersect Nothing x          = x
intersect x Nothing          = x

-- | Negate the interval. neg _|_ = _|_.
neg     :: D.Precision -> Interval -> Interval
neg p i = do b <- i
return \$! B.neg p b

{-# INLINE wrap #-}
wrap :: (D.Precision -> B.Ball -> B.Ball -> B.Ball)
-> D.Precision -> Interval -> Interval -> Interval
wrap f p i i' = do !b <- i
!b' <- i'
return \$! f p b b'

-- | Addition. If one of the arguments is _|_, so is the result.
add :: D.Precision -> Interval -> Interval -> Interval

-- | Multiplication. If one of the arguments is _|_, so is the result
mul :: D.Precision -> Interval -> Interval -> Interval
mul = wrap B.mul

-- | Subtraction. If one of the arguments is _|_, so is the result
sub :: D.Precision -> Interval -> Interval -> Interval
sub = wrap B.sub

-- | Division. If one of the arguments is _|_ or divisor contains 0 then result is _|_.
div          :: D.Precision -> Interval -> Interval -> Interval
div p i i' = do  !b <- i
!b' <- i'
B.div p b b'
-- | Square root. If one argument is _|_ or interval contains 0 then result is _|_.
sqrt     :: D.Precision -> Interval -> Interval
sqrt p i = do !b <- i
B.sqrt p b

-- | Natural logarithm. If one argument is _|_ or interval contains 0 then result is _|_.
log     :: D.Precision -> Interval -> Interval
log p i = do !b <- i
B.log p b

-- | @ e ^ i @ If argument is _|_ so is the result.
exp     :: D.Precision -> Interval -> Interval
exp p i = do !b <- i
return \$! (B.exp p b)

-- | Compare two intervals. If one of them is _|_ the result is incomparable,
-- otherwise result is comparison of balls.
compareI                      :: Interval -> Interval -> POrdering
compareI (Just b) (Just b')   = B.compareB b b'
compareI _ _                  = Incomparable

-- | Maximum of intervals. If one interval is _|_ so is the result.
maxI :: D.Precision -> Interval -> Interval -> Interval
maxI = wrap B.maxB

-- | Similar to maxI.
minI :: D.Precision -> Interval -> Interval -> Interval
minI = wrap B.minB

-- | Center of interval. Center on _|_ will result in fail.
center Nothing             = fail "center of _|_ is not defined"
center (Just (B.Ball c _)) = return c

-- | Radius of interval. Radius on _|_ will result in fail.
radius (Just (B.Ball _ r)) = return r

-- | Lower endpoint of interval with precision of the center.
-- Lower on _|_ will result in fail.
lower Nothing  = fail "lower bound of _|_ is -infinity"
lower (Just b) = return (B.lower_ b)

-- | Upper endpoint of interval with precision of the center.
-- Upper on _|_ will result in fail.
upper Nothing  = fail "upper bound of _|_ is +infinity"
upper (Just b) = return (B.upper_ b)

-- | Width of the interval. Widht on _|_ will result in fail.
width Nothing  = fail "width of _|_ is infinity"
width (Just b) = return (B.width b)

fromString     :: D.Precision -> String -> Interval
fromString p s = Just (B.fromString p s)

fromInt     :: D.Precision -> Int -> Interval
fromInt p d = Just (B.fromInt p d)

fromWord     :: D.Precision -> Word -> Interval
fromWord p d = Just (B.fromWord p d)

toString          :: Interval -> String
toString Nothing  = "_|_"
toString (Just b) = show b
```