ac-library-hs-1.2.3.0: test/Tests/Extra/SegTree2d/Dense.hs
{-# LANGUAGE RecordWildCards #-}
module Tests.Extra.SegTree2d.Dense (tests) where
import AtCoder.Extra.SegTree2d.Dense qualified as Seg
import Control.Monad.Primitive (PrimMonad, PrimState)
import Control.Monad.ST (RealWorld)
import Data.Foldable (for_)
import Data.Semigroup (Sum (..))
import Data.Vector.Generic.Mutable qualified as VGM
import Data.Vector.Unboxed qualified as VU
import Data.Vector.Unboxed.Mutable qualified as VUM
import Test.QuickCheck.Monadic as QCM
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
data Init = Init
{ wQ :: {-# UNPACK #-} !Int,
hQ :: {-# UNPACK #-} !Int,
q :: {-# UNPACK #-} !Int,
vecM :: !(IO (VUM.MVector RealWorld (Sum Int))),
segM :: !(IO (Seg.DenseSegTree2d RealWorld (Sum Int)))
}
instance Show Init where
show Init {..} = show ("Init", wQ, hQ, q)
instance QC.Arbitrary Init where
arbitrary = do
QC.Positive q <- QC.arbitrary
w <- QC.chooseInt (1, 15)
h <- QC.chooseInt (1, 15)
let vecM = VUM.replicate (w * h) mempty
pure $ Init w h q vecM (Seg.new w h)
data Query
= Read !(Int, Int)
| ReadMaybe !(Int, Int)
| Write !(Int, Int) !Int
| ModifyAdd !Int !(Int, Int)
| Prod !(Int, Int, Int, Int)
| AllProd
deriving
( Eq,
Show
)
genQuery :: Int -> Int -> QC.Gen Query
genQuery w h = do
QC.oneof
[ Read <$> xy,
ReadMaybe <$> xy',
Write <$> xy <*> val,
ModifyAdd <$> val <*> xy,
Prod <$> rect,
pure AllProd
]
where
xy = (,) <$> QC.chooseInt (0, w - 1) <*> QC.chooseInt (0, h - 1)
xy' = (,) <$> QC.chooseInt (-1, w) <*> QC.chooseInt (-1, h)
rect = do
xl <- QC.chooseInt (-1, w)
xr <- QC.chooseInt (xl, w)
yl <- QC.chooseInt (-1, h)
yr <- QC.chooseInt (yl, h)
pure (xl, xr, yl, yr)
val = QC.arbitrary @Int
-- | Arbitrary return type for the `Query` result.
data Result
= None
| S !(Sum Int)
| I !Int
| MS !(Maybe (Sum Int))
deriving (Show, Eq)
-- | containers. (referencial implementation)
handleRef :: Int -> Int -> VUM.MVector RealWorld (Sum Int) -> Query -> IO Result
handleRef w h vec q = case q of
Read (!x, !y) -> do
S <$> VGM.read vec (w * y + x)
ReadMaybe (!x, !y)
| 0 <= x && x < w && 0 <= y && y < h -> MS . Just <$> VGM.read vec (w * y + x)
| otherwise -> pure $ MS Nothing
Write (!x, !y) v -> do
VGM.write vec (w * y + x) (Sum v)
pure None
ModifyAdd dw (!x, !y) -> do
-- FIXME: why die?
VGM.modify vec (+ Sum dw) (w * y + x)
pure None
Prod (!x1, !x2, !y1, !y2) -> do
vec' <- VU.unsafeFreeze vec
pure
. S
. VU.sum
$ VU.ifilter
( \i _ ->
let (!y, !x) = i `divMod` w
in x1 <= x && x < x2 && y1 <= y && y < y2
)
vec'
AllProd -> do
vec' <- VU.unsafeFreeze vec
pure . S $ VU.sum vec'
handleAcl :: (PrimMonad m) => Seg.DenseSegTree2d (PrimState m) (Sum Int) -> Query -> m Result
handleAcl seg q = case q of
Read (!x, !y) -> do
S <$> Seg.read seg x y
ReadMaybe (!x, !y) -> do
MS <$> Seg.readMaybe seg x y
Write (!x, !y) v -> do
Seg.write seg x y $ Sum v
pure None
ModifyAdd w (!x, !y) -> do
Seg.modify seg (+ Sum w) x y
pure None
Prod (!x1, !x2, !y1, !y2) -> do
S <$> Seg.prod seg x1 x2 y1 y2
AllProd -> do
S <$> Seg.allProd seg
prop_randomTest :: Init -> QC.Property
prop_randomTest Init {..} = QCM.monadicIO $ do
seg <- QCM.run segM
vec <- QCM.run vecM
qs <- QCM.pick $ QC.vectorOf q (genQuery wQ hQ)
for_ qs $ \query -> do
expected <- QCM.run $ handleRef wQ hQ vec query
actual <- QCM.run $ handleAcl seg query
QCM.assertWith (expected == actual) $ show (query, expected, actual)
unit_zero :: TestTree
unit_zero = testCase "zero" $ do
seg <- Seg.new @_ @(Sum Int) 0 0
(@?= mempty) =<< Seg.prod seg 0 10 0 10
(@?= mempty) =<< Seg.allProd seg
pure ()
tests :: [TestTree]
tests =
[ unit_zero,
QC.testProperty "random test" prop_randomTest
]