ac-library-hs-1.2.2.0: benchmarks/Tests/MulMod.hs
{-# LANGUAGE DataKinds #-}
-- | A bit tedious tests.
module Tests.MulMod (tests) where
import BenchLib.MulMod.Barrett64 qualified as Barrett64
import BenchLib.MulMod.BarrettWideWord qualified as BarrettWideWord
import BenchLib.MulMod.Montgomery qualified as Montgomery
import Data.Foldable (for_)
import Data.Proxy (Proxy (..))
import Data.Word (Word64)
import GHC.TypeNats (KnownNat, natVal)
import Test.QuickCheck qualified as QC
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck qualified as QC
newtype InRange s a = InRange a
deriving (Show, Eq)
instance (KnownNat s) => QC.Arbitrary (InRange s Word64) where
arbitrary = InRange . (`mod` fromIntegral (natVal (Proxy @s))) <$> QC.arbitrary
prop_barrettWideWord :: forall m. (KnownNat m) => InRange m Word64 -> InRange m Word64 -> Bool
prop_barrettWideWord (InRange a) (InRange b) = a * b `mod` m == BarrettWideWord.mulMod bt a b
where
m = fromIntegral $ natVal (Proxy @m) :: Word64
bt = BarrettWideWord.new64 m
unit_barrettWideWordBounds :: Word64 -> TestTree
unit_barrettWideWordBounds m = testCase ("barrett64 bounds " ++ show m) $ do
let bt = BarrettWideWord.new64 m
for_ [0 .. 4] $ \a -> do
for_ [0 .. 4] $ \b -> do
a * b `mod` m @=? BarrettWideWord.mulMod bt a b
for_ [fromIntegral m - 5 .. fromIntegral m - 1] $ \a -> do
for_ [fromIntegral m - 5 .. fromIntegral m - 1] $ \b -> do
fromIntegral a * fromIntegral b `mod` m @=? BarrettWideWord.mulMod bt a b
prop_barrett64 :: forall m. (KnownNat m) => InRange m Word64 -> InRange m Word64 -> Bool
prop_barrett64 (InRange a) (InRange b) = (a * b `mod` m) == Barrett64.mulMod bt a b
where
m = fromIntegral $ natVal (Proxy @m)
bt = Barrett64.new m
unit_barrett64Bounds :: Word64 -> TestTree
unit_barrett64Bounds m = testCase ("barrett64 bounds " ++ show m) $ do
let bt = Barrett64.new m
for_ [0 .. 4] $ \a -> do
for_ [0 .. 4] $ \b -> do
a * b `mod` m @=? Barrett64.mulMod bt a b
for_ [m - 5 .. m - 1] $ \a -> do
for_ [m - 5 .. m - 1] $ \b -> do
a * b `mod` m @=? Barrett64.mulMod bt a b
prop_montgomery :: forall m. (KnownNat m) => InRange m Word64 -> InRange m Word64 -> Bool
prop_montgomery (InRange a) (InRange b) = a * b `mod` m == Montgomery.reduce mont (Montgomery.mulMod mont a b)
where
m = fromIntegral $ natVal (Proxy @m)
mont = Montgomery.new m
unit_montgomeryBounds :: Word64 -> TestTree
unit_montgomeryBounds m = testCase ("montgomery64 bounds " ++ show m) $ do
let mont = Montgomery.new m
for_ [0 .. 4] $ \a -> do
for_ [0 .. 4] $ \b -> do
a * b `mod` m @=? Montgomery.reduce mont (Montgomery.mulMod mont a b)
for_ [m - 4 .. m - 1] $ \a -> do
for_ [m - 4 .. m - 1] $ \b -> do
a * b `mod` m @=? Montgomery.reduce mont (Montgomery.mulMod mont a b)
tests :: [TestTree]
tests =
[ QC.testProperty "barrettWideWord random 998244353" $ prop_barrettWideWord @998244353,
unit_barrettWideWordBounds 998244353,
QC.testProperty "barrett64 random 998244353" $ prop_barrett64 @998244353,
unit_barrett64Bounds 998244353,
QC.testProperty "montgomery random 998244353" $ prop_montgomery @998244353,
unit_montgomeryBounds 998244353,
-- TODO: run in a separate tree
QC.testProperty "barrettWideWord random 2147483647" $ prop_barrettWideWord @2147483647,
unit_barrettWideWordBounds 2147483647,
QC.testProperty "barrett64 random 2147483647" $ prop_barrett64 @2147483647,
unit_barrett64Bounds 2147483647,
QC.testProperty "montgomery random 2147483647" $ prop_montgomery @2147483647,
unit_montgomeryBounds 2147483647
]