ac-library-hs-1.3.0.0: test/Tests/Extra/Bisect.hs
module Tests.Extra.Bisect where
import AtCoder.Extra.Bisect
import Data.List qualified as L
import Data.Vector.Generic qualified as VG
import Data.Vector.Unboxed qualified as VU
import Test.Tasty
import Test.Tasty.QuickCheck as QC
-- | Takes half-open interval [l, r).
naiveMaxRightIn :: Int -> Int -> (Int -> Bool) -> VU.Vector Int -> Int
naiveMaxRightIn l r p xs =
(+ l)
. VU.length
. VU.takeWhile p
. VU.take (r - l)
$ VU.drop l xs
naiveLowerBound :: VU.Vector Int -> Int -> Int
naiveLowerBound xs = naiveLowerBoundIn 0 (VU.length xs) xs
naiveLowerBoundIn :: Int -> Int -> VU.Vector Int -> Int -> Int
naiveLowerBoundIn l r xs target = naiveMaxRightIn l r (< target) xs
naiveUpperBound :: VU.Vector Int -> Int -> Int
naiveUpperBound xs = naiveUpperBoundIn 0 (VU.length xs) xs
naiveUpperBoundIn :: Int -> Int -> VU.Vector Int -> Int -> Int
naiveUpperBoundIn l r xs target = naiveMaxRightIn l r (<= target) xs
boundsQueryGen :: Gen (Int, Int, VU.Vector Int)
boundsQueryGen = do
n <- QC.chooseInt (1, 100)
p <- QC.chooseInt (-25, 25)
xs <- VU.fromList . L.sort <$> QC.vectorOf n (QC.chooseInt (-20, 20))
pure (n, p, xs)
maxRightQueryGen :: Gen (Int, Int, VU.Vector Int, [(Int, Int)])
maxRightQueryGen = do
n <- QC.chooseInt (1, 100)
p <- QC.chooseInt (-25, 25)
xs <- VU.fromList . L.sort <$> QC.vectorOf n (QC.chooseInt (-20, 20))
let lrs = [(l, r) | l <- [0 .. n], r <- [l .. n]]
pure (n, p, xs, lrs)
prop_lowerBound :: TestTree
prop_lowerBound = QC.testProperty "lowerBound" $ do
(!_, !target, !xs) <- boundsQueryGen
pure . QC.counterexample (show (target, xs)) $ naiveLowerBound xs target QC.=== lowerBound xs target
prop_lowerBoundIn :: TestTree
prop_lowerBoundIn = QC.testProperty "lowerBoundIn" $ do
(!_, !target, !xs, !lrs) <- maxRightQueryGen
pure . QC.conjoin $
map
( \(!l, !r) ->
naiveLowerBoundIn l r xs target == lowerBoundIn l r xs target
)
lrs
prop_upperBound :: TestTree
prop_upperBound = QC.testProperty "upperBound" $ do
(!_, !target, !xs) <- boundsQueryGen
pure $ naiveUpperBound xs target QC.=== upperBound xs target
prop_upperBoundIn :: TestTree
prop_upperBoundIn = QC.testProperty "upperBoundIn" $ do
(!_, !target, !xs, !lrs) <- maxRightQueryGen
pure . QC.conjoin $
map
( \(!l, !r) ->
naiveUpperBoundIn l r xs target == upperBoundIn l r xs target
)
lrs
prop_maxRight :: TestTree
prop_maxRight = QC.testProperty "maxRight" $ do
(!_, !boundary, !xs, !lrs) <- maxRightQueryGen
pure . QC.conjoin $
map
( \(!l, !r) ->
naiveMaxRightIn l r (<= boundary) xs == maxRight l r (\i -> xs VG.! i <= boundary)
)
lrs
minLeftQueryGen :: Gen (Int, Int, VU.Vector Int, [(Int, Int)])
minLeftQueryGen = do
n <- QC.chooseInt (1, 100)
p <- QC.chooseInt (-25, 25)
xs <- VU.fromList . L.sort <$> QC.vectorOf n (QC.chooseInt (-20, 20))
let lrs = [(l, r) | l <- [0 .. n], r <- [l .. n]]
pure (n, p, xs, lrs)
prop_minLeft :: TestTree
prop_minLeft = QC.testProperty "minLeft" $ do
(!_, !boundary, !xs, !lrs) <- minLeftQueryGen
pure . QC.conjoin $
map
( \(!l, !r) ->
let expected =
(r -)
. VU.length
. VU.takeWhile (>= boundary)
. VU.reverse
. VU.take (r - l)
$ VU.drop l xs
res = minLeft l r (\i -> xs VG.! i >= boundary)
in QC.counterexample (show ((l, r), boundary, xs)) $
expected QC.=== res
)
lrs
tests :: [TestTree]
tests =
[ prop_lowerBound,
prop_upperBound,
prop_lowerBoundIn,
prop_upperBoundIn,
prop_maxRight,
prop_minLeft
]