module Language.Boogie.Intervals where
import Data.Ratio
import Data.Maybe
class Eq a => Lattice a where
top :: a
bot :: a
(<:) :: a -> a -> Bool
join :: a -> a -> a
meet :: a -> a -> a
x <: y = meet x y == x
data Extended = NegInf | Finite Integer | Inf
deriving Eq
instance Show Extended where
show NegInf = "-Infinity"
show (Finite i) = show i
show Inf = "Infinity"
instance Num Extended where
Inf + NegInf = error ("Cannot add " ++ show Inf ++ " and " ++ show NegInf)
NegInf + Inf = error ("Cannot add " ++ show NegInf ++ " and " ++ show Inf)
Inf + _ = Inf
NegInf + _ = NegInf
Finite _ + Inf = Inf
Finite _ + NegInf = NegInf
Finite i + Finite j = Finite (i + j)
Finite 0 * _ = Finite 0
_ * Finite 0 = Finite 0
e1 * e2 | signum (e1) == 1 && signum (e2) == 1 = negate e1 * negate e2
e1 * e2 | signum (e1) == 1 = negate $ negate e1 * e2
e1 * e2 | signum (e2) == 1 = negate $ e1 * negate e2
Inf * _ = Inf
_ * Inf = Inf
Finite i * Finite j = Finite (i * j)
negate Inf = NegInf
negate NegInf = Inf
negate (Finite i) = Finite (i)
abs Inf = Inf
abs NegInf = Inf
abs (Finite i) = Finite (abs i)
signum Inf = 1
signum NegInf = 1
signum (Finite i) = Finite (signum i)
fromInteger i = Finite i
extDiv :: (Ratio Integer -> Integer) -> Extended -> Extended -> Maybe Extended
extDiv r (Finite i) (Finite j) = Just $ Finite (r (i % j))
extDiv _ Inf (Finite j) = Just $ signum (Finite j) * Inf
extDiv _ NegInf (Finite j) = Just $ signum (Finite j) * NegInf
extDiv _ (Finite i) Inf = Just $ 0
extDiv _ (Finite i) NegInf = Just $ 0
extDiv _ _ _ = Nothing
instance Ord Extended where
NegInf <= b = True
b <= NegInf = False
b <= Inf = True
Inf <= b = False
Finite x <= Finite y = x <= y
data Interval = Interval {
lower :: Extended,
upper :: Extended
}
isBottom (Interval l u) = l > u
isBounded (Interval (Finite l) (Finite u)) = True
isBounded _ = False
positives = Interval 1 Inf
negatives = Interval NegInf (1)
nonNegatives = Interval 0 Inf
nonPositives = Interval NegInf 0
mapBounds f (Interval l1 u1) (Interval l2 u2) = [f b1 b2 | b1 <- [l1, u1], b2 <- [l2, u2]]
instance Show Interval where
show int | isBottom int = "[]"
show (Interval l u) = "[" ++ show l ++ ".." ++ show u ++ "]"
instance Eq Interval where
int1 == int2 | isBottom int1, isBottom int2 = True
Interval l1 u1 == Interval l2 u2 = l1 == l2 && u1 == u2
instance Lattice Interval where
top = Interval NegInf Inf
bot = Interval Inf NegInf
join int1 int2 | isBottom int1 = int2
join int1 int2 | isBottom int2 = int1
join (Interval l1 u1) (Interval l2 u2) = Interval (min l1 l2) (max u1 u2)
meet int1 int2 | isBottom int1 = int1
meet int1 int2 | isBottom int2 = int2
meet (Interval l1 u1) (Interval l2 u2) = Interval (max l1 l2) (min u1 u2)
instance Num Interval where
int1 + int2 | isBottom int1 || isBottom int2 = bot
Interval l1 u1 + Interval l2 u2 = Interval (l1 + l2) (u1 + u2)
int1 * int2 | isBottom int1 || isBottom int2 = bot
| otherwise = Interval (minimum (mapBounds (*) int1 int2)) (maximum (mapBounds (*) int1 int2))
negate (Interval l u) = Interval (u) (l)
abs int = int
signum _ = 1
fromInteger n = Interval (Finite n) (Finite n)
(//) int1 int2 | isBottom int1 || isBottom int2 = bot
| 0 <: int2 = join (int1 // meet int2 negatives) (int1 // meet int2 positives)
| otherwise = Interval (minimum (catMaybes (mapBounds (extDiv ceiling) int1 int2))) (maximum (catMaybes (mapBounds (extDiv floor) int1 int2)))