OddWord-1.0.2.0: test/Equiv.hs
{-# LANGUAGE Haskell2010, ScopedTypeVariables, DataKinds, KindSignatures #-}
module Equiv where
import Prelude hiding (catch)
import Test.QuickCheck hiding ((.&.))
import Test.QuickCheck.Gen
import Data.Bits
import Data.Maybe
import Data.Proxy
import Data.Word
import Data.Word.Odd
import Control.Applicative
import Control.Exception
import System.IO.Unsafe
import GHC.TypeLits
-- | Represents a range of unary functions which can be applied to a word.
data UFunc (n::Nat)
= Add Integer | Mul Integer | Sub Integer | SubR Integer
| Div Integer | Mod Integer | Quot Integer | Rem Integer
| DivR Integer | ModR Integer | QuotR Integer | RemR Integer
| Neg | Abs | Inv | AddDigit
| From Integer | And Integer | Or Integer | Xor Integer
| TstB Int | ClrB Int | SetB Int | InvB Int
| FromB Int | Shift Int | Rot Int | PopCnt
| CntLZ | CntTZ
| AdjEnum Int Integer
deriving Show
instance KnownNat n => Arbitrary (UFunc n) where
arbitrary = oneof
[Add <$> choose (0, upper)
,Mul <$> choose (0, upper)
,Sub <$> choose (0, upper)
,SubR <$> choose (0, upper)
,Div <$> choose (0, upper)
,Mod <$> choose (0, upper)
,Quot <$> choose (0, upper)
,Rem <$> choose (0, upper)
,DivR <$> choose (0, upper)
,ModR <$> choose (0, upper)
,QuotR <$> choose (0, upper)
,RemR <$> choose (0, upper)
,return Neg
,return Abs
,return Inv
,return AddDigit
,From <$> arbitrary
,And <$> choose (0, upper)
,Or <$> choose (0, upper)
,Xor <$> choose (0, upper)
,TstB <$> choose (0, 2*width)
,ClrB <$> choose (0, 2*width)
,SetB <$> choose (0, 2*width)
,InvB <$> choose (0, 2*width)
,FromB <$> choose (0, 2*width)
,Shift <$> choose (0, 2*width)
,Rot <$> choose (0, 2*width)
,return PopCnt
,return CntLZ
,return CntTZ
,AdjEnum <$> arbitrary <*> choose (0, upper)
]
where width = fromIntegral $ natVal (Proxy :: Proxy n)
upper = shiftL 1 width - 1
-- | Total wrapper for 'div'.
safeDiv :: (Integral a, Bounded a) => a -> a -> a
safeDiv d 0 = maxBound
safeDiv d n = div d n
-- | Total wrapper for 'mod'.
safeMod :: (Integral a) => a -> a -> a
safeMod d 0 = 0
safeMod d n = mod d n
-- | Total wrapper for 'quot'.
safeQuot :: (Integral a, Bounded a) => a -> a -> a
safeQuot d 0 = maxBound
safeQuot d n = quot d n
-- | Total wrapper for 'rem'.
safeRem :: (Integral a) => a -> a -> a
safeRem d 0 = 0
safeRem d n = rem d n
-- | Total wrapper for 'toEnum'.
safeToEnum :: (Enum a) => a -> Int -> a
safeToEnum def x =
unsafePerformIO (evaluate (toEnum x) `catch` \(ErrorCall _) -> return def)
-- | Interpreter for executing 'UFunc' values.
fromUFunc :: (Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a) =>
UFunc n -> a -> a
fromUFunc (Add i) x = x + (fromInteger i)
fromUFunc (Mul i) x = x * (fromInteger i)
fromUFunc (Sub i) x = x - (fromInteger i)
fromUFunc (SubR i) x = (fromInteger i) - x
fromUFunc (Div i) x = safeDiv x (fromInteger i)
fromUFunc (Mod i) x = safeMod x (fromInteger i)
fromUFunc (Quot i) x = safeQuot x (fromInteger i)
fromUFunc (Rem i) x = safeRem x (fromInteger i)
fromUFunc (DivR i) x = safeDiv (fromInteger i) x
fromUFunc (ModR i) x = safeMod (fromInteger i) x
fromUFunc (QuotR i) x = safeQuot (fromInteger i) x
fromUFunc (RemR i) x = safeRem (fromInteger i) x
fromUFunc Neg x = negate x
fromUFunc Abs x = abs x
fromUFunc Inv x = complement x
fromUFunc (From i) _ = fromInteger i
fromUFunc AddDigit x = read . ('1':) $ show x
fromUFunc (And i) x = x .&. (fromInteger i)
fromUFunc (Or i) x = x .|. (fromInteger i)
fromUFunc (Xor i) x = xor x (fromInteger i)
fromUFunc (TstB n) x = fromIntegral $ fromEnum $ testBit x n
fromUFunc (ClrB n) x = clearBit x n
fromUFunc (SetB n) x = setBit x n
fromUFunc (InvB n) x = complementBit x n
fromUFunc (FromB n) _ = bit n
fromUFunc (Shift n) x = shift x n
fromUFunc (Rot n) x = rotate x n
fromUFunc PopCnt x = fromIntegral $ popCount x
fromUFunc CntLZ x = fromIntegral $ countLeadingZeros x
fromUFunc CntTZ x = fromIntegral $ countTrailingZeros x
fromUFunc (AdjEnum i def) x = safeToEnum (fromIntegral def) . (+i) $ fromEnum x
-- | Checks that computations using real and simulated words produce the same
-- result for a series of 'UFunc's.
verifyEquivalence :: forall n a b.
(KnownNat n,
Integral a, Bounded a, Enum a, FiniteBits a, Read a, Show a,
Integral b, Bounded b, Enum b, FiniteBits b, Read b, Show b) =>
Proxy n -> Proxy a -> Proxy b -> Property
verifyEquivalence width _ _ = property $ \(us :: [UFunc n]) ->
let refFn = foldr (.) id $ map fromUFunc us :: a -> a
tstFn = foldr (.) id $ map fromUFunc us :: b -> b
in toInteger (refFn 0) == toInteger (tstFn 0)
-- | A 16-bit word backed by something else.
type TestWord16 a = OddWord a (One (Zero (Zero (Zero (Zero ())))))