mod-0.1.2.2: test/Test.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Main (main) where
import Data.Bits
import Data.Mod
import qualified Data.Mod.Word as Word
import Data.Proxy
import Data.Semigroup
import Foreign.Storable (Storable(..))
import GHC.TypeNats (KnownNat, SomeNat(..), natVal, someNatVal)
import Test.Tasty
import Test.Tasty.QuickCheck
import Test.QuickCheck.Classes.Base
#ifdef MIN_VERSION_semirings
import Data.Semiring (Ring)
import Test.QuickCheck.Classes (semiringLaws, ringLaws)
#endif
#ifdef MIN_VERSION_vector
import Data.Primitive (Prim)
import Data.Vector.Unboxed (Unbox)
import Test.QuickCheck.Classes (muvectorLaws, primLaws)
#endif
main :: IO ()
main = defaultMain $ testGroup "All"
[ testGroup "Mod 1" $
testProperty "fromInteger"
(fromIntegerProp (Proxy :: Proxy 1)) :
map lawsToTest (laws1 (Proxy :: Proxy (Mod 1)))
, testGroup "Mod 2310" $
testProperty "fromInteger"
(fromIntegerProp (Proxy :: Proxy 2310)) :
testProperty "invertMod" (invertModProp @2310) :
testProperty "powMod" (powModProp @2310) :
map lawsToTest (laws (Proxy :: Proxy (Mod 2310)))
, testGroup "Mod 18446744073709551615" $
testProperty "fromInteger"
(fromIntegerProp (Proxy :: Proxy 18446744073709551615)) :
testProperty "invertMod" (invertModProp @18446744073709551615) :
testProperty "powMod" (powModProp @18446744073709551615) :
map lawsToTest (laws (Proxy :: Proxy (Mod 18446744073709551615)))
, testGroup "Mod 18446744073709551626" $
testProperty "fromInteger"
(fromIntegerProp (Proxy :: Proxy 18446744073709551626)) :
testProperty "powMod" (powModProp @18446744073709551626) :
testProperty "invertMod" (invertModProp @18446744073709551626) :
map lawsToTest (laws (Proxy :: Proxy (Mod 18446744073709551626)))
, testGroup "Mod 123456789012345678901234567890" $
testProperty "fromInteger"
(fromIntegerProp (Proxy :: Proxy 123456789012345678901234567890)) :
testProperty "powMod" (powModProp @123456789012345678901234567890) :
testProperty "invertMod" (invertModProp @123456789012345678901234567890) :
map lawsToTest (laws (Proxy :: Proxy (Mod 123456789012345678901234567890)))
, testGroup "Random Mod"
[ testProperty "fromInteger" fromIntegerRandomProp
, testProperty "invertMod" invertModRandomProp
, testProperty "powMod" powModRandomProp
, testProperty "powMod on sum" powModRandomAdditiveProp
, testProperty "powMod special case" powModCase
]
, testGroup "Word.Mod 1" $
testProperty "fromInteger"
(fromIntegerWordProp (Proxy :: Proxy 1)) :
map lawsToTest (laws1 (Proxy :: Proxy (Word.Mod 1)))
, testGroup "Word.Mod 2310" $
testProperty "fromInteger"
(fromIntegerWordProp (Proxy :: Proxy 2310)) :
testProperty "powMod" (powModWordProp @2310) :
testProperty "invertMod" (invertModWordProp @2310) :
map lawsToTest (laws (Proxy :: Proxy (Word.Mod 2310)))
, if finiteBitSize (0 :: Word) == 64 then
testGroup "Word.Mod 18446744073709551615" $
testProperty "fromInteger"
(fromIntegerWordProp (Proxy :: Proxy 18446744073709551615)) :
testProperty "powMod" (powModWordProp @18446744073709551615) :
testProperty "invertMod" (invertModWordProp @18446744073709551615) :
map lawsToTest (laws (Proxy :: Proxy (Word.Mod 18446744073709551615)))
else
testGroup "Word.Mod 4294967295" $
testProperty "fromInteger"
(fromIntegerWordProp (Proxy :: Proxy 4294967295)) :
testProperty "powMod" (powModWordProp @4294967295) :
testProperty "invertMod" (invertModWordProp @4294967295) :
map lawsToTest (laws (Proxy :: Proxy (Word.Mod 4294967295)))
, testGroup "Random Word.Mod"
[ testProperty "fromInteger" fromIntegerWordRandomProp
, testProperty "invertMod" invertModWordRandomProp
, testProperty "invertMod near maxBound" invertModWordRandomPropNearMaxBound
, testProperty "powMod" powModWordRandomProp
, testProperty "powMod on sum" powModWordRandomAdditiveProp
, testProperty "powMod special case" powModWordCase
]
]
#ifdef MIN_VERSION_semirings
#ifdef MIN_VERSION_vector
laws1 :: (Eq a, Ord a, Show a, Num a, Storable a, Ring a, Prim a, Unbox a, Arbitrary a) => Proxy a -> [Laws]
#else
laws1 :: (Eq a, Ord a, Show a, Num a, Storable a, Ring a, Arbitrary a) => Proxy a -> [Laws]
#endif
#else
#ifdef MIN_VERSION_vector
laws1 :: (Eq a, Ord a, Show a, Num a, Storable a, Prim a, Unbox a, Arbitrary a) => Proxy a -> [Laws]
#else
laws1 :: (Eq a, Ord a, Show a, Num a, Storable a, Arbitrary a) => Proxy a -> [Laws]
#endif
#endif
laws1 p =
[ eqLaws p
, ordLaws p
, numLaws p
, showLaws p
, storableLaws p
#ifdef MIN_VERSION_semirings
, semiringLaws p
, ringLaws p
#endif
#ifdef MIN_VERSION_vector
, primLaws p
, muvectorLaws p
#endif
]
#ifdef MIN_VERSION_semirings
#ifdef MIN_VERSION_vector
laws :: (Eq a, Ord a, Show a, Num a, Storable a, Ring a, Enum a, Bounded a, Prim a, Unbox a, Arbitrary a) => Proxy a -> [Laws]
#else
laws :: (Eq a, Ord a, Show a, Num a, Storable a, Ring a, Enum a, Bounded a, Arbitrary a) => Proxy a -> [Laws]
#endif
#else
#ifdef MIN_VERSION_vector
laws :: (Eq a, Ord a, Show a, Num a, Storable a, Enum a, Bounded a, Prim a, Unbox a, Arbitrary a) => Proxy a -> [Laws]
#else
laws :: (Eq a, Ord a, Show a, Num a, Storable a, Enum a, Bounded a, Arbitrary a) => Proxy a -> [Laws]
#endif
#endif
laws p = boundedEnumLaws p : laws1 p
lawsToTest :: Laws -> TestTree
lawsToTest (Laws name props) =
testGroup name $ map (uncurry testProperty) props
instance KnownNat m => Arbitrary (Mod m) where
arbitrary = oneof [arbitraryBoundedEnum, negate <$> arbitraryBoundedEnum, fromInteger <$> arbitrary]
shrink = map fromInteger . shrink . toInteger . unMod
instance KnownNat m => Arbitrary (Word.Mod m) where
arbitrary = oneof [arbitraryBoundedEnum, negate <$> arbitraryBoundedEnum, fromInteger <$> arbitrary]
shrink = map fromIntegral . shrink . Word.unMod
-------------------------------------------------------------------------------
-- fromInteger
fromIntegerRandomProp :: Positive Integer -> Integer -> Property
fromIntegerRandomProp (Positive m) n = m > 1 ==> case someNatVal (fromInteger m) of
SomeNat p -> fromIntegerProp p n
fromIntegerProp :: forall m. KnownNat m => Proxy m -> Integer -> Property
fromIntegerProp p n = unMod m === fromInteger (n `mod` toInteger (natVal p))
where
m :: Mod m
m = fromInteger n
fromIntegerWordRandomProp :: Word -> Integer -> Property
fromIntegerWordRandomProp m n = m > 1 ==> case someNatVal (fromIntegral m) of
SomeNat p -> fromIntegerWordProp p n
fromIntegerWordProp :: forall m. KnownNat m => Proxy m -> Integer -> Property
fromIntegerWordProp p n = Word.unMod m === fromInteger (n `mod` toInteger (natVal p))
where
m :: Word.Mod m
m = fromInteger n
-------------------------------------------------------------------------------
-- invertMod
invertModRandomProp :: Positive Integer -> Integer -> Property
invertModRandomProp (Positive m) n = m > 1 ==> case someNatVal (fromInteger m) of
SomeNat (Proxy :: Proxy m) -> invertModProp (fromInteger n :: Mod m)
invertModProp :: KnownNat m => Mod m -> Property
invertModProp x = case invertMod x of
Nothing -> g =/= 1
Just x' -> g === 1 .&&. x * x' === 1 .&&. x' * x === 1 .&&. x' === x ^% (-1 :: Int)
where
g = gcd (unMod x) (fromIntegral (natVal x))
invertModWordRandomProp :: Word -> Integer -> Property
invertModWordRandomProp m n = m > 1 ==> case someNatVal (fromIntegral m) of
SomeNat (Proxy :: Proxy m) -> invertModWordProp (fromInteger n :: Word.Mod m)
invertModWordRandomPropNearMaxBound :: Word -> Integer -> Property
invertModWordRandomPropNearMaxBound m n = m < maxBound ==>
case someNatVal (fromIntegral (maxBound - m)) of
SomeNat (Proxy :: Proxy m) -> invertModWordProp (fromInteger n :: Word.Mod m)
invertModWordProp :: KnownNat m => Word.Mod m -> Property
invertModWordProp x = case Word.invertMod x of
Nothing -> g =/= 1
Just x' -> g === 1 .&&. x * x' === 1 .&&. x' * x === 1 .&&. x' === x Word.^% (-1 :: Int)
where
g = gcd (Word.unMod x) (fromIntegral (natVal x))
-------------------------------------------------------------------------------
-- powMod
powModRandomProp :: Positive Integer -> Integer -> Int -> Property
powModRandomProp (Positive m) x n = m > 1 ==> case someNatVal (fromInteger m) of
SomeNat (Proxy :: Proxy m) -> powModProp (fromInteger x :: Mod m) n
powModProp :: KnownNat m => Mod m -> Int -> Property
powModProp x n
| n >= 0 = x ^% n === getProduct (stimes n (Product x))
| otherwise = case invertMod x of
Nothing -> property True
Just x' -> x ^% n === getProduct (stimes (-n) (Product x'))
powModRandomAdditiveProp :: Positive Integer -> Integer -> Huge Integer -> Huge Integer -> Property
powModRandomAdditiveProp (Positive m) x (Huge n1) (Huge n2) = m > 1 ==> case someNatVal (fromInteger m) of
SomeNat (Proxy :: Proxy m) -> powModAdditiveProp (fromInteger x :: Mod m) n1 n2
powModAdditiveProp :: KnownNat m => Mod m -> Integer -> Integer -> Property
powModAdditiveProp x n1 n2
| invertMod x == Nothing, n1 < 0 || n2 < 0
= property True
| otherwise
= (x ^% n1) * (x ^% n2) === x ^% (n1 + n2)
powModCase :: Property
powModCase = once $ 0 ^% n === (0 :: Mod 2)
where
n = 1 `shiftL` 64 :: Integer
powModWordRandomProp :: Word -> Integer -> Int -> Property
powModWordRandomProp m x k = m > 1 ==> case someNatVal (fromIntegral m) of
SomeNat (Proxy :: Proxy m) -> powModWordProp (fromInteger x :: Word.Mod m) k
powModWordProp :: KnownNat m => Word.Mod m -> Int -> Property
powModWordProp x n
| n >= 0 = x Word.^% n === getProduct (stimes n (Product x))
| otherwise = case Word.invertMod x of
Nothing -> property True
Just x' -> x Word.^% n === getProduct (stimes (-n) (Product x'))
powModWordRandomAdditiveProp :: Word -> Integer -> Huge Integer -> Huge Integer -> Property
powModWordRandomAdditiveProp m x (Huge n1) (Huge n2) = m > 1 ==> case someNatVal (fromIntegral m) of
SomeNat (Proxy :: Proxy m) -> powModWordAdditiveProp (fromInteger x :: Word.Mod m) n1 n2
powModWordAdditiveProp :: KnownNat m => Word.Mod m -> Integer -> Integer -> Property
powModWordAdditiveProp x n1 n2
| Word.invertMod x == Nothing, n1 < 0 || n2 < 0
= property True
| otherwise
= (x Word.^% n1) * (x Word.^% n2) === x Word.^% (n1 + n2)
powModWordCase :: Property
powModWordCase = once $ 0 Word.^% n === (0 :: Word.Mod 2)
where
n = 1 `shiftL` 64 :: Integer
newtype Huge a = Huge { getHuge :: a }
deriving (Show)
instance (Bits a, Num a, Arbitrary a) => Arbitrary (Huge a) where
arbitrary = do
Positive l <- arbitrary
ds <- vector l
return $ Huge $ foldl1 (\acc n -> acc `shiftL` 63 + n) ds
shrink (Huge n) = Huge <$> shrink n