aig-0.2.5: tests/Tests/Operations.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
module Tests.Operations (op_tests) where
import Test.Tasty
import Test.Tasty.QuickCheck
--import Test.QuickCheck
import Data.AIG.Interface (BasicGraph, BasicLit, newBasicGraph)
import qualified Data.AIG.Interface as AIG
import qualified Data.AIG.Operations as AIG
import Data.Bits
import Data.Int
import Data.Word
type BV s = AIG.BV (BasicLit s)
--------------------------------------------------------------------------
-- Some test harness stuff to make writing down unit tests easier
class Precond x where
precond :: x -> Bool
precond _ = True
class Arbitrary x => AIGTestableValue g x y where
injectArg :: g -> x -> y
extractArg :: g -> y -> Maybe x
class PushIO y where
pushIO :: (a -> y) -> IO a -> y
instance PushIO (IO b) where
pushIO f x = x >>= f
instance PushIO b => PushIO (a -> b) where
pushIO f x a = pushIO (\x' -> f x' a) x
class (Testable p, PushIO y) => AIGTestable g x y p | g x y -> p where
runTest :: IO g -> x -> y -> Bool -> p
newtype Denom a = Denom a
deriving (Eq, Ord, Show)
instance Precond Bool
instance Precond Word32
instance Precond Word64
instance Precond Int32
instance Precond Int64
instance Precond Integer
instance (Integral a, Eq a) => Precond (Denom a) where
precond (Denom x) = x /= 0
instance Precond Int where
precond x = 0 <= x && x < 64
instance Arbitrary a => Arbitrary (Denom a) where
arbitrary = Denom <$> arbitrary
instance (AIGTestableValue g a x, AIGTestableValue g b y) =>
AIGTestableValue g (a,b) (x,y) where
injectArg g (a,b) = (injectArg g a, injectArg g b)
extractArg g (x,y) = (,) <$> extractArg g x <*> extractArg g y
instance AIGTestableValue g a b => AIGTestableValue g (Denom a) b where
injectArg g (Denom x) = injectArg g x
extractArg g x = Denom <$> extractArg g x
instance AIGTestableValue (BasicGraph s) Int (BV s) where
injectArg g x = AIG.bvFromInteger g (finiteBitSize x) (toInteger x)
extractArg g x = fromInteger <$> AIG.asSigned g x
instance AIGTestableValue (BasicGraph s) Word32 (BV s) where
injectArg g x = AIG.bvFromInteger g (finiteBitSize x) (toInteger x)
extractArg g x = fromInteger <$> AIG.asUnsigned g x
instance AIGTestableValue (BasicGraph s) Word64 (BV s) where
injectArg g x = AIG.bvFromInteger g (finiteBitSize x) (toInteger x)
extractArg g x = fromInteger <$> AIG.asUnsigned g x
instance AIGTestableValue (BasicGraph s) Int32 (BV s) where
injectArg g x = AIG.bvFromInteger g (finiteBitSize x) (toInteger x)
extractArg g x = fromInteger <$> AIG.asSigned g x
instance AIGTestableValue (BasicGraph s) Int64 (BV s) where
injectArg g x = AIG.bvFromInteger g (finiteBitSize x) (toInteger x)
extractArg g x = fromInteger <$> AIG.asSigned g x
instance AIGTestableValue (BasicGraph s) Integer Integer where
injectArg _g x = x
extractArg _g x = Just x
instance AIGTestableValue (BasicGraph s) Bool (BasicLit s) where
injectArg g x = AIG.constant g x
extractArg g x = AIG.asConstant g x
instance (Eq x, Show x, AIGTestableValue g x y) => AIGTestable g x (IO y) Property
where
runTest gph x y pre = pre ==> ioProperty $
do gph' <- gph
y' <- y
case extractArg gph' y' of
Nothing -> fail $ unwords ["Expected concrete output"]
Just z
| x == z -> return True
| otherwise -> fail $ unwords ["Expected", show x, "but got", show z]
instance
(Precond x, Show x, AIGTestableValue g x y, AIGTestable g a b p) =>
AIGTestable g (x -> a) (y -> b) (x -> p)
where
runTest gph f g pre = \x ->
let y = (gph >>= \gph' -> return $ injectArg gph' x)
in runTest gph (f x) (pushIO g y) (precond x && pre)
------------------------------------------------------------------------------------
-- Make a unit test from a concrete operation and an operation on AIGs
mkTest :: forall a b p
. (Testable p, AIGTestable (BasicGraph ()) a b p)
=> String
-> a
-> (BasicGraph () -> b)
-> TestTree
mkTest nm a b =
withResource newBasicGraph (\_ -> return ()) $ \(g :: IO (BasicGraph ())) ->
testProperty nm $ (runTest g a (pushIO b g) True :: p)
op_tests :: [TestTree]
op_tests =
[ mkTest "ite" (\b (x :: Word64) y -> if b then x else y) AIG.ite
, mkTest "neg" (negate :: Int64 -> Int64) AIG.neg
, mkTest "add" ((+) :: Word64 -> Word64 -> Word64) AIG.add
, mkTest "addC" addC AIG.addC
, mkTest "sub" ((-) :: Word64 -> Word64 -> Word64) AIG.sub
, mkTest "subC" subC AIG.subC
, mkTest "addConst" addConst AIG.addConst
, mkTest "subConst" subConst AIG.subConst
, mkTest "mul" ((*) :: Word64 -> Word64 -> Word64) AIG.mul
, mkTest "mulFull" mulFull AIG.mulFull
, mkTest "smulFull" smulFull AIG.smulFull
, mkTest "squot" squot AIG.squot
, mkTest "srem" srem AIG.srem
, mkTest "uquot" uquot AIG.uquot
, mkTest "urem" urem AIG.urem
, mkTest "shl" (shiftL :: Word64 -> Int -> Word64) AIG.shl
, mkTest "ushr" (shiftR :: Word64 -> Int -> Word64) AIG.ushr
, mkTest "sshr" (shiftR :: Int64 -> Int -> Int64) AIG.sshr
, mkTest "rol" (rotateL :: Word64 -> Int -> Word64) AIG.rol
, mkTest "ror" (rotateR :: Word64 -> Int -> Word64) AIG.ror
, mkTest "bvEq" ((==) :: Word64 -> Word64 -> Bool) AIG.bvEq
, mkTest "isZero" (\x -> (x :: Word64) == 0) AIG.isZero
, mkTest "nonZero" (\x -> (x :: Word64) /= 0) AIG.nonZero
, mkTest "sle" ((<=) :: Int64 -> Int64 -> Bool) AIG.sle
, mkTest "slt" ((<) :: Int64 -> Int64 -> Bool) AIG.slt
, mkTest "ule" ((<=) :: Word64 -> Word64 -> Bool) AIG.ule
, mkTest "ult" ((<) :: Word64 -> Word64 -> Bool) AIG.ult
, mkTest "sabs" (abs :: Int64 -> Int64) AIG.sabs
, mkTest "sext" sext (\g x -> (return $ AIG.sext g x 64) :: IO _)
, mkTest "zext" zext (\g x -> (return $ AIG.zext g x 64) :: IO _)
, mkTest "trunc" trunc (\(_ :: BasicGraph ()) (x :: BV ()) -> (return $ AIG.trunc 64 x) :: IO _)
, mkTest "priorityEncode" priorityEncode (\g -> AIG.priorityEncode g 32)
, mkTest "logBase2_down" log2down AIG.logBase2_down
, mkTest "logBase2_up" log2up AIG.logBase2_up
, mkTest "clz" clz AIG.countLeadingZeros
, mkTest "ctz" ctz AIG.countTrailingZeros
, mkTest "pmul" pmul AIG.pmul
, mkTest "pdiv" pdiv AIG.pdiv
, mkTest "pmod" pmod AIG.pmod
]
addC :: Word32 -> Word32 -> (Word32, Bool)
addC x y = ( fromIntegral z, testBit z 32 )
where z :: Word64
z = fromIntegral x + fromIntegral y
subC :: Word32 -> Word32 -> (Word32, Bool)
subC x y = ( fromIntegral z, testBit z 32 )
where z :: Word64
z = fromIntegral x - fromIntegral y
addConst :: Word64 -> Integer -> Word64
addConst x y = x + fromInteger y
subConst :: Word64 -> Integer -> Word64
subConst x y = x - fromInteger y
mulFull :: Word32 -> Word32 -> Word64
mulFull x y = fromIntegral x * fromIntegral y
smulFull :: Int32 -> Int32 -> Int64
smulFull x y = fromIntegral x * fromIntegral y
uquot :: Word64 -> Denom Word64 -> Word64
uquot x (Denom y) = quot x y
urem :: Word64 -> Denom Word64 -> Word64
urem x (Denom y) = rem x y
squot :: Int64 -> Denom Int64 -> Int64
squot x (Denom y) = quot x y
srem :: Int64 -> Denom Int64 -> Int64
srem x (Denom y) = rem x y
sext :: Int32 -> Int64
sext = fromIntegral
zext :: Word32 -> Word64
zext = fromIntegral
trunc :: Word64 -> Word32
trunc = fromIntegral
log2down :: Word64 -> Int64
log2down x = fromIntegral (finiteBitSize x) - 1 - fromIntegral (countLeadingZeros x)
log2up :: Word64 -> Int64
log2up x = log2down (x - 1) + 1
clz :: Word64 -> Word64
clz = fromIntegral . countLeadingZeros
ctz :: Word64 -> Word64
ctz = fromIntegral . countTrailingZeros
pmul :: Word32 -> Word32 -> Word64
pmul x y = foldr xor zeroBits pprods
where
pprods = [ if testBit y i
then shiftL (fromIntegral x) i
else 0
| i <- [0 .. finiteBitSize y - 1]
]
priorityEncode :: Word64 -> (Bool, Word32)
priorityEncode x
| x == 0 = (False, 0)
| otherwise = (True, fromIntegral (finiteBitSize x) - 1 - fromIntegral (countLeadingZeros x))
pdiv :: Word64 -> Denom Word32 -> Word64
pdiv x (Denom y) = fst $ pdivmod x y
pmod :: Word64 -> Denom Word32 -> Word32
pmod x (Denom y) = snd $ pdivmod x y
pdivmod :: Word64 -> Word32 -> (Word64, Word32)
pdivmod x y = go 0 x (finiteBitSize x - 1) start
where
start = finiteBitSize x - finiteBitSize y + countLeadingZeros y
y' = fromIntegral y
go q r i j
| j < 0 = (q,fromIntegral r)
| testBit r i = go (setBit q j) (r `xor` (y' `shiftL` j)) (i-1) (j-1)
| otherwise = go q r (i-1) (j-1)