{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Data.Delta -- Copyright : (c) Masahiro Sakai 2011-2013 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : provisional -- Portability : non-portable (TypeFamilies) -- -- Augmenting number types with infinitesimal parameter δ. -- -- Reference: -- -- * Bruno Dutertre and Leonardo de Moura, -- \"/A Fast Linear-Arithmetic Solver for DPLL(T)/\", -- Computer Aided Verification In Computer Aided Verification, Vol. 4144 -- (2006), pp. 81-94. -- -- -- ----------------------------------------------------------------------------- module ToySolver.Data.Delta ( -- * The Delta type Delta (..) -- * Construction , fromReal , delta -- * Query , realPart , deltaPart -- * Relationship with integers , floor' , ceiling' , isInteger' ) where import Data.VectorSpace import ToySolver.Internal.Util (isInteger) -- | @Delta r k@ represents r + kδ for symbolic infinitesimal parameter δ. data Delta r = Delta !r !r deriving (Ord, Eq, Show) -- | symbolic infinitesimal parameter δ. delta :: Num r => Delta r delta = Delta 0 1 -- | Conversion from a base @r@ value to @Delta r@. fromReal :: Num r => r -> Delta r fromReal x = Delta x 0 -- | Extracts the real part.. realPart :: Delta r -> r realPart (Delta r _) = r -- | Extracts the δ part.. deltaPart :: Delta r -> r deltaPart (Delta _ k) = k instance Num r => AdditiveGroup (Delta r) where Delta r1 k1 ^+^ Delta r2 k2 = Delta (r1+r2) (k1+k2) zeroV = Delta 0 0 negateV (Delta r k) = Delta (- r) (- k) instance Num r => VectorSpace (Delta r) where type Scalar (Delta r) = r c *^ Delta r k = Delta (c*r) (c*k) -- | This instance assumes the symbolic infinitesimal parameter δ is a nilpotent with δ² = 0. instance (Num r, Ord r) => Num (Delta r) where (+) = (^+^) negate = negateV Delta r1 k1 * Delta r2 k2 = Delta (r1*r2) (r1*k2+r2*k1) abs x = case x `compare` 0 of LT -> negateV x EQ -> x GT -> x signum x = case x `compare` 0 of LT -> -1 EQ -> 0 GT -> 1 fromInteger x = Delta (fromInteger x) 0 -- | This is unsafe instance in the sense that only a proper real can be a divisor. instance (Fractional r, Ord r) => Fractional (Delta r) where Delta r1 k1 / Delta r2 0 = Delta (r1 / r2) (k1 / r2) Delta r1 k1 / Delta r2 k2 = error "Fractional{ToySolver.Data.Delta.Delta}.(/): divisor must be a proper real" fromRational x = Delta (fromRational x) 0 instance (Real r, Eq r) => Real (Delta r) where toRational (Delta r 0) = toRational r toRational (Delta r k) = error "Real{ToySolver.Data.Delta.Delta}.toRational: not a real number" instance (RealFrac r, Eq r) => RealFrac (Delta r) where properFraction x = case x `compare` 0 of LT -> let n = ceiling' x in (n, x - fromIntegral n) EQ -> (0, 0) GT -> let n = floor' x in (n, x - fromIntegral n) ceiling = ceiling' floor = floor' -- | 'Delta' version of 'floor'. -- @'floor'' x@ returns the greatest integer not greater than @x@ floor' :: (RealFrac r, Integral a) => Delta r -> a floor' (Delta r k) = fromInteger $ if r2==r && k < 0 then i-1 else i where i = floor r r2 = fromInteger i -- | 'Delta' version of 'ceiling'. -- @'ceiling'' x@ returns the least integer not less than @x@ ceiling' :: (RealFrac r, Integral a) => Delta r -> a ceiling' (Delta r k) = fromInteger $ if r2==r && k > 0 then i+1 else i where i = ceiling r r2 = fromInteger i -- | Is this a integer? isInteger' :: RealFrac r => Delta r -> Bool isInteger' (Delta r k) = isInteger r && k == 0