cryptol-2.2.0: src/Cryptol/TypeCheck/Solver/Interval.hs
-- |
-- Module : $Header$
-- Copyright : (c) 2013-2015 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
--
-- This module defines intervals and interval arithmetic.
{-# LANGUAGE Safe #-}
module Cryptol.TypeCheck.Solver.Interval
( Interval(..)
, anything
, iConst
, iAdd, iMul, iExp
, iMin, iMax
, iLg2, iWidth
, iSub, iDiv, iMod
, iLenFromThen, iLenFromTo, iLenFromThenTo
, iLeq, iLt, iDisjoint
) where
import Cryptol.TypeCheck.Solver.InfNat
{- | Representation of intervals.
Intervals always include the lower bound.
Intervals include the upper bound if:
* either the upper bound is finite, or
* the upper bound is 'Inf' and @isFinite == False@.
Invariant: if the upper bound is finite, then `isFinite == True`.
> [x,y] Interval (Nat x) (Nat y) True
> [x,inf] Interval (Nat x) Inf False
> [x,inf) Interval (Nat x) Inf True
-}
data Interval = Interval
{ lowerBound :: Nat' -- ^ Lower bound
, upperBound :: Nat' -- ^ Upper bound
, isFinite :: Bool -- ^ Do we know this to be a finite value.
-- Note that for @[inf,inf]@ this field is `False`
-- (i.e., this field is not talking about the size of the interval,
-- but, rather, about if it contains infinity).
} deriving Show
-- | Any possible value.
anything :: Interval
anything = Interval { lowerBound = Nat 0
, upperBound = Inf
, isFinite = False
}
anyFinite :: Interval
anyFinite = anything { isFinite = True }
iConst :: Nat' -> Interval
iConst x = Interval { lowerBound = x, upperBound = x, isFinite = x < Inf }
iAdd :: Interval -> Interval -> Interval
iAdd = liftMono2 nAdd (&&)
iMul :: Interval -> Interval -> Interval
iMul = liftMono2 nMul (&&)
iMin :: Interval -> Interval -> Interval
iMin = liftMono2 nMin (||)
iMax :: Interval -> Interval -> Interval
iMax = liftMono2 nMax (&&)
iLg2 :: Interval -> Interval
iLg2 = liftMono1 nLg2
iWidth :: Interval -> Interval
iWidth = liftMono1 nWidth
iExp :: Interval -> Interval -> Interval
iExp i1 i2 = fixUp (liftMono2 nExp (&&) i1 i2)
where
-- exp k : is a monotonic function for k >= 1
-- exp 0 : is a monotonic from 1 onwards
-- Example of why we need fixing, consdier:
-- [0,0] ^ [0,5]
-- Monotonic computation results in:
-- [1,0]
fixUp i3
| lowerBound i1 == Nat 0 &&
lowerBound i2 == Nat 0 &&
upperBound i2 >= Nat 1 =
Interval { lowerBound = Nat 0
, upperBound = nMax (Nat 1) (upperBound i3)
, isFinite = isFinite i3
}
fixUp i3 = i3
iSub :: Interval -> Interval -> Interval
iSub = liftPosNeg nSub
iDiv :: Interval -> Interval -> Interval
iDiv = liftPosNeg nDiv
iMod :: Interval -> Interval -> Interval
iMod _ i2 = Interval { lowerBound = Nat 0
, upperBound = case upperBound i2 of
Inf -> Inf
Nat n -> Nat (n - 1)
, isFinite = True -- we never have infinite reminder.
}
-- XXX
iLenFromThen :: Interval -> Interval -> Interval -> Interval
iLenFromThen _ _ _ = anyFinite
-- XXX
iLenFromTo :: Interval -> Interval -> Interval
iLenFromTo _ _ = anyFinite
-- XXX
iLenFromThenTo :: Interval -> Interval -> Interval -> Interval
iLenFromThenTo _ _ _ = anyFinite
-- | The first interval is definiately smaller
iLeq :: Interval -> Interval -> Bool
iLeq i1 i2 = upperBound i1 <= lowerBound i2
-- | The first interval is definiately smaller
iLt :: Interval -> Interval -> Bool
iLt i1 i2 = upperBound i1 < lowerBound i2
|| (isFinite i1 && lowerBound i2 == Inf)
-- | The two intervals do not overlap.
iDisjoint :: Interval -> Interval -> Bool
iDisjoint i1 i2 = iLt i1 i2 || iLt i2 i1
--------------------------------------------------------------------------------
liftMono1 :: (Nat' -> Nat') -- ^ Binary monotonic fun. to lift
-> Interval -> Interval
liftMono1 f i =
let u = f (upperBound i)
in Interval { lowerBound = f (lowerBound i)
, upperBound = u
, isFinite = mkFin (isFinite i) u
}
liftMono2 :: (Nat' -> Nat' -> Nat') -- ^ Binary monotonic fun. to lift
-> (Bool -> Bool -> Bool) -- ^ Compute finitneness
-> Interval -> Interval -> Interval
liftMono2 f isF i1 i2 =
let u = f (upperBound i1) (upperBound i2)
in Interval { lowerBound = f (lowerBound i1) (lowerBound i2)
, upperBound = u
, isFinite = mkFin (isF (isFinite i1) (isFinite i2)) u
}
-- For div and sub, increase in first argument, decrease in second.
liftPosNeg :: (Nat' -> Nat' -> Maybe Nat')
-> Interval -> Interval -> Interval
liftPosNeg f i1 i2 =
Interval { lowerBound = case f (lowerBound i1) (upperBound i2) of
Nothing -> Nat 0
Just n -> n
, upperBound = case f (upperBound i1) (lowerBound i2) of
Just n -> n
Nothing -> upperBound i1
, isFinite = isFinite i1
}
mkFin :: Bool -> Nat' -> Bool
mkFin ifInf ub = case ub of
Nat _ -> True
Inf -> ifInf