packages feed

grisette-0.13.0.1: test/Grisette/SymPrim/SomeBVTests.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Use /=" #-}
{-# HLINT ignore "Use ==" #-}

module Grisette.SymPrim.SomeBVTests (someBVTests) where

import Control.DeepSeq (NFData, force)
import Control.Exception (ArithException (Overflow), catch, evaluate)
import Control.Monad.Except (ExceptT)
import Data.Bits
  ( Bits
      ( clearBit,
        complement,
        complementBit,
        setBit,
        shiftL,
        unsafeShiftL,
        xor,
        (.&.),
        (.|.)
      ),
    FiniteBits (finiteBitSize),
  )
import Data.Proxy (Proxy (Proxy))
import Data.Serialize (decode, encode)
import Grisette
  ( AsKey (AsKey),
    AsKey1 (AsKey1),
    BV (bv, bvConcat, bvExt, bvSelect, bvSext, bvZext),
    ITEOp (symIte),
    LogicalOp (symNot),
    Mergeable (rootStrategy),
    SafeLinearArith (safeAdd, safeSub),
    SignConversion (toSigned, toUnsigned),
    Solvable (con, isym, ssym),
    SomeBV (SomeBVLit),
    SomeWordN,
    SymEq ((./=), (.==)),
    genSym,
    genSymSimple,
    mrgIf,
    mrgReturn,
    mrgSingle,
  )
import Grisette.Internal.Core.Control.Monad.Union (Union (Union))
import Grisette.Internal.Core.Data.UnionBase
  ( UnionBase (UnionSingle),
    ifWithLeftMost,
  )
import Grisette.Internal.SymPrim.BV (IntN)
import Grisette.Internal.SymPrim.SomeBV
  ( SomeBV (SomeBV),
    SomeBVException (BitwidthMismatch),
    SomeIntN,
    SomeSymIntN,
    arbitraryBV,
    binSomeBV,
    binSomeBVR1,
    binSomeBVR2,
    binSomeBVSafe,
    binSomeBVSafeR1,
    conBV,
    conBVView,
    isymBV,
    ssymBV,
    unarySomeBV,
    unarySomeBVR1,
    pattern ConBV,
    pattern SomeIntN,
  )
import Grisette.Internal.SymPrim.SymBV (SymIntN)
import Grisette.Lib.Control.Monad.Except (mrgThrowError)
import Grisette.Lib.Data.Functor (mrgFmap)
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.HUnit (assertBool, (@?=))
import Test.QuickCheck
  ( Arbitrary (arbitrary),
    Gen,
    NonNegative (getNonNegative),
    forAll,
    ioProperty,
  )

testFuncMatch ::
  (Eq r, Show r) =>
  String ->
  (SomeIntN -> SomeIntN -> r) ->
  SomeIntN ->
  SomeIntN ->
  r ->
  Test
testFuncMatch name f a b r = testCase name $ do
  let actual = f a b
  let expected = r
  actual @?= expected

testFuncMatchLit ::
  String ->
  (SomeIntN -> SomeIntN -> SomeIntN) ->
  SomeIntN ->
  SomeIntN ->
  SomeIntN ->
  Test
testFuncMatchLit name f a b r = testCase name $ do
  let SomeBVLit actual = f a b
  let SomeBVLit expected = r
  actual @?= expected

testSymFuncMatch ::
  String ->
  (SomeSymIntN -> SomeSymIntN -> SomeSymIntN) ->
  SomeSymIntN ->
  SomeSymIntN ->
  SomeSymIntN ->
  Test
testSymFuncMatch name f a b r = testCase name $ do
  let actual = f a b
  let expected = r
  AsKey actual @?= AsKey expected

testSymFuncMatchLit ::
  String ->
  (SomeSymIntN -> SomeSymIntN -> SomeSymIntN) ->
  SomeSymIntN ->
  SomeSymIntN ->
  SomeSymIntN ->
  Test
testSymFuncMatchLit name f a b r = testCase name $ do
  let SomeBVLit actual = f a b
  let SomeBVLit expected = r
  AsKey actual @?= AsKey expected

testFuncMisMatch ::
  (NFData r, Show r, Eq r) =>
  (SomeIntN -> SomeIntN -> r) ->
  SomeIntN ->
  SomeIntN ->
  r ->
  Test
testFuncMisMatch f a b r = testCase "bit width mismatch" $ do
  actual <-
    evaluate (force $ f a b)
      `catch` \(_ :: SomeBVException) -> return r
  let expected = r
  actual @?= expected

testSafeFuncMatchException ::
  (Eq r, Show r, Mergeable r) =>
  ( SomeIntN ->
    SomeIntN ->
    ExceptT (Either SomeBVException ArithException) Union r
  ) ->
  SomeIntN ->
  SomeIntN ->
  ArithException ->
  Test
testSafeFuncMatchException f a b e = testCase "bit width match" $ do
  let actual = f a b
  let expected = mrgThrowError (Right e)
  actual @?= expected

testSafeFuncMatch ::
  (Eq r, Show r, Mergeable r) =>
  String ->
  ( SomeIntN ->
    SomeIntN ->
    ExceptT (Either SomeBVException ArithException) Union r
  ) ->
  SomeIntN ->
  SomeIntN ->
  r ->
  Test
testSafeFuncMatch name f a b r = testCase name $ do
  let actual = f a b
  let expected = mrgSingle r
  actual @?= expected

testSafeFuncMatchLit ::
  String ->
  ( SomeIntN ->
    SomeIntN ->
    Either (Either SomeBVException ArithException) SomeIntN
  ) ->
  SomeIntN ->
  SomeIntN ->
  SomeIntN ->
  Test
testSafeFuncMatchLit name f a b r = testCase name $ do
  let Right (SomeBVLit actual) = f a b
  let Right (SomeBVLit expected) = mrgSingle r
  actual @?= expected

testSafeFuncMisMatch ::
  (Eq r, Show r, Mergeable r) =>
  ( SomeIntN ->
    SomeIntN ->
    ExceptT (Either SomeBVException ArithException) Union r
  ) ->
  SomeIntN ->
  SomeIntN ->
  Test
testSafeFuncMisMatch f a b = testCase "bit width mismatch" $ do
  let actual = f a b
  let expected = mrgThrowError (Left BitwidthMismatch)
  actual @?= expected

someBVTests :: Test
someBVTests =
  testGroup
    "SomeBV"
    [ testGroup
        "Helpers"
        [ testCase "conBV" $ do
            let actual = conBV (bv 4 5)
            let expected = bv 4 5 :: SomeSymIntN
            AsKey actual @?= AsKey expected,
          testGroup
            "conBVView"
            [ testCase "is concrete" $ do
                let value = bv 4 5 :: SomeSymIntN
                let actual = conBVView value
                let expected = Just (bv 4 5)
                actual @?= expected
                case value of
                  ConBV v -> v @?= bv 4 5
                  _ -> fail "is concrete",
              testCase "is not concrete" $ do
                let value = ssymBV 4 "a" :: SomeSymIntN
                let actual = conBVView value
                let expected = Nothing
                actual @?= expected
                case value of
                  ConBV _ -> fail "is not concrete"
                  _ -> return ()
            ],
          testCase "ssymBV" $ AsKey (ssymBV 4 "a") @?= AsKey (SomeBV (ssym "a" :: SymIntN 4)),
          testCase "isymBV" $
            AsKey (isymBV 4 "a" 1) @?= AsKey (SomeBV (isym "a" 1 :: SymIntN 4)),
          testGroup
            "unarySomeBV"
            [ testCase "SomeBV" $ do
                let actual =
                      unarySomeBV @IntN @SomeIntN
                        (SomeIntN . negate)
                        undefined
                        (bv 4 5 :: SomeIntN)
                let expected = bv 4 (-5)
                actual @?= expected,
              testCase "SomeBVLit" $ do
                let SomeBVLit actual =
                      unarySomeBV @IntN @SomeIntN
                        undefined
                        (SomeBVLit . negate)
                        (5 :: SomeIntN)
                let SomeBVLit expected = SomeBVLit $ -5
                actual @?= expected
            ],
          testGroup
            "unarySomeBVR1"
            [ testCase "SomeBV" $ do
                let actual = unarySomeBVR1 negate undefined (bv 4 5 :: SomeIntN)
                let expected = bv 4 (-5)
                actual @?= expected,
              testCase "SomeBVLit" $ do
                let SomeBVLit actual =
                      unarySomeBVR1 undefined negate (SomeBVLit 5 :: SomeIntN)
                let SomeBVLit expected = SomeBVLit $ -5
                actual @?= expected
            ],
          testGroup
            "binSomeBV"
            [ testFuncMatch @SomeIntN
                "SomeBV/SomeBV"
                (binSomeBV (\l r -> SomeIntN $ l + r) undefined)
                (bv 4 5)
                (bv 4 2)
                (bv 4 7),
              testFuncMatch @SomeIntN
                "SomeBV/SomeBVLit"
                (binSomeBV (\l r -> SomeIntN $ l + r) undefined)
                (bv 4 5)
                2
                (bv 4 7),
              testFuncMatch @SomeIntN
                "SomeBVLit/SomeBV"
                (binSomeBV (\l r -> SomeIntN $ l + r) undefined)
                5
                (bv 4 2)
                (bv 4 7),
              testFuncMatchLit
                "SomeBVLit/SomeBVLit"
                (binSomeBV undefined (\l r -> SomeBVLit $ l + r))
                5
                2
                7,
              testSymFuncMatch
                "SomeBV/SomeBV"
                (binSomeBV (\l r -> SomeBV $ l + r) undefined)
                (ssymBV 4 "a")
                (ssymBV 4 "b")
                ((ssymBV 4 "a") + (ssymBV 4 "b")),
              testSymFuncMatch
                "SomeBV/SomeBVCondLit"
                (binSomeBV (\l r -> SomeBV $ l + r) undefined)
                (ssymBV 4 "a")
                (symIte "b" 5 6)
                ((ssymBV 4 "a") + symIte "b" (bv 4 5) (bv 4 6)),
              testSymFuncMatchLit
                "SomeBVCondLit/SomeBVCondLit"
                (binSomeBV undefined (\l r -> SomeBVLit $ l + r))
                (symIte "a" 5 6)
                (symIte "b" 5 6)
                (symIte "a" (symIte "b" 10 11) (symIte "b" 11 12)),
              testSymFuncMatchLit
                "SomeBVLit/SomeBVCondLit"
                (binSomeBV undefined (\l r -> SomeBVLit $ l + r))
                5
                (symIte "b" 5 6)
                (symIte "b" 10 11),
              testFuncMisMatch @SomeIntN
                (binSomeBV (\l r -> SomeIntN $ l + r) undefined)
                (bv 4 5)
                (bv 5 4)
                (bv 3 0)
            ],
          testGroup
            "binSomeBVR1"
            [ testFuncMatch
                "SomeBV/SomeBV"
                (binSomeBVR1 (+) undefined)
                (bv 4 5)
                (bv 4 2)
                (bv 4 7),
              testFuncMisMatch
                (binSomeBVR1 (+) undefined)
                (bv 4 5)
                (bv 5 4)
                (bv 3 0)
            ],
          testGroup
            "binSomeBVR2"
            [ testFuncMatch
                "SomeBV/SomeBV"
                (binSomeBVR2 (\l r -> (l + r, l - r)) undefined)
                (bv 4 5)
                (bv 4 2)
                (bv 4 7, bv 4 3),
              testFuncMisMatch
                (binSomeBVR2 (\l r -> (l + r, l - r)) undefined)
                (bv 4 5)
                (bv 5 4)
                (bv 3 0, bv 6 1)
            ],
          testGroup "binSomeBVSafe" $ do
            let func l r = mrgFmap SomeIntN $ safeAdd l r
            [ testSafeFuncMatch @SomeIntN
                "SomeBV/SomeBV"
                (binSomeBVSafe func undefined)
                (bv 4 5)
                (bv 4 2)
                (bv 4 7),
              testSafeFuncMatch @SomeIntN
                "SomeBV/SomeBVInt"
                (binSomeBVSafe func undefined)
                (bv 4 5)
                2
                (bv 4 7),
              testSafeFuncMatchLit
                "SomeBVInt/SomeBVInt"
                ( binSomeBVSafe
                    undefined
                    (\l r -> mrgReturn $ SomeBVLit $ l + r)
                )
                5
                2
                7,
              testSafeFuncMatchException @SomeIntN
                (binSomeBVSafe func undefined)
                (bv 4 5)
                (bv 4 5)
                Overflow,
              testSafeFuncMisMatch @SomeIntN
                (binSomeBVSafe func undefined)
                (bv 4 5)
                (bv 5 4)
              ],
          testGroup
            "binSomeBVSafeR1"
            [ testSafeFuncMatch
                "SomeBV/SomeBV"
                (binSomeBVSafeR1 safeAdd undefined)
                (bv 4 5)
                (bv 4 2)
                (bv 4 7),
              testSafeFuncMatchException
                (binSomeBVSafeR1 safeAdd undefined)
                (bv 4 5)
                (bv 4 5)
                Overflow,
              testSafeFuncMisMatch
                (binSomeBVSafeR1 safeAdd undefined)
                (bv 4 5)
                (bv 5 4)
            ],
          testGroup "binSomeBVSafeR2" $ do
            let func l r = do
                  a <- safeAdd l r
                  b <- safeSub l r
                  mrgSingle (a, b)
            [ testSafeFuncMatch
                "SomeBV/SomeBV"
                func
                (bv 4 5)
                (bv 4 2)
                (bv 4 7, bv 4 3),
              testSafeFuncMatchException
                func
                (bv 4 5)
                (bv 4 5)
                Overflow,
              testSafeFuncMisMatch func (bv 4 5) (bv 5 4)
              ]
        ],
      testGroup
        "BV"
        [ testCase "bvConcat" $ do
            bvConcat (bv 8 0x14 :: SomeIntN) (bv 4 2) @?= bv 12 0x142,
          testCase "bvZext" $ do
            bvZext 8 (bv 4 0x8 :: SomeIntN) @?= bv 8 0x08,
          testCase "bvSext" $ do
            bvSext 8 (bv 4 0x8 :: SomeIntN) @?= bv 8 0xF8,
          testCase "bvExt" $ do
            bvExt 8 (bv 4 0x8 :: SomeIntN) @?= bv 8 0xF8
            bvExt 8 (bv 4 0x8 :: SomeWordN) @?= bv 8 0x08,
          testCase "bvSelect" $ do
            bvSelect 1 4 (bv 8 0x17 :: SomeIntN) @?= bv 4 0xB,
          testCase "bv" $ bv 8 0x14 @?= (SomeIntN (0x14 :: IntN 8))
        ],
      testGroup
        "Mergeable"
        [ testGroup "SomeIntN" $ do
            (name, l, r, merged) <-
              [ ( "same bitwidth",
                  bv 4 3,
                  bv 4 5,
                  ifWithLeftMost
                    True
                    "cond"
                    (UnionSingle $ bv 4 3)
                    (UnionSingle $ bv 4 5)
                ),
                ( "same bitwidth, should invert",
                  bv 4 5,
                  bv 4 2,
                  ifWithLeftMost
                    True
                    (symNot "cond")
                    (UnionSingle $ bv 4 2)
                    (UnionSingle $ bv 4 5)
                ),
                ( "different bitwidth",
                  bv 4 5,
                  bv 5 4,
                  ifWithLeftMost
                    True
                    "cond"
                    (UnionSingle $ bv 4 5)
                    (UnionSingle $ bv 5 4)
                ),
                ( "different bitwidth, should invert",
                  bv 5 4,
                  bv 4 5,
                  ifWithLeftMost
                    True
                    (symNot "cond")
                    (UnionSingle $ bv 4 5)
                    (UnionSingle $ bv 5 4)
                )
              ]
            return $ testCase name $ do
              let actual =
                    mrgIf "cond" (return l) (return r) :: Union SomeIntN
              let expected = Union (Just rootStrategy) merged
              AsKey actual @?= AsKey expected,
          testGroup "SomeSymIntN" $ do
            (name, l, r, merged) <-
              [ ( "same bitwidth",
                  ssymBV 4 "a",
                  ssymBV 4 "b",
                  (UnionSingle $ AsKey $ symIte "cond" (ssymBV 4 "a") (ssymBV 4 "b"))
                ),
                ( "different bitwidth",
                  ssymBV 4 "a",
                  ssymBV 5 "b",
                  ifWithLeftMost
                    True
                    "cond"
                    (UnionSingle $ AsKey $ ssymBV 4 "a")
                    (UnionSingle $ AsKey $ ssymBV 5 "b")
                ),
                ( "different bitwidth, should invert",
                  ssymBV 5 "b",
                  ssymBV 4 "a",
                  ifWithLeftMost
                    True
                    (symNot "cond")
                    (UnionSingle $ AsKey $ ssymBV 4 "a")
                    (UnionSingle $ AsKey $ ssymBV 5 "b")
                )
              ]
            return $ testCase name $ do
              let actual =
                    mrgIf
                      "cond"
                      (return $ AsKey l)
                      (return $ AsKey r) ::
                      Union (AsKey SomeSymIntN)
              let expected = Union (Just rootStrategy) merged
              AsKey1 actual @?= AsKey1 expected
        ],
      testGroup
        "GenSym"
        [ testCase "Proxy n" $ do
            let actual = genSym (Proxy :: Proxy 4) "a" :: Union (AsKey SomeSymIntN)
            let expected = mrgSingle $ AsKey $ isymBV 4 "a" 0
            AsKey1 actual @?= AsKey1 expected,
          testCase "SomeBV" $ do
            let actual =
                  genSym (bv 4 1 :: SomeSymIntN) "a" :: Union (AsKey SomeSymIntN)
            let expected = mrgSingle $ AsKey $ isymBV 4 "a" 0
            AsKey1 actual @?= AsKey1 expected,
          testCase "Int" $ do
            let actual =
                  genSym (4 :: Int) "a" :: Union (AsKey SomeSymIntN)
            let expected = mrgSingle $ AsKey $ isymBV 4 "a" 0
            AsKey1 actual @?= AsKey1 expected
        ],
      testGroup
        "GenSymSimple"
        [ testCase "Proxy n" $ do
            let actual = genSymSimple (Proxy :: Proxy 4) "a" :: SomeSymIntN
            let expected = isymBV 4 "a" 0
            AsKey actual @?= AsKey expected,
          testCase "SomeBV" $ do
            let actual =
                  genSymSimple (bv 4 1 :: SomeSymIntN) "a" :: SomeSymIntN
            let expected = isymBV 4 "a" 0
            AsKey actual @?= AsKey expected,
          testCase "Int" $ do
            let actual = genSymSimple (4 :: Int) "a" :: SomeSymIntN
            let expected = isymBV 4 "a" 0
            AsKey actual @?= AsKey expected
        ],
      testProperty "arbitraryBV" $
        forAll (arbitraryBV 4) $
          \(bv :: SomeIntN) -> ioProperty $ finiteBitSize bv @?= 4,
      testGroup
        "Eq"
        [ testCase "same bitwidth equal" $ do
            let a = bv 4 5 :: SomeIntN
            let b = bv 4 5 :: SomeIntN
            assertBool "SomeBV with same bitwidth should compare the value" $
              a == b
            assertBool "SomeBV with same bitwidth should compare the value" $
              not $
                a /= b,
          testProperty "==/SomeBV/SomeBVLit" $ \(a :: Integer) (b :: Integer) ->
            let ai = fromIntegral a :: SomeWordN
                bi = fromIntegral b :: SomeWordN
                ab = bv 4 a :: SomeWordN
                bb = bv 4 b :: SomeWordN
             in (ai == bb) == (ab == bb)
                  && (ab == bi) == (ab == bb)
                  && ai == ab,
          testProperty "/=/SomeBV/SomeBVLit" $ \(a :: Integer) (b :: Integer) ->
            let ai = fromIntegral a :: SomeWordN
                bi = fromIntegral b :: SomeWordN
                ab = bv 4 a :: SomeWordN
                bb = bv 4 b :: SomeWordN
             in (ai /= bb) == (ab /= bb)
                  && (ab /= bi) == (ab /= bb)
                  && not (ai /= ab),
          testCase "same bitwidth not equal" $ do
            let a = bv 4 4 :: SomeIntN
            let b = bv 4 5 :: SomeIntN
            assertBool "SomeBV with same bitwidth should compare the value" $
              not $
                a == b
            assertBool "SomeBV with same bitwidth should compare the value" $
              a /= b,
          testCase "different bitwidth" $ do
            let a = bv 3 5 :: SomeIntN
            let b = bv 4 5 :: SomeIntN
            assertBool "SomeBV with different bit width are not equal" $
              not $
                a == b
            assertBool "SomeBV with different bit width are not equal" $ a /= b
        ],
      testGroup
        "SymEq"
        [ testCase "same bitwidth" $ do
            let a = ssymBV 4 "a" :: SomeSymIntN
            let b = ssymBV 4 "b" :: SomeSymIntN
            AsKey (a .== b) @?= AsKey (("a" :: SymIntN 4) .== "b")
            AsKey (a ./= b) @?= AsKey (("a" :: SymIntN 4) ./= "b"),
          testCase "different bitwidth" $ do
            let a = ssymBV 4 "a" :: SomeSymIntN
            let b = ssymBV 3 "b" :: SomeSymIntN
            AsKey (a .== b) @?= AsKey (con False)
            AsKey (a ./= b) @?= AsKey (con True)
        ],
      testGroup
        "Num"
        [ testGroup
            "SomeIntN"
            [ binOpLitTest @SomeIntN (+) "+",
              binOpLitTest @SomeIntN (-) "-",
              unaryOpLitTest @SomeIntN negate "negate"
            ],
          testGroup
            "SomeWordN"
            [ binOpLitTest @SomeWordN (+) "+",
              binOpLitTest @SomeWordN (-) "-",
              unaryOpLitTest @SomeWordN negate "negate"
            ]
        ],
      testGroup
        "SignConversion"
        [ testGroup
            "SomeIntN"
            [unaryOpLitTest @SomeIntN toUnsigned "toUnsigned"],
          testGroup
            "SomeWordN"
            [unaryOpLitTest @SomeWordN toSigned "toSigned"]
        ],
      testGroup
        "Bits"
        [ testGroup
            "SomeIntN"
            [ binOpLitTest @SomeIntN (.&.) ".&.",
              binOpLitTest @SomeIntN (.|.) ".|.",
              binOpLitTest @SomeIntN xor "xor",
              unaryOpLitTest @SomeIntN complement "complement",
              binIntOpLitTest @SomeIntN
                (getNonNegative <$> arbitrary)
                setBit
                "setBit",
              binIntOpLitTest @SomeIntN
                (getNonNegative <$> arbitrary)
                clearBit
                "clearBit",
              binIntOpLitTest @SomeIntN
                (getNonNegative <$> arbitrary)
                complementBit
                "complementBit",
              binIntOpLitTest @SomeIntN
                (getNonNegative <$> arbitrary)
                shiftL
                "shiftL",
              binIntOpLitTest @SomeIntN
                (getNonNegative <$> arbitrary)
                unsafeShiftL
                "unsafeShiftL"
            ],
          testGroup
            "SomeWordN"
            [ binOpLitTest @SomeWordN (.&.) ".&.",
              binOpLitTest @SomeWordN (.|.) ".|.",
              binOpLitTest @SomeWordN xor "xor",
              unaryOpLitTest @SomeWordN complement "complement",
              binIntOpLitTest @SomeWordN
                (getNonNegative <$> arbitrary)
                setBit
                "setBit",
              binIntOpLitTest @SomeWordN
                (getNonNegative <$> arbitrary)
                clearBit
                "clearBit",
              binIntOpLitTest @SomeWordN
                (getNonNegative <$> arbitrary)
                complementBit
                "complementBit",
              binIntOpLitTest @SomeWordN
                (getNonNegative <$> arbitrary)
                shiftL
                "shiftL",
              binIntOpLitTest @SomeWordN
                (getNonNegative <$> arbitrary)
                unsafeShiftL
                "unsafeShiftL"
            ]
        ],
      testProperty "Serialize" $ forAll (arbitraryBV 8) $ \(v :: SomeWordN) ->
        Right v == decode (encode v)
    ]

binOpLitTest ::
  forall bv r. (Num bv, Eq r, BV bv) => (bv -> bv -> r) -> String -> Test
binOpLitTest f name =
  testProperty (name ++ "/SomeBV/SomeBVLit") $ \(a :: Integer) (b :: Integer) ->
    let ai = fromIntegral a :: bv
        bi = fromIntegral b :: bv
        ab = bv 4 a :: bv
        bb = bv 4 b :: bv
     in (f ai bb) == (f ab bb)
          && (f ab bi) == (f ab bb)
          && (f ai bi) == (f ab bb)

binIntOpLitTest ::
  forall bv.
  (Num bv, Eq bv, BV bv) =>
  Gen Int ->
  (bv -> Int -> bv) ->
  String ->
  Test
binIntOpLitTest gen f name =
  testProperty (name ++ "/SomeBV/SomeBVLit") $ \(a :: Integer) -> forAll gen $
    \(b :: Int) ->
      let ai = fromIntegral a :: bv
          ab = bv 4 a :: bv
       in (f ai b) == (f ab b)
            && (f ab b) == (f ab b)
            && (f ai b) == (f ab b)

unaryOpLitTest ::
  forall bv r. (Num bv, Eq r, BV bv) => (bv -> r) -> String -> Test
unaryOpLitTest f name =
  testProperty (name ++ "/SomeBV/SomeBVLit") $ \(a :: Integer) ->
    let ai = fromIntegral a :: bv
        ab = bv 4 a :: bv
     in f ai == f ab