ac-library-hs-1.2.2.0: test/Tests/Extra/Monoid.hs
{-# LANGUAGE DataKinds #-}
module Tests.Extra.Monoid (tests) where
import AtCoder.Extra.Monoid
import AtCoder.Extra.Monoid.Affine1 qualified as A
import AtCoder.Extra.Monoid.Mat2x2 qualified as M
import AtCoder.Extra.Monoid.RollingHash (RollingHash (..))
import AtCoder.ModInt qualified as ModInt
import Data.Bit (Bit (..))
import Data.Proxy (Proxy (..))
import Data.Semigroup (Max (..), Min (..), Product (..), Sum (..), stimes)
import Test.QuickCheck.Classes qualified as QCC
import Test.QuickCheck.Property qualified as QC
import Test.Tasty
import Test.Tasty.QuickCheck qualified as QC
import Tests.Util (laws, myForAllShrink)
-- TODO: (const True) should be removed
segActLaw :: (Monoid a, Eq a) => (SegAct f a, QC.Arbitrary f, Eq f, Show f, QC.Arbitrary f, QC.Arbitrary a, Show a) => Proxy (f, a) -> QCC.Laws
segActLaw p =
QCC.Laws
"SegAct"
[ ("Identity map", segActIdentity p),
("Monoid Action", segActMonoidAction p),
("Linear Monoid Action", segActLinearMonoidAction p),
("Endomorphism", segActEndomorphism p)
]
segActIdentity :: forall f a. (SegAct f a, QC.Arbitrary f, Eq f, Show f, Monoid a, Eq a, QC.Arbitrary a, Show a) => Proxy (f, a) -> QC.Property
segActIdentity _ = myForAllShrink True (const True) desc lhsS lhs rhsS rhs
where
desc :: a -> [String]
desc (a :: a) = ["a = " ++ show a]
lhsS = "segAct mempty a"
lhs = segAct (mempty @f)
rhsS = "a"
rhs = id
segActMonoidAction :: forall f a. (SegAct f a, QC.Arbitrary f, Eq f, Show f, Monoid a, Eq a, QC.Arbitrary a, Show a) => Proxy (f, a) -> QC.Property
segActMonoidAction _ = myForAllShrink True (const True) desc lhsS lhs rhsS rhs
where
desc :: (f, f, a) -> [String]
desc (!f2, !f1, !a) = ["f2 = " ++ show f2 ++ ", f1 = " ++ show f1 ++ ", a = " ++ show a]
lhsS = "(f_2 <> f_1) a"
lhs (!f2, !f1, !a) = (f2 <> f1) `segAct` a
rhsS = "f_2 (f_1 a)"
rhs (!f2, !f1, !a) = f2 `segAct` (f1 `segAct` a)
segActEndomorphism :: forall f a. (SegAct f a, QC.Arbitrary f, Eq f, Show f, Monoid a, Eq a, QC.Arbitrary a, Show a) => Proxy (f, a) -> QC.Property
segActEndomorphism _ = myForAllShrink True (const True) desc lhsS lhs rhsS rhs
where
desc :: (f, a, a) -> [String]
desc (!f, !a1, !a2) = ["f = " ++ show f ++ ", a1 = " ++ show a1 ++ ", a2 = " ++ show a2]
lhsS = "f (a1 <> a2)"
lhs (!f, !a1, !a2) = segActWithLength 2 f (a1 <> a2)
rhsS = "(f a1) <> (f a2)"
rhs (!f, !a1, !a2) = (f `segAct` a1) <> (f `segAct` a2)
segActLinearMonoidAction :: forall f a. (SegAct f a, QC.Arbitrary f, Eq f, Show f, Monoid a, Eq a, QC.Arbitrary a, Show a) => Proxy (f, a) -> QC.Property
segActLinearMonoidAction _ = myForAllShrink True (const True) desc lhsS lhs rhsS rhs
where
desc :: (QC.Positive Int, f, a) -> [String]
desc (QC.Positive !len, !f, !a) = ["len = " ++ show len ++ ", f = " ++ show f ++ ", a = " ++ show a]
lhsS = "segActWithLength len f (a^len)"
lhs (QC.Positive !len, !f, !a) = segActWithLength len f $! stimes len a
rhsS = "(f a)^len"
rhs (QC.Positive !len, !f, !a) = stimes len (segAct f a)
-- orphan instance
instance (QC.Arbitrary a, Monoid a) => QC.Arbitrary (Affine1 a) where
arbitrary = Affine1 <$> QC.arbitrary
-- orphan instance
instance (QC.Arbitrary a) => QC.Arbitrary (RangeAdd a) where
arbitrary = RangeAdd <$> QC.arbitrary
-- orphan instance
instance (QC.Arbitrary a, Monoid a) => QC.Arbitrary (RangeSet a) where
arbitrary = do
b <- (== 1) <$> QC.chooseInt (1, 30)
if b
then RangeSet . (Bit True,) <$> QC.arbitrary
else pure mempty
-- orphan instance
instance (QC.Arbitrary a) => QC.Arbitrary (Mat2x2 a) where
arbitrary = Mat2x2 <$> QC.arbitrary
-- orphan instance
instance (QC.Arbitrary a) => QC.Arbitrary (V2 a) where
arbitrary = V2 <$> QC.arbitrary
-- orphan instance
instance QC.Arbitrary (RollingHash b 998244353) where
arbitrary = do
hash <- QC.chooseInt (0, 998244353 - 1)
next <- QC.chooseInt (0, 998244353 - 1)
pure $ RollingHash hash next
-- orphan instance
instance QC.Arbitrary (Max Int) where
arbitrary = Max <$> QC.arbitrary
-- orphan instance
instance QC.Arbitrary (Min Int) where
arbitrary = Min <$> QC.arbitrary
-- orphan instance (TODO: move to common implementation)
instance QC.Arbitrary ModInt.ModInt998244353 where
arbitrary = ModInt.new <$> QC.arbitrary
prop_affineZero :: Affine1 (Sum Int) -> QC.Property
prop_affineZero a =
QC.conjoin
[ A.zero <> a QC.=== A.zero,
a <> A.zero QC.=== (\(A.Affine1 (!_, !b)) -> A.Affine1 (0, b)) a
]
prop_affineIdent :: Affine1 (Sum Int) -> QC.Property
prop_affineIdent a =
QC.conjoin
[ A.ident <> a QC.=== a,
a <> A.ident QC.=== a
]
prop_mat2x2Zero :: Mat2x2 Int -> QC.Property
prop_mat2x2Zero a =
QC.conjoin
[ M.zero <> a QC.=== M.zero,
a <> M.zero QC.=== M.zero
]
prop_mat2x2Ident :: Mat2x2 Int -> QC.Property
prop_mat2x2Ident a =
QC.conjoin
[ M.ident <> a QC.=== a,
a <> M.ident QC.=== a
]
prop_mat2x2Inv :: Mat2x2 ModInt.ModInt998244353 -> QC.Property
prop_mat2x2Inv a =
(M.det a /= 0 QC.==>) $
QC.conjoin
[ M.inv a <> a QC.=== M.ident,
a <> M.inv a QC.=== M.ident
]
tests :: [TestTree]
tests =
[ testGroup
"Affine1"
[ QC.testProperty "zero" prop_affineZero,
QC.testProperty "ident" prop_affineIdent,
laws @(Affine1 (Sum Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(Affine1 (Sum Int), Sum Int)
[ segActLaw
]
],
testGroup
"RangeAdd"
[ laws @(RangeAdd (Sum Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeAdd (Sum Int), Sum Int)
[ segActLaw
],
laws @(RangeAdd (Max Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeAdd (Max Int), Max Int)
[ segActLaw
],
laws @(RangeAdd (Min Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeAdd (Min Int), Min Int)
[ segActLaw
]
],
testGroup
"RangeSet"
[ laws @(RangeSet (Sum Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeSet (Sum Int), Sum Int)
[ segActLaw
],
laws @(RangeSet (Product Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeSet (Product Int), Product Int)
[ segActLaw
],
laws @(RangeSet (Max Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeSet (Max Int), Max Int)
[ segActLaw
],
laws @(RangeSet (Min Int))
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(RangeSet (Min Int), Min Int)
[ segActLaw
]
],
testGroup
"Mat2x2"
[ QC.testProperty "zero" prop_mat2x2Zero,
QC.testProperty "ident" prop_mat2x2Ident,
QC.testProperty "inv" prop_mat2x2Inv,
laws @(Mat2x2 Int)
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
],
laws @(Mat2x2 Int, V2 Int)
[ segActLaw
]
],
testGroup
"V2"
[ laws @(V2 Int)
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
]
],
testGroup
"RollingHash"
[ laws @(RollingHash 100 998244353)
[ QCC.semigroupLaws,
QCC.monoidLaws,
QCC.semigroupMonoidLaws
]
]
]