content: module Word24(tests) where
  
  import Prelude as P
  
  import Test.Tasty
  import           Test.Tasty.QuickCheck    (testProperty)
  
  import Test.QuickCheck hiding ((.&.))
  
  import Orphans()
  import Data.Int
  import Data.Int.Int24
  import Data.Word
  import Data.Word.Word24
  import Data.Bits
  import Foreign.Storable
  import GHC.Real
  
  prop_enum3 :: Int -> Bool
  prop_addIdent :: Word24 -> Bool
  prop_multIdent :: Word24 -> Bool
  prop_unsigned :: Word24 -> Bool
  prop_smaller :: Word16 -> Bool
  prop_show :: Word16 -> Bool
  prop_read :: Word24 -> Bool
  prop_adder :: Word16 -> Word16 -> Property
  prop_negate :: Word24 -> Bool
  prop_abs :: Word24 -> Bool
  prop_signum :: Word24 -> Bool
  prop_real :: Word24 -> Bool
  prop_enum1 :: Word24 -> Property
  prop_enum2 :: Word24 -> Property
  prop_enum4 :: Word24 -> Property
  prop_enum5 :: Word24 -> Word8 -> Bool
  prop_enum6 :: Word24 -> Word24 -> Bool
  prop_quot :: Word24 -> Word24 -> Bool
  prop_rem :: Word24 -> Word24 -> Bool
  prop_div :: Word24 -> Word24 -> Bool
  prop_mod :: Word24 -> Word24 -> Bool
  prop_quotrem :: Word24 -> Word24 -> Bool
  prop_divmod :: Word24 -> Word24 -> Bool
  prop_and :: Word16 -> Word16 -> Bool
  prop_or :: Word16 -> Word16 -> Bool
  prop_xor :: Word16 -> Word16 -> Bool
  prop_xor_ident :: Word24 -> Word24 -> Bool
  prop_shiftL :: Word24 -> Bool
  prop_shiftR :: Word24 -> Property
  prop_shiftR2 :: Word24 -> Int -> Property
  prop_shiftL_ident :: Word24 -> Bool
  prop_rotate :: Word24 -> Int -> Bool
  prop_comp :: Word24 -> Bool
  prop_byteSwap :: Word24 -> Bool
  prop_clz :: Word24 -> Bool
  prop_ctz :: Word24 -> Bool
  prop_bit_ident :: Bits a => a -> NonNegative Int -> Bool
  prop_sizeOf :: Word24 -> Bool
  prop_align :: Word24 -> Bool
  prop_addIdentI :: Int24 -> Bool
  prop_multIdentI :: Int24 -> Bool
  prop_smallerI :: Int16 -> Bool
  prop_showI :: Int16 -> Bool
  prop_readI :: Int24 -> Bool
  prop_adderI :: Int16 -> Int16 -> Bool
  prop_negateI :: Int24 -> Bool
  prop_absI :: Int24 -> Bool
  prop_signumI :: Int24 -> Bool
  prop_realI :: Int24 -> Bool
  prop_enum1I :: Int24 -> Property
  prop_enum2I :: Int24 -> Property
  prop_enum3I :: Int -> Bool
  prop_enum4I :: Int24 -> Property
  prop_enum5I :: Int24 -> Word8 -> Bool
  prop_enum6I :: Int24 -> Int24 -> Bool
  prop_quotI :: Int24 -> Int24 -> Bool
  prop_remI :: Int24 -> Int24 -> Bool
  prop_divI :: Int24 -> Int24 -> Bool
  prop_modI :: Int24 -> Int24 -> Bool
  
  -- ----------------------------------------
  -- Word24 Properties
  
  prop_addIdent a = a - a == 0
  
  prop_multIdent a = a * 1 == a
  
  prop_unsigned a = a >= 0
  
  prop_smaller a = (fromIntegral ((fromIntegral a) :: Word24) :: Word16) == a
  
  prop_show a = show a == show (fromIntegral a :: Word24)
  
  prop_read a = a == (read . show) a
  
  prop_adder a b = (a < ub) && (b < ub) ==>
                   fromIntegral (a + b) ==
                   ((fromIntegral a + fromIntegral b) :: Word24)
       where
          ub :: Word16
          ub = maxBound `div` 2
  
  prop_negate a = a == negate (negate a)
  
  prop_abs a = a == abs a
  
  prop_signum a = if a == 0 then signum a == a else signum a == 1
  
  prop_real a = let r = toRational a in numerator r == fromIntegral a
  
  -- Word24 Enum properties
  prop_enum1 a = a < maxBound ==> succ a == a + 1
  
  prop_enum2 a = a > minBound ==> pred a == a - 1
  
  prop_enum3 a = let a' = abs a in
                 toEnum a' == fromIntegral @_ @Integer a'
  
  prop_enum4 a = a < maxBound ==> take 2 (enumFrom a) == [a, a+1]
  
  prop_enum5 a b = let b' = fromIntegral b in
                   enumFromTo a (a + b') ==
                   map fromIntegral (enumFromTo (fromIntegral a :: Integer)
                                               (fromIntegral (a + b') :: Integer))
  
  prop_enum6 a b = take 2 (enumFromThen a b) == [a,b]
  
  -- Word24 Integral properties
  prop_quot a b =
    quot a b == fromIntegral (quot (fromIntegral a :: Word32) (fromIntegral b :: Word32))
  
  prop_rem a b =
    rem a b == fromIntegral (rem (fromIntegral a :: Word32) (fromIntegral b :: Word32))
  
  prop_div a b =
    div a b == fromIntegral (div (fromIntegral a :: Word32) (fromIntegral b :: Word32))
  
  prop_mod a b =
    mod a b == fromIntegral (mod (fromIntegral a :: Word32) (fromIntegral b :: Word32))
  
  prop_quotrem a b = let (j, k) = quotRem a b in
    a == (b * j) + k
  
  prop_divmod a b =
    divMod a b == (div a b, mod a b)
  
  -- binary Word properties
  prop_and a b = (a .&. b) == fromIntegral ( (fromIntegral a :: Word24) .&. (fromIntegral b :: Word24))
  
  prop_or a b = (a .|. b) == fromIntegral ( (fromIntegral a :: Word24) .|. (fromIntegral b :: Word24))
  
  prop_xor a b = (a `xor` b) == fromIntegral ( (fromIntegral a :: Word24) `xor` (fromIntegral b :: Word24))
  
  prop_xor_ident a b = (a `xor` b) `xor` b == a
  
  prop_shiftL a = a `shiftL` 1 == a * 2
  
  prop_shiftR a = a < maxBound `div` 2 ==>
    (a * 2) `shift` (-1) == a
  
  prop_shiftR2 a n = n >= 0 ==> a `shiftR` n == a `shift` (negate n)
  
  prop_shiftL_ident a = a `shiftL` 0 == a
  
  prop_rotate a b = (a `rotate` b) `rotate` (negate b) == a
  
  prop_comp a = complement (complement a) == a
  
  prop_byteSwap a = byteSwap24 (byteSwap24 a) == a
  
  prop_clz a = countLeadingZeros a == countLeadingZeros' a
    where
      countLeadingZeros' :: Word24 -> Int
      countLeadingZeros' x = (w-1) - go (w-1)
        where
          go i | i < 0       = i -- no bit set
               | testBit x i = i
               | otherwise   = go (i-1)
  
          w = finiteBitSize x
  
  prop_ctz a = countTrailingZeros a == countTrailingZeros' a
    where
      countTrailingZeros' :: Word24 -> Int
      countTrailingZeros' x = go 0
        where
          go i | i >= w      = i
               | testBit x i = i
               | otherwise   = go (i+1)
          w = finiteBitSize x
  
  prop_bit_ident q (NonNegative j) = testBit (bit j `asTypeOf` q) j == (j < 24)
  
  prop_popCount :: (Integral a1, Num a2, FiniteBits a1,
                          FiniteBits a2) =>
                         a1 -> a2 -> a1 -> Bool
  prop_popCount s t a = if a >= 0
    then popCount (a `asTypeOf` s) == popCount (fromIntegral a `asTypeOf` t)
    else
      finiteBitSize s - popCount (a `asTypeOf` s) == finiteBitSize t - popCount (fromIntegral a `asTypeOf` t)
  
  -- Word Storable properties
  prop_sizeOf a = sizeOf a == 3
  
  prop_align a = alignment a == 3
  
  
  
  -- ----------------------------------------
  -- Int24 Properties
  
  prop_addIdentI a = a - a == 0
  
  prop_multIdentI a = a * 1 == a
  
  prop_smallerI a = (fromIntegral ((fromIntegral a) :: Int24) :: Int16) == a
  
  prop_showI a = show a == show (fromIntegral a :: Int24)
  
  prop_readI a = a == (read . show) a
  
  prop_adderI a b = ((fromIntegral a + fromIntegral b) :: Int) ==
                    fromIntegral ((fromIntegral a + fromIntegral b) :: Int24)
  
  prop_negateI a = a == negate (negate a)
  
  prop_absI a = if a >= 0 then a == abs a else a == negate (abs a)
  
  prop_signumI a = signum a == fromIntegral (signum (fromIntegral a :: Int))
  
  prop_realI a = let r = toRational a in numerator r == fromIntegral a
  
  -- Int24 Enum Properties
  prop_enum1I a = a < maxBound ==> succ a == a + 1
  
  prop_enum2I a = a > minBound ==> pred a == a - 1
  
  prop_enum3I a = let a' = abs a in
                 toEnum a' == fromIntegral @_ @Integer a'
  
  prop_enum4I a = a < maxBound ==> take 2 (enumFrom a) == [a, a+1]
  
  prop_enum5I a b = let b' = fromIntegral b in
                   enumFromTo a (a + b') ==
                   map fromIntegral (enumFromTo (fromIntegral a :: Integer)
                                               (fromIntegral (a + b') :: Integer))
  
  prop_enum6I a b = take 2 (enumFromThen a b) == [a,b]
  
  -- Int24 Integral properties
  prop_quotI a b =
    quot a b == fromIntegral (quot (fromIntegral a :: Int32) (fromIntegral b :: Int32))
  
  prop_remI a b =
    rem a b == fromIntegral (rem (fromIntegral a :: Int32) (fromIntegral b :: Int32))
  
  prop_divI a b =
    div a b == fromIntegral (div (fromIntegral a :: Int32) (fromIntegral b :: Int32))
  
  prop_modI a b =
    mod a b == fromIntegral (mod (fromIntegral a :: Int32) (fromIntegral b :: Int32))
  
  prop_quotremI :: Int24 -> Int24 -> Bool
  prop_quotremI a b = let (j, k) = quotRem a b in
    a == (b * j) + k
  
  prop_divmodI :: Int24 -> Int24 -> Bool
  prop_divmodI a b =
    divMod a b == (div a b, mod a b)
  
  
  -- binary Int properties
  prop_andI :: Int16 -> Int16 -> Bool
  prop_andI a b = (a .&. b) == fromIntegral ( (fromIntegral a :: Int24) .&. (fromIntegral b :: Int24))
  
  prop_orI :: Int16 -> Int16 -> Bool
  prop_orI a b = (a .|. b) == fromIntegral ( (fromIntegral a :: Int24) .|. (fromIntegral b :: Int24))
  
  prop_xorI :: Int16 -> Int16 -> Bool
  prop_xorI a b = (a `xor` b) == fromIntegral ( (fromIntegral a :: Int24) `xor` (fromIntegral b :: Int24))
  
  prop_xor_identI :: Int24 -> Int24 -> Bool
  prop_xor_identI a b = (a `xor` b) `xor` b == a
  
  prop_shiftLI :: Int24 -> Bool
  prop_shiftLI a = a `shiftL` 1 == a * 2
  
  prop_shiftL_identI :: Int24 -> Bool
  prop_shiftL_identI a = a `shiftL` 0 == a
  
  prop_shiftRI :: Int24 -> Property
  prop_shiftRI a = (a < maxBound `div` 2) && (a > minBound `div` 2) ==>
    (a * 2) `shift` (-1) == a
  
  prop_shiftR2I :: Int24 -> Int -> Property
  prop_shiftR2I a n = n >= 0 ==> a `shiftR` n == a `shift` (negate n)
  
  prop_rotateI :: Int24 -> Int -> Bool
  prop_rotateI a b = (a `rotate` b) `rotate` (negate b) == a
  
  prop_compI :: Int24 -> Bool
  prop_compI a = complement (complement a) == a
  
  prop_clzI :: Int24 -> Bool
  prop_clzI a = countLeadingZeros a == countLeadingZeros' a
    where
      countLeadingZeros' :: Int24 -> Int
      countLeadingZeros' x = (w-1) - go (w-1)
        where
          go i | i < 0       = i -- no bit set
               | testBit x i = i
               | otherwise   = go (i-1)
  
          w = finiteBitSize x
  
  prop_ctzI :: Int24 -> Bool
  prop_ctzI a = countTrailingZeros a == countTrailingZeros' a
    where
      countTrailingZeros' :: Int24 -> Int
      countTrailingZeros' x = go 0
        where
          go i | i >= w      = i
               | testBit x i = i
               | otherwise   = go (i+1)
          w = finiteBitSize x
  
  -- Int Storable properties
  prop_sizeOfI :: Int24 -> Bool
  prop_sizeOfI a = sizeOf a == 3
  
  prop_alignI :: Int24 -> Bool
  prop_alignI a = alignment a == 3
  
  
  -- ----------------------------------------
  -- tests
  tests :: [TestTree]
  tests = [
   testGroup "Word24"
    [ testGroup "basic" [
      testProperty "add. identity" prop_addIdent
      ,testProperty "mult. identity" prop_multIdent
      ,testProperty "unsigned" prop_unsigned
      ,testProperty "Word16/Word24 conversion" prop_smaller
      ,testProperty "Show" prop_show
      ,testProperty "Read" prop_read
      ,testProperty "addition" prop_adder
      ,testProperty "negate identity" prop_negate
      ,testProperty "absolute value" prop_abs
      ,testProperty "signum" prop_signum
      ,testProperty "Real identity" prop_real
      ]
    ,testGroup "Integral instance" [
      testProperty  "quot" prop_quot
      ,testProperty "rem" prop_rem
      ,testProperty "div" prop_div
      ,testProperty "mod" prop_mod
      ,testProperty "quotRem" prop_quotrem
      ,testProperty "divmod" prop_divmod
      ]
    ,testGroup "Enum instance" [
      testProperty "enum succ" prop_enum1
      ,testProperty "enum pred" prop_enum2
      ,testProperty "toEnum" prop_enum3
      ,testProperty "enumFrom " prop_enum4
      ,testProperty "enumFromTo" prop_enum5
      ,testProperty "enumFromThen" prop_enum6
      ]
    ,testGroup "Bits instance" [
      testProperty "binary and" prop_and
      ,testProperty "binary or" prop_or
      ,testProperty "binary xor" prop_xor
      ,testProperty "binary xor identity" prop_xor_ident
      ,testProperty "binary shiftL" prop_shiftL
      ,testProperty "binary shiftL identity" prop_shiftL_ident
      ,testProperty "binary shift right" prop_shiftR
      ,testProperty "binary shiftR" prop_shiftR2
      ,testProperty "binary rotate" prop_rotate
      ,testProperty "binary complement" prop_comp
      ,testProperty "binary byteSwap24" prop_byteSwap
      ,testProperty "binary countLeadingZeros" prop_clz
      ,testProperty "binary countTrailingZeros" prop_ctz
      ,testProperty "binary countTrailingZeros 0" (prop_ctz (0::Word24))
      ,testProperty "bit/testBit" (prop_bit_ident (0::Word24))
      ,testProperty "popCount"    (prop_popCount (0::Word24) (0::Word))
      ]
    ,testGroup "Storable instance" [
      testProperty  "sizeOf Word24" prop_sizeOf
      ,testProperty "aligntment" prop_align
      ]
    ]
   ,testGroup "Int24"
    [testGroup "basic" [
      testProperty "add. identity" prop_addIdentI
      ,testProperty "mult. identity" prop_multIdentI
      ,testProperty "Int16/Int24 conversion" prop_smallerI
      ,testProperty "Show" prop_showI
      ,testProperty "Read" prop_readI
      ,testProperty "addition" prop_adderI
      ,testProperty "negate identity" prop_negateI
      ,testProperty "absolute value" prop_absI
      ,testProperty "signum" prop_signumI
      ,testProperty "Real identity" prop_realI
      ]
    ,testGroup "Integral instance" [
      testProperty  "quot" prop_quotI
      ,testProperty "rem" prop_remI
      ,testProperty "div" prop_divI
      ,testProperty "mod" prop_modI
      ,testProperty "quotRem" prop_quotremI
      ,testProperty "divmod" prop_divmodI
      ]
    ,testGroup "Enum instance" [
      testProperty "enum succ" prop_enum1I
      ,testProperty "enum pred" prop_enum2I
      ,testProperty "toEnum" prop_enum3I
      ,testProperty "enumFrom " prop_enum4I
      ,testProperty "enumFromTo" prop_enum5I
      ,testProperty "enumFromThen" prop_enum6I
      ]
    ,testGroup "Bits instance" [
      testProperty "binary and" prop_andI
      ,testProperty "binary or" prop_orI
      ,testProperty "binary xor" prop_xorI
      ,testProperty "binary xor identity" prop_xor_identI
      ,testProperty "binary shiftL" prop_shiftLI
      ,testProperty "binary shiftL identity" prop_shiftL_identI
      ,testProperty "binary shift right" prop_shiftRI
      ,testProperty "binary shiftR" prop_shiftR2I
      ,testProperty "binary rotate" prop_rotateI
      ,testProperty "binary complement" prop_compI
      ,testProperty "binary countLeadingZeros" prop_clzI
      ,testProperty "binary countTrailingZeros" prop_ctzI
      ,testProperty "binary countTrailingZeros 0" (prop_ctzI (0::Int24))
      ,testProperty "bit/testBit" (prop_bit_ident (0::Int24))
      ,testProperty "popCount"    (prop_popCount (0::Int24) (0::Int))
      ]
    ,testGroup "Storable instance" [
      testProperty  "sizeOf Int24" prop_sizeOfI
      ,testProperty "aligntment" prop_alignI
      ]
    ]
   ]
  
  