module Numeric.Bound
(
LowerBound
, UpperBound
, closedLower
, closedUpper
, openLower
, openUpper
, infiniteLower
, infiniteUpper
, adjacentLower
, adjacentUpper
, mergeable
, isAbove
, isBelow
, isSingleton
, isInfiniteLower
, isInfiniteUpper
, isPositiveLower
, isPositiveUpper
, isNegativeLower
, isNegativeUpper
, isZeroLower
, isZeroUpper
, containsLower
, containsUpper
, elems
, minValue
, maxValue
, Result(..)
, applyResult
, plusLower
, plusUpper
, minusLower
, minusUpper
, timesLower
, timesUpper
, timesMixed
, divLower
, divUpper
, divLowerUpper
, divUpperLower
, recipLower
, recipUpper
, negateLower
, negateUpper
, prettyLower
, prettyUpper
) where
import Numeric.Distance (Dist, adjacent, shift)
data Bound a
= Open a
| Closed a
| Infinite
deriving (Eq, Show)
data Result a
= Lower (LowerBound a)
| Upper (UpperBound a)
| Both (LowerBound a) (UpperBound a)
deriving (Eq, Show)
newtype LowerBound a = LowerBound { unLowerBound :: Bound a }
deriving (Eq, Show)
instance Ord a => Ord (LowerBound a) where
compare (LowerBound x) (LowerBound y) =
case (x,y) of
(Open a, Closed b) | a >= b -> GT
| otherwise -> LT
(Closed a, Open b) | a <= b -> LT
| otherwise -> GT
(Open a, Open b) -> compare a b
(Closed a, Closed b) -> compare a b
(Infinite, Infinite) -> EQ
(_, Infinite) -> GT
(Infinite, _) -> LT
newtype UpperBound a = UpperBound { unUpperBound :: Bound a }
deriving (Eq, Show)
instance Ord a => Ord (UpperBound a) where
compare (UpperBound x) (UpperBound y) =
case (x,y) of
(Open a, Closed b) | a <= b -> LT
| otherwise -> GT
(Closed a, Open b) | a >= b -> GT
| otherwise -> LT
(Open a, Open b) -> compare a b
(Closed a, Closed b) -> compare a b
(Infinite, Infinite) -> EQ
(_, Infinite) -> LT
(Infinite, _) -> GT
openLower :: Dist a => a -> LowerBound a
openLower = closeLower . LowerBound . Open
closedLower :: a -> LowerBound a
closedLower = LowerBound . Closed
infiniteLower :: LowerBound a
infiniteLower = LowerBound Infinite
openUpper :: Dist a => a -> UpperBound a
openUpper = closeUpper . UpperBound . Open
closedUpper :: a -> UpperBound a
closedUpper = UpperBound . Closed
infiniteUpper :: UpperBound a
infiniteUpper = UpperBound Infinite
closeLower :: Dist a => LowerBound a -> LowerBound a
closeLower b@(LowerBound (Open a)) =
case shift 1 a of
Just sa -> closedLower sa
Nothing -> b
closeLower b = b
closeUpper :: Dist a => UpperBound a -> UpperBound a
closeUpper b@(UpperBound (Open a)) =
case shift (-1) a of
Just sa -> closedUpper sa
Nothing -> b
closeUpper b = b
containsLower :: Ord a => a -> LowerBound a -> Bool
containsLower value bound =
case unLowerBound bound of
Open a -> value > a
Closed a -> value >= a
Infinite -> True
containsUpper :: Ord a => a -> UpperBound a -> Bool
containsUpper value bound =
case unUpperBound bound of
Open a -> value < a
Closed a -> value <= a
Infinite -> True
isInfiniteLower :: LowerBound a -> Bool
isInfiniteLower (LowerBound Infinite) = True
isInfiniteLower _ = False
isInfiniteUpper :: UpperBound a -> Bool
isInfiniteUpper (UpperBound Infinite) = True
isInfiniteUpper _ = False
isNegativeLower :: (Num a, Ord a) => LowerBound a -> Bool
isNegativeLower bound =
case unLowerBound bound of
Open a -> a < 0
Closed a -> a < 0
Infinite -> True
isNegativeUpper :: (Num a, Ord a) => UpperBound a -> Bool
isNegativeUpper bound =
case unUpperBound bound of
Open a -> a <= 0
Closed a -> a < 0
Infinite -> False
isPositiveLower :: (Num a, Ord a) => LowerBound a -> Bool
isPositiveLower bound =
case unLowerBound bound of
Open a -> a >= 0
Closed a -> a > 0
Infinite -> False
isPositiveUpper :: (Num a, Ord a) => UpperBound a -> Bool
isPositiveUpper bound =
case unUpperBound bound of
Open a -> a > 0
Closed a -> a > 0
Infinite -> True
isClosedZero :: (Eq a, Num a) => Bound a -> Bool
isClosedZero (Closed 0) = True
isClosedZero _ = False
isClosedZeroLower :: (Eq a, Num a) => LowerBound a -> Bool
isClosedZeroLower = isClosedZero . unLowerBound
isClosedZeroUpper :: (Eq a, Num a) => UpperBound a -> Bool
isClosedZeroUpper = isClosedZero . unUpperBound
isOpenZero :: (Eq a, Num a) => Bound a -> Bool
isOpenZero (Open 0) = True
isOpenZero _ = False
isZero :: (Eq a, Num a) => Bound a -> Bool
isZero bound = isClosedZero bound || isOpenZero bound
isZeroLower :: (Eq a, Num a) => LowerBound a -> Bool
isZeroLower = isZero . unLowerBound
isZeroUpper :: (Eq a, Num a) => UpperBound a -> Bool
isZeroUpper = isZero . unUpperBound
isAbove :: Ord a => LowerBound a -> UpperBound a -> Bool
isAbove (LowerBound x) (UpperBound y) =
case (x,y) of
(Open a, Open b) -> a >= b
(Open a, Closed b) -> a >= b
(Closed a, Open b) -> a >= b
(Closed a, Closed b) -> a > b
_ -> False
isBelow :: Ord a => UpperBound a -> LowerBound a -> Bool
isBelow = flip isAbove
isSingleton :: Eq a => LowerBound a -> UpperBound a -> Bool
isSingleton (LowerBound (Closed a)) (UpperBound (Closed b)) = a == b
isSingleton _ _ = False
elems :: Enum a => LowerBound a -> UpperBound a -> Maybe [a]
elems (LowerBound x) (UpperBound y) =
case (x,y) of
(Open a, Open b) -> Just [succ a .. pred b]
(Open a, Closed b) -> Just [succ a .. b]
(Closed a, Open b) -> Just [a .. pred b]
(Closed a, Closed b) -> Just [a .. b]
_ -> Nothing
adjacentLower :: Dist a => UpperBound a -> Maybe (LowerBound a)
adjacentLower bound =
case unUpperBound bound of
Open a -> Just $ LowerBound (Closed a)
Closed a -> Just . closeLower $ LowerBound (Open a)
Infinite -> Nothing
adjacentUpper :: Dist a => LowerBound a -> Maybe (UpperBound a)
adjacentUpper bound =
case unLowerBound bound of
Open a -> Just (closedUpper a)
Closed a -> Just (openUpper a)
Infinite -> Nothing
plusLower :: Num a => LowerBound a -> LowerBound a -> LowerBound a
plusLower (LowerBound x) (LowerBound y) =
LowerBound (apply (+) x y)
plusUpper :: Num a => UpperBound a -> UpperBound a -> UpperBound a
plusUpper (UpperBound x) (UpperBound y) =
UpperBound (apply (+) x y)
minusLower :: Num a => UpperBound a -> LowerBound a -> UpperBound a
minusLower (UpperBound x) (LowerBound y) =
UpperBound (apply (-) x y)
minusUpper :: Num a => LowerBound a -> UpperBound a -> LowerBound a
minusUpper (LowerBound x) (UpperBound y) =
LowerBound (apply (-) x y)
apply :: (a -> a -> a) -> Bound a -> Bound a -> Bound a
apply f x y =
case (x,y) of
(Open a, Open b) -> Open (f a b)
(Open a, Closed b) -> Open (f a b)
(Closed a, Open b) -> Open (f a b)
(Closed a, Closed b) -> Closed (f a b)
_ -> Infinite
timesLower :: (Num a, Ord a) => LowerBound a -> LowerBound a -> Result a
timesLower l@(LowerBound x) r@(LowerBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a * b)
(Open a, Closed b) -> bothOpen (a * b)
(Closed a, Open b) -> bothOpen (a * b)
(Closed a, Closed b) -> bothClosed (a * b)
(Infinite, _) -> inf r
(_, Infinite) -> inf l
where
inf b | isNegativeLower b = Upper infiniteUpper
| isClosedZeroLower b = Both (closedLower 0) (closedUpper 0)
| otherwise = Lower infiniteLower
timesUpper :: (Num a, Ord a) => UpperBound a -> UpperBound a -> Result a
timesUpper l@(UpperBound x) r@(UpperBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a * b)
(Open a, Closed b) -> bothOpen (a * b)
(Closed a, Open b) -> bothOpen (a * b)
(Closed a, Closed b) -> bothClosed (a * b)
(Infinite, _) -> inf r
(_, Infinite) -> inf l
where
inf b | isPositiveUpper b = Upper infiniteUpper
| isClosedZeroUpper b = Both (closedLower 0) (closedUpper 0)
| otherwise = Lower infiniteLower
timesMixed :: (Num a, Ord a) => LowerBound a -> UpperBound a -> Result a
timesMixed l@(LowerBound x) r@(UpperBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a * b)
(Open a, Closed b) -> bothOpen (a * b)
(Closed a, Open b) -> bothOpen (a * b)
(Closed a, Closed b) -> bothClosed (a * b)
(Infinite, _) -> infLower r
(_, Infinite) -> infUpper l
where
infLower b | isPositiveUpper b = Lower infiniteLower
| isClosedZeroUpper b = Both (closedLower 0) (closedUpper 0)
| otherwise = Upper infiniteUpper
infUpper b | isNegativeLower b = Lower infiniteLower
| isClosedZeroLower b = Both (closedLower 0) (closedUpper 0)
| otherwise = Upper infiniteUpper
divLower :: Integral a => LowerBound a -> LowerBound a -> Result a
divLower (LowerBound x) r@(LowerBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a `div` b)
(Open a, Closed b) -> bothOpen (a `div` b)
(Closed a, Open b) -> bothOpen (a `div` b)
(Closed a, Closed b) -> bothClosed (a `div` b)
(Infinite, _) -> inf r
(_, Infinite) -> bothClosed 0
where
inf b | isNegativeLower b = Upper infiniteUpper
| otherwise = Lower infiniteLower
divUpper :: Integral a => UpperBound a -> UpperBound a -> Result a
divUpper (UpperBound x) r@(UpperBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a `div` b)
(Open a, Closed b) -> bothOpen (a `div` b)
(Closed a, Open b) -> bothOpen (a `div` b)
(Closed a, Closed b) -> bothClosed (a `div` b)
(Infinite, _) -> inf r
(_, Infinite) -> bothClosed 0
where
inf b | isNegativeUpper b = Lower infiniteLower
| otherwise = Upper infiniteUpper
divLowerUpper :: Integral a => LowerBound a -> UpperBound a -> Result a
divLowerUpper (LowerBound x) r@(UpperBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a `div` b)
(Open a, Closed b) -> bothOpen (a `div` b)
(Closed a, Open b) -> bothOpen (a `div` b)
(Closed a, Closed b) -> bothClosed (a `div` b)
(Infinite, _) -> inf r
(_, Infinite) -> bothClosed 0
where
inf b | isNegativeUpper b = Upper infiniteUpper
| otherwise = Lower infiniteLower
divUpperLower :: Integral a => UpperBound a -> LowerBound a -> Result a
divUpperLower (UpperBound x) r@(LowerBound y) =
case (x,y) of
(Open a, Open b) -> bothOpen (a `div` b)
(Open a, Closed b) -> bothOpen (a `div` b)
(Closed a, Open b) -> bothOpen (a `div` b)
(Closed a, Closed b) -> bothClosed (a `div` b)
(Infinite, _) -> inf r
(_, Infinite) -> bothClosed 0
where
inf b | isNegativeLower b = Lower infiniteLower
| otherwise = Upper infiniteUpper
bothClosed :: a -> Result a
bothClosed value =
Both (closedLower value) (closedUpper value)
bothOpen :: a -> Result a
bothOpen value =
Both (LowerBound (Open value)) (UpperBound (Open value))
applyResult
:: Ord a
=> (Maybe (LowerBound a), Maybe (UpperBound a))
-> Result a
-> (Maybe (LowerBound a), Maybe (UpperBound a))
applyResult (nowMin,nowMax) result =
case result of
Both low up -> (update min nowMin low, update max nowMax up)
Lower low -> (update min nowMin low, nowMax)
Upper up -> (nowMin, update max nowMax up)
where
update f current new =
Just $
case current of
Just m -> f m new
Nothing -> new
negateBound :: Num a => Bound a -> Bound a
negateBound (Open a) = Open (negate a)
negateBound (Closed a) = Closed (negate a)
negateBound Infinite = Infinite
negateLower :: Num a => LowerBound a -> UpperBound a
negateLower = UpperBound . negateBound . unLowerBound
negateUpper :: Num a => UpperBound a -> LowerBound a
negateUpper = LowerBound . negateBound . unUpperBound
recipBound :: Fractional a => Bound a -> Bound a
recipBound (Open a) = Open (recip a)
recipBound (Closed a) = Closed (recip a)
recipBound Infinite = Closed 0
recipUpper :: Fractional a => UpperBound a -> LowerBound a
recipUpper = LowerBound . recipBound . unUpperBound
recipLower :: Fractional a => LowerBound a -> UpperBound a
recipLower = UpperBound . recipBound . unLowerBound
mergeable :: Dist a => UpperBound a -> LowerBound a -> Bool
mergeable (UpperBound x) (LowerBound y) =
case (x,y) of
(Open a, Closed b) -> a == b
(Closed a, Open b) -> a == b
(Closed a, Closed b) -> a == b || adjacent a b
_ -> False
maxValue :: UpperBound a -> Maybe a
maxValue bound =
case unUpperBound bound of
Closed a -> Just a
_ -> Nothing
minValue :: LowerBound a -> Maybe a
minValue bound =
case unLowerBound bound of
Closed a -> Just a
_ -> Nothing
prettyLower :: Show a => LowerBound a -> String
prettyLower bound =
case unLowerBound bound of
Open a -> "(" ++ show a
Closed a -> "[" ++ show a
Infinite -> "(-\8734"
prettyUpper :: Show a => UpperBound a -> String
prettyUpper bound =
case unUpperBound bound of
Open a -> show a ++ ")"
Closed a -> show a ++ "]"
Infinite -> "\8734)"