module Data.IntegerInterval
(
IntegerInterval
, module Data.ExtendedReal
, interval
, (<=..<=)
, (<..<=)
, (<=..<)
, (<..<)
, whole
, empty
, singleton
, null
, member
, notMember
, isSubsetOf
, isProperSubsetOf
, lowerBound
, upperBound
, lowerBound'
, upperBound'
, width
, (<!), (<=!), (==!), (>=!), (>!), (/=!)
, (<?), (<=?), (==?), (>=?), (>?), (/=?)
, (<??), (<=??), (==??), (>=??), (>??), (/=??)
, intersection
, intersections
, hull
, hulls
, pickup
, simplestIntegerWithin
, toInterval
, fromInterval
, fromIntervalOver
, fromIntervalUnder
) where
import Algebra.Lattice
import Control.DeepSeq
import Control.Exception (assert)
import Control.Monad hiding (join)
import Data.Data
import Data.ExtendedReal
import Data.Hashable
import Data.List hiding (null)
import Data.Maybe
import Prelude hiding (null)
import qualified Data.Interval as Interval
infix 5 <=..<=
infix 5 <..<=
infix 5 <=..<
infix 5 <..<
infix 4 <!
infix 4 <=!
infix 4 ==!
infix 4 >=!
infix 4 >!
infix 4 /=!
infix 4 <?
infix 4 <=?
infix 4 ==?
infix 4 >=?
infix 4 >?
infix 4 /=?
infix 4 <??
infix 4 <=??
infix 4 ==??
infix 4 >=??
infix 4 >??
infix 4 /=??
data IntegerInterval = Interval !(Extended Integer) !(Extended Integer)
deriving (Eq, Typeable)
lowerBound :: IntegerInterval -> Extended Integer
lowerBound (Interval lb _) = lb
upperBound :: IntegerInterval -> Extended Integer
upperBound (Interval _ ub) = ub
lowerBound' :: IntegerInterval -> (Extended Integer, Bool)
lowerBound' (Interval lb@(Finite _) _) = (lb, True)
lowerBound' (Interval lb _) = (lb, False)
upperBound' :: IntegerInterval -> (Extended Integer, Bool)
upperBound' (Interval _ ub@(Finite _)) = (ub, True)
upperBound' (Interval _ ub) = (ub, False)
instance NFData IntegerInterval where
rnf (Interval lb ub) = rnf lb `seq` rnf ub
instance Hashable IntegerInterval where
hashWithSalt s (Interval lb ub) = s `hashWithSalt` lb `hashWithSalt` ub
instance JoinSemiLattice IntegerInterval where
join = hull
instance MeetSemiLattice IntegerInterval where
meet = intersection
instance Lattice IntegerInterval
instance BoundedJoinSemiLattice IntegerInterval where
bottom = empty
instance BoundedMeetSemiLattice IntegerInterval where
top = whole
instance BoundedLattice IntegerInterval
instance Show IntegerInterval where
showsPrec _ x | null x = showString "empty"
showsPrec p x = showParen (p > appPrec) $
showString "interval " .
showsPrec (appPrec+1) (lowerBound' x) .
showChar ' ' .
showsPrec (appPrec+1) (upperBound' x)
instance Read IntegerInterval where
readsPrec p r =
(readParen (p > appPrec) $ \s0 -> do
("interval",s1) <- lex s0
(lb,s2) <- readsPrec (appPrec+1) s1
(ub,s3) <- readsPrec (appPrec+1) s2
return (interval lb ub, s3)) r
++
(do ("empty", s) <- lex r
return (empty, s))
instance Data IntegerInterval where
gfoldl k z x = z (<=..<=) `k` lowerBound x `k` upperBound x
toConstr _ = error "toConstr"
gunfold _ _ = error "gunfold"
dataTypeOf _ = mkNoRepType "Data.IntegerInterval"
interval
:: (Extended Integer, Bool)
-> (Extended Integer, Bool)
-> IntegerInterval
interval (x1,in1) (x2,in2) =
(if in1 then x1 else x1 + 1) <=..<= (if in2 then x2 else x2 1)
(<=..<=)
:: Extended Integer
-> Extended Integer
-> IntegerInterval
(<=..<=) PosInf _ = empty
(<=..<=) _ NegInf = empty
(<=..<=) lb ub
| lb <= ub = Interval lb ub
| otherwise = empty
(<..<=)
:: Extended Integer
-> Extended Integer
-> IntegerInterval
(<..<=) lb ub = (lb+1) <=..<= ub
(<=..<)
:: Extended Integer
-> Extended Integer
-> IntegerInterval
(<=..<) lb ub = lb <=..<= ub1
(<..<)
:: Extended Integer
-> Extended Integer
-> IntegerInterval
(<..<) lb ub = lb+1 <=..<= ub1
whole :: IntegerInterval
whole = Interval NegInf PosInf
empty :: IntegerInterval
empty = Interval PosInf NegInf
singleton :: Integer -> IntegerInterval
singleton x = Finite x <=..<= Finite x
intersection :: IntegerInterval -> IntegerInterval -> IntegerInterval
intersection (Interval l1 u1) (Interval l2 u2) = max l1 l2 <=..<= min u1 u2
intersections :: [IntegerInterval] -> IntegerInterval
intersections xs = foldl' intersection whole xs
hull :: IntegerInterval -> IntegerInterval -> IntegerInterval
hull x1 x2
| null x1 = x2
| null x2 = x1
hull (Interval l1 u1) (Interval l2 u2) = min l1 l2 <=..<= max u1 u2
hulls :: [IntegerInterval] -> IntegerInterval
hulls xs = foldl' hull empty xs
null :: IntegerInterval -> Bool
null (Interval l u) = u < l
isSingleton :: IntegerInterval -> Bool
isSingleton (Interval l u) = l==u
member :: Integer -> IntegerInterval -> Bool
member x (Interval l u) = l <= Finite x && Finite x <= u
notMember :: Integer -> IntegerInterval -> Bool
notMember a i = not $ member a i
isSubsetOf :: IntegerInterval -> IntegerInterval -> Bool
isSubsetOf (Interval lb1 ub1) (Interval lb2 ub2) = lb2 <= lb1 && ub1 <= ub2
isProperSubsetOf :: IntegerInterval -> IntegerInterval -> Bool
isProperSubsetOf i1 i2 = i1 /= i2 && i1 `isSubsetOf` i2
width :: IntegerInterval -> Integer
width x | null x = 0
width (Interval (Finite l) (Finite u)) = u l
width _ = error "Data.IntegerInterval.width: unbounded interval"
pickup :: IntegerInterval -> Maybe Integer
pickup (Interval NegInf PosInf) = Just 0
pickup (Interval (Finite l) _) = Just l
pickup (Interval _ (Finite u)) = Just u
pickup _ = Nothing
simplestIntegerWithin :: IntegerInterval -> Maybe Integer
simplestIntegerWithin i
| null i = Nothing
| 0 <! i = Just $ let Finite x = lowerBound i in x
| i <! 0 = Just $ let Finite x = upperBound i in x
| otherwise = assert (0 `member` i) $ Just $ 0
(<!) :: IntegerInterval -> IntegerInterval -> Bool
a <! b = a+1 <=! b
(<=!) :: IntegerInterval -> IntegerInterval -> Bool
a <=! b = upperBound a <= lowerBound b
(==!) :: IntegerInterval -> IntegerInterval -> Bool
a ==! b = a <=! b && a >=! b
(/=!) :: IntegerInterval -> IntegerInterval -> Bool
a /=! b = null $ a `intersection` b
(>=!) :: IntegerInterval -> IntegerInterval -> Bool
(>=!) = flip (<=!)
(>!) :: IntegerInterval -> IntegerInterval -> Bool
(>!) = flip (<!)
(<?) :: IntegerInterval -> IntegerInterval -> Bool
a <? b = lowerBound a < upperBound b
(<??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer, Integer)
a <?? b = do
(x,y) <- a+1 <=?? b
return (x1,y)
(<=?) :: IntegerInterval -> IntegerInterval -> Bool
a <=? b =
case lb_a `compare` ub_b of
LT -> True
GT -> False
EQ ->
case lb_a of
NegInf -> False
PosInf -> False
Finite _ -> True
where
lb_a = lowerBound a
ub_b = upperBound b
(<=??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer,Integer)
a <=?? b = do
case pickup (intersection a b) of
Just x -> return (x,x)
Nothing -> do
guard $ upperBound a <= lowerBound b
x <- pickup a
y <- pickup b
return (x,y)
(==?) :: IntegerInterval -> IntegerInterval -> Bool
a ==? b = not $ null $ intersection a b
(==??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer,Integer)
a ==?? b = do
x <- pickup (intersection a b)
return (x,x)
(/=?) :: IntegerInterval -> IntegerInterval -> Bool
a /=? b = not (null a) && not (null b) && not (a == b && isSingleton a)
(/=??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer,Integer)
a /=?? b = do
guard $ not $ null a
guard $ not $ null b
guard $ not $ a == b && isSingleton a
if not (isSingleton b)
then f a b
else liftM (\(y,x) -> (x,y)) $ f b a
where
f a b = do
x <- pickup a
y <- msum [pickup (b `intersection` c) | c <- [inf <..< Finite x, Finite x <..< inf]]
return (x,y)
(>=?) :: IntegerInterval -> IntegerInterval -> Bool
(>=?) = flip (<=?)
(>?) :: IntegerInterval -> IntegerInterval -> Bool
(>?) = flip (<?)
(>=??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer, Integer)
(>=??) = flip (<=??)
(>??) :: IntegerInterval -> IntegerInterval -> Maybe (Integer, Integer)
(>??) = flip (<??)
appPrec :: Int
appPrec = 10
scaleInterval :: Integer -> IntegerInterval -> IntegerInterval
scaleInterval _ x | null x = empty
scaleInterval c (Interval lb ub) =
case compare c 0 of
EQ -> singleton 0
LT -> Finite c * ub <=..<= Finite c * lb
GT -> Finite c * lb <=..<= Finite c * ub
instance Num IntegerInterval where
a + b | null a || null b = empty
Interval lb1 ub1 + Interval lb2 ub2 = lb1 + lb2 <=..<= ub1 + ub2
negate a = scaleInterval (1) a
fromInteger i = singleton (fromInteger i)
abs x = ((x `intersection` nonneg) `hull` (negate x `intersection` nonneg))
where
nonneg = 0 <=..< inf
signum x = zero `hull` pos `hull` neg
where
zero = if member 0 x then singleton 0 else empty
pos = if null $ (0 <..< inf) `intersection` x
then empty
else singleton 1
neg = if null $ (inf <..< 0) `intersection` x
then empty
else singleton (1)
a * b | null a || null b = empty
Interval lb1 ub1 * Interval lb2 ub2 = minimum xs <=..<= maximum xs
where
xs = [ mul x1 x2 | x1 <- [lb1, ub1], x2 <- [lb2, ub2] ]
mul :: Extended Integer -> Extended Integer -> Extended Integer
mul 0 _ = 0
mul _ 0 = 0
mul x1 x2 = x1*x2
toInterval :: Real r => IntegerInterval -> Interval.Interval r
toInterval (Interval l u) = fmap fromInteger l Interval.<=..<= fmap fromInteger u
fromInterval :: Interval.Interval Integer -> IntegerInterval
fromInterval i = (if in1 then x1 else x1 + 1) <=..<= (if in2 then x2 else x2 1)
where
(x1,in1) = Interval.lowerBound' i
(x2,in2) = Interval.upperBound' i
fromIntervalOver :: RealFrac r => Interval.Interval r -> IntegerInterval
fromIntervalOver i = fmap floor lb <=..<= fmap ceiling ub
where
lb = Interval.lowerBound i
ub = Interval.upperBound i
fromIntervalUnder :: RealFrac r => Interval.Interval r -> IntegerInterval
fromIntervalUnder i = fmap f lb <=..<= fmap g ub
where
lb = Interval.lowerBound i
ub = Interval.upperBound i
f x = if fromIntegral y `Interval.member` i then y else y+1
where
y = ceiling x
g x = if fromIntegral y `Interval.member` i then y else y1
where
y = floor x