packages feed

ac-library-hs-1.2.2.0: test/Tests/Extra/Semigroup/Matrix.hs

{-# LANGUAGE DataKinds #-}

module Tests.Extra.Semigroup.Matrix (tests) where

import AtCoder.Extra.Semigroup.Matrix qualified as Mat
import AtCoder.ModInt qualified as M
import Data.Semigroup (stimes)
import Data.Vector.Unboxed qualified as VU
import GHC.TypeNats (KnownNat)
import Test.QuickCheck.Classes qualified as QCC
import Test.Tasty
import Test.Tasty.QuickCheck qualified as QC
import Tests.Util (laws)

-- TODO: (const True) should be removed

-- orphan instance
instance (QC.Arbitrary a, VU.Unbox a) => QC.Arbitrary (Mat.Matrix a) where
  -- for simplicity, make a 33x33 matrix
  arbitrary = do
    let n = 33
    vec <- VU.fromList <$> QC.vectorOf (n * n) (QC.arbitrary @a)
    pure $ Mat.Matrix n n vec

-- orphan instance
instance (KnownNat p) => QC.Arbitrary (M.ModInt p) where
  arbitrary = M.new <$> QC.arbitrary

prop_mulToCol :: QC.Gen QC.Property
prop_mulToCol = do
  h <- QC.chooseInt (1, 16)
  w <- QC.chooseInt (1, 16)
  vec <- VU.fromList <$> QC.vectorOf (h * w) (QC.arbitrary @Int)
  let mat = Mat.new h w vec
  col <- VU.fromList <$> QC.vectorOf w (QC.arbitrary @Int)
  let lhs = Mat.mulToCol mat col
  let rhs = Mat.vecM $ Mat.mul mat (Mat.new w 1 col)
  pure  $ lhs QC.=== rhs

m :: Int
m = 998244353

prop_pow :: QC.Small Int -> QC.NonNegative Int -> QC.Gen QC.Property
prop_pow (QC.Small n) (QC.NonNegative k) = do
  vec <- VU.fromList <$> QC.vectorOf (n * n) (QC.chooseInt (0, m - 1))
  let mat = Mat.new n n vec
  if k == 0
    then pure $ Mat.pow k mat QC.=== Mat.ident n
    else pure $ Mat.pow k mat QC.=== stimes k mat

tests :: [TestTree]
tests =
  [ QC.testProperty "mulToCol" prop_mulToCol,
    laws @(Mat.Matrix (M.ModInt 998244353))
      [ QCC.semigroupLaws
      ],
    QC.testProperty "pow" prop_pow
  ]