ac-library-hs-1.5.2.1: test/Tests/Extra/IntMap.hs
{-# LANGUAGE RecordWildCards #-}
module Tests.Extra.IntMap where
import AtCoder.Extra.IntMap qualified as IM
import Control.Monad (foldM_)
import Control.Monad.Primitive (PrimMonad, PrimState)
import Control.Monad.ST (RealWorld)
import Data.Map.Strict qualified as IMR -- R: referential implementation
import Data.Vector.Unboxed qualified as VU
import GHC.Stack (HasCallStack)
import Test.QuickCheck.Monadic as QCM
import Test.Tasty
import Test.Tasty.QuickCheck as QC
data Init = Init
{ n :: {-# UNPACK #-} !Int,
ref0 :: !(IMR.Map Int Int),
imM :: !(IO (IM.IntMap RealWorld Int))
}
instance Show Init where
show Init {..} = show (n, ref0)
instance QC.Arbitrary Init where
arbitrary = do
n <- QC.chooseInt (1, 10)
pure $ Init n IMR.empty (IM.new n)
data Query
= Size
| Member Int
| NotMember Int
| Null
| Lookup Int
| Insert Int Int
| InsertWithAdd Int Int
| Delete Int
| Delete_ Int
| LookupGE Int
| LookupGT Int
| LookupLE Int
| LookupLT Int
| LookupMin
| LookupMax
| DeleteMin
| DeleteMax
deriving (Show)
-- | Arbitrary return type for the `Query` result.
data Result
= None
| B Bool
| I Int
| M (Maybe Int)
| KV (Maybe (Int, Int))
| XS (VU.Vector Int)
| KVS (VU.Vector (Int, Int))
deriving (Show, Eq)
queryGen :: Int -> QC.Gen Query
queryGen n = do
QC.oneof
[ pure Size,
Member <$> lookupKeyGen,
NotMember <$> lookupKeyGen,
pure Null,
Lookup <$> lookupKeyGen,
-- insert is partial function
Insert <$> insertKeyGen <*> valGen,
InsertWithAdd <$> insertKeyGen <*> valGen,
Delete <$> lookupKeyGen,
Delete_ <$> lookupKeyGen,
LookupGE <$> lookupKeyGen,
LookupGT <$> lookupKeyGen,
LookupLE <$> lookupKeyGen,
LookupLT <$> lookupKeyGen,
pure LookupMin,
pure LookupMax
]
where
-- for total functions
lookupKeyGen = QC.chooseInt (-1, n)
-- for partial functions
insertKeyGen = QC.chooseInt (0, n - 1)
valGen = QC.chooseInt (-10, 10)
-- | containers. (referential implementation)
handleRef :: IMR.Map Int Int -> Query -> (IMR.Map Int Int, Result)
handleRef im q = case q of
Size -> (im, I $ IMR.size im)
Member k -> (im, B $ IMR.member k im)
NotMember k -> (im, B . not $ IMR.member k im)
Null -> (im, B $ IMR.null im)
Lookup k -> (im, M $ IMR.lookup k im)
Insert k v -> (IMR.insert k v im, None)
InsertWithAdd k v -> (IMR.insertWith (+) k v im, None)
Delete k -> (IMR.delete k im, B $ IMR.member k im)
Delete_ k -> (IMR.delete k im, None)
LookupGE k -> (im, KV (IMR.lookupGE k im))
LookupGT k -> (im, KV (IMR.lookupGT k im))
LookupLE k -> (im, KV (IMR.lookupLE k im))
LookupLT k -> (im, KV (IMR.lookupLT k im))
LookupMin -> (im, KV (IMR.lookupMin im))
LookupMax -> (im, KV (IMR.lookupMax im))
DeleteMin -> wrapKV IMR.deleteFindMin
DeleteMax -> wrapKV IMR.deleteFindMax
where
wrapKV f
| IMR.null im = (im, KV Nothing)
| otherwise = let (!kv, !im') = f im in (im', KV (Just kv))
-- | ac-library-hs.
handleAcl :: (HasCallStack, PrimMonad m) => IM.IntMap (PrimState m) Int -> Query -> m Result
handleAcl im q = case q of
Size -> I <$> IM.size im
Member k -> B <$> IM.member im k
NotMember k -> B <$> IM.notMember im k
Null -> B <$> IM.null im
Lookup k -> M <$> IM.lookup im k
Insert k v -> do
IM.insert im k v
pure None
InsertWithAdd k v -> do
IM.insertWith im (+) k v
pure None
Delete k -> B <$> IM.delete im k
Delete_ k -> do
IM.delete_ im k
pure None
LookupGE k -> KV <$> IM.lookupGE im k
LookupGT k -> KV <$> IM.lookupGT im k
LookupLE k -> KV <$> IM.lookupLE im k
LookupLT k -> KV <$> IM.lookupLT im k
LookupMin -> KV <$> IM.lookupMin im
LookupMax -> KV <$> IM.lookupMax im
DeleteMin -> KV <$> IM.deleteMin im
DeleteMax -> KV <$> IM.deleteMax im
prop_randomTest :: Init -> QC.Property
prop_randomTest Init {..} = QCM.monadicIO $ do
im <- QCM.run imM
q <- QCM.pick $ QC.chooseInt (1, 5 * n)
qs <- QCM.pick $ QC.vectorOf q (queryGen n)
foldM_
( \ref query -> do
let (!ref', !expected) = handleRef ref query
res <- QCM.run $ handleAcl im query
QCM.assertWith (expected == res) $ show (query, expected, res)
-- check the map contents:
let assocsE = VU.fromList $ IMR.assocs ref'
assocs <- QCM.run $ IM.assocs im
QCM.assertWith (assocsE == assocs) $ show ("- assocs", assocsE, assocs)
let keysE = VU.fromList $ IMR.keys ref'
keys <- QCM.run $ IM.keys im
QCM.assertWith (keysE == keys) $ show ("- keys", keysE, keys)
let elemsE = VU.fromList $ IMR.elems ref'
elems <- QCM.run $ IM.elems im
QCM.assertWith (elemsE == elems) $ show ("- elems", elemsE, elems)
let sizeE = IMR.size ref'
size <- QCM.run $ IM.size im
QCM.assertWith (sizeE == size) $ show ("- size", sizeE, size)
pure ref'
)
ref0
qs
tests :: [TestTree]
tests =
[ QC.testProperty "randomTest" prop_randomTest
]