packages feed

co-feldspar-0.1.0.1: src/Feldspar/Verify/Arithmetic.hs

{-# language ScopedTypeVariables #-}

module Feldspar.Verify.Arithmetic where

import Data.Typeable

import Feldspar.Verify.Monad hiding (ite)
import Feldspar.Verify.SMT
import qualified Feldspar.Verify.SMT as SMT

--------------------------------------------------------------------------------
-- Wrapper around simple-SMT types for signed and unsigned arithmetic.
--
-- https://github.com/nick8325/imperative-edsl/blob/master/src/Language/Embedded/Verify/Arithmetic.hs
--------------------------------------------------------------------------------

data W8
data W16
data W32
data W64

class Width w where width :: Num a => w -> a
instance Width W8  where width _ = 8
instance Width W16 where width _ = 16
instance Width W32 where width _ = 32
instance Width W64 where width _ = 64

--------------------------------------------------------------------------------

data Signed
data Unsigned

class Sign s where isSigned :: BV s w -> Bool
instance Sign Signed   where isSigned _ = True
instance Sign Unsigned where isSigned _ = False

--------------------------------------------------------------------------------

newtype BV s w = BV { unBV :: SExpr }
  deriving (Eq, Ord, Typeable, Show)

instance (Sign s, Width w) => Num (BV s w) where
  fromInteger n = BV (bits (width (undefined :: w)) n)
  x + y | x == 0 = y
  x + y | y == 0 = x
  BV x + BV y = BV (bvAdd x y)
  x - y | y == 0 = x
  BV x - BV y = BV (bvSub x y)

  -- Feldspar uses the idiom b * x (where b is a boolean) to mean
  -- b ? x : 0. But the SMT solver doesn't like multiplication.
  -- Here are some transformations which simplify away that idiom.
  BV (List [Atom "ite", b, x, y]) * z = BV (ite b (unBV (BV x * z)) (unBV (BV y * z)))
  x * BV (List [Atom "ite", b, y, z]) = BV (ite b (unBV (x * BV y)) (unBV (x * BV z)))
  x * y | x == 0 || y == 0 = 0
  x * y | x == 1 = y
  x * y | x == 2 = y + y
  x * y | y == 1 = x
  x * y | y == 2 = x + x
  BV x * BV y = error "no Mult for BV"
  negate (BV x) = BV (bvNeg x)
  abs = smtAbs
  signum = smtSignum

instance Enum (BV s w) where
  toEnum   = error "no Enum for BV"
  fromEnum = error "no Enum for BV"

instance (Sign s, Width w) => Real (BV s w) where
  toRational = error "no toRational for BV"

instance (Sign s, Width w) => Integral (BV s w) where
  toInteger = error "no toInteger for BV"
  n0@(BV n) `quotRem` d0@(BV d)
    | d0 == 1 = (n0, 0)
    | d0 == 2 = (shiftR n0 2, n0 .&. 1)
    | otherwise = error "no"

-- A slightly lighter version of Data.Bits.
class Bits a where
  (.&.), (.|.) :: a -> a -> a
  complement :: a -> a
  xor :: a -> a -> a
  shiftL :: a -> a -> a
  shiftR :: a -> a -> a

instance (Sign s, Width w) => Bits (BV s w) where
  BV x .&. BV y = BV (bvAnd x y)
  BV x .|. BV y = BV (bvOr x y)
  complement (BV x) = BV (bvNot x)
  BV x `xor` BV y = BV (bvXOr x y)
  shiftL (BV x) (BV n) = BV (bvShl x n)
  shiftR x0@(BV x) (BV n)
    | isSigned x0 = BV (bvAShr x n)
    | otherwise   = BV (bvLShr x n)

instance (Sign s, Width w) => SMTOrd (BV s w) where
  x .<. y
    | isSigned x = bvSLt (unBV x) (unBV y)
    | otherwise  = bvULt (unBV x) (unBV y)
  x .<=. y
    | isSigned x = bvSLeq (unBV x) (unBV y)
    | otherwise  = bvULeq (unBV x) (unBV y)
  x .>.  y = y .<.  x
  x .>=. y = y .<=. x

instance (Sign s, Width w) => Fresh (BV s w) where
  fresh = freshSExpr

instance (Sign s, Width w) => TypedSExpr (BV s w) where
  smtType _ = tBits (width (undefined :: w))
  toSMT = unBV
  fromSMT = BV

newtype Rat = Rat { unRat :: SExpr }
  deriving (Eq, Ord, Typeable)
instance Show Rat where
  show (Rat x) = show x

instance Fresh Rat where
  fresh = freshSExpr
instance TypedSExpr Rat where
  smtType _ = tReal
  toSMT = unRat
  fromSMT = Rat

instance Num Rat where
  fromInteger = Rat . real . fromInteger
  Rat x + Rat y = Rat (add x y)
  Rat x - Rat y = Rat (sub x y)
  Rat x * Rat y = Rat (mul x y)
  negate (Rat x) = Rat (neg x)
  abs (Rat x) = Rat (SMT.abs x)
  signum = smtSignum

instance Fractional Rat where
  Rat x / Rat y = Rat (realDiv x y)
  fromRational = Rat . real

instance SMTOrd Rat where
  Rat x .<.  Rat y = lt  x y
  Rat x .<=. Rat y = leq x y
  Rat x .>.  Rat y = gt  x y
  Rat x .>=. Rat y = geq x y

smtAbs :: (Num a, SMTOrd a, TypedSExpr a) => a -> a
smtAbs (x :: a) =
  fromSMT (ite (x .<. 0) (toSMT (negate x)) (toSMT x))

smtSignum :: (Num a, SMTOrd a, TypedSExpr a) => a -> a
smtSignum (x :: a) =
  fromSMT $
    ite (x .<. 0) (toSMT (-1 :: a)) $
    ite (x .>. 0) (toSMT  (1 :: a))
    (toSMT (0 :: a))

--------------------------------------------------------------------------------