ac-library-hs-1.2.2.0: src/AtCoder/Extra/DynLazySegTree/Raw.hs
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_HADDOCK hide #-}
-- | Base module of a dynamic, lazily propagated segment tree.
--
-- @since 1.2.1.0
module AtCoder.Extra.DynLazySegTree.Raw
( -- * Dynamic, lazily propagated segment tree
DynLazySegTree (..),
-- * Re-exports
SegAct (..),
P.Index (..),
-- * Constructors
newST,
newRootST,
newNodeST,
newSeqST,
-- * Accessing elements
modifyMST,
-- * Products
prodST,
-- prodMaybe,
-- * Applications
applyInST,
-- * Tree operations
copyIntervalWithST,
resetIntervalST,
-- * Binary searches
maxRightM,
-- -- * Conversions
-- freezeST,
)
where
import AtCoder.Extra.Pool qualified as P
import AtCoder.Internal.Assert qualified as ACIA
import AtCoder.LazySegTree (SegAct (..))
import Control.Monad (when)
import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)
import Control.Monad.ST (ST)
import Data.Coerce (coerce)
import Data.Maybe (fromMaybe)
import Data.Vector.Generic qualified as VG
import Data.Vector.Generic.Mutable qualified as VGM
import Data.Vector.Unboxed qualified as VU
import Data.Vector.Unboxed.Mutable qualified as VUM
import GHC.Stack (HasCallStack)
import Prelude hiding (read)
-- | A dynamic, lazily propagated segment tree that covers a half-open interval \([l_0, r_0)\).
-- The nodes are instantinated as needed.
--
-- @since 1.2.1.0
data DynLazySegTree s f a = DynLazySegTree
{ -- | The maximum number of nodes allocated
--
-- @since 1.2.1.0
capacityLdst :: {-# UNPACK #-} !Int,
-- | Whether the data is persistent or not
--
-- @since 1.2.1.0
isPersistentLdst :: {-# UNPACK #-} !Bool,
-- | Left index boundary (inclusive)
--
-- @since 1.2.1.0
l0Ldst :: {-# UNPACK #-} !Int,
-- | Right index boundary (exclusive)
--
-- @since 1.2.1.0
r0Ldst :: {-# UNPACK #-} !Int,
-- | Initial monoid value assignment \(g: (l, r) \rightarrow a\)
--
-- @since 1.2.1.0
initialProdLdst :: !(Int -> Int -> a),
-- | `Pool` for free slot management.
--
-- @since 1.2.1.0
poolLdst :: !(P.Pool s ()),
-- | Decomposed node storage: left children
--
-- @since 1.2.1.0
lLdst :: !(VUM.MVector s P.Index),
-- | Decomposed node storage: right children
--
-- @since 1.2.1.0
rLdst :: !(VUM.MVector s P.Index),
-- | Decomposed node storage: monoid value
--
-- @since 1.2.1.0
xLdst :: !(VUM.MVector s a),
-- | Decomposed node storage: lazily propagated monoid action
--
-- @since 1.2.1.0
lazyLdst :: !(VUM.MVector s f)
}
-- | \(O(n)\)
--
-- @since 1.2.1.0
{-# INLINEABLE newST #-}
newST :: (HasCallStack, VU.Unbox f, VU.Unbox a) => Bool -> Int -> Int -> Int -> (Int -> Int -> a) -> ST s (DynLazySegTree s f a)
newST isPersistentLdst capacityLdst l0Ldst r0Ldst initialProdLdst = do
let !_ = ACIA.runtimeAssert (l0Ldst <= r0Ldst) $ "AtCoder.Extra.DynLazySegTree.Raw.newST: given invalid interval " ++ show (l0Ldst, r0Ldst)
poolLdst <- P.new capacityLdst
lLdst <- VUM.unsafeNew capacityLdst
rLdst <- VUM.unsafeNew capacityLdst
xLdst <- VUM.unsafeNew capacityLdst
lazyLdst <- VUM.unsafeNew capacityLdst
pure DynLazySegTree {..}
-- | \(O(1)\)
--
-- @since 1.2.1.0
{-# INLINE newRootST #-}
newRootST :: (HasCallStack, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> ST s P.Index
newRootST dst@DynLazySegTree {..} = do
newNodeInST dst l0Ldst r0Ldst
-- | \(O(1)\)
--
-- @since 1.2.1.0
{-# INLINE newNodeST #-}
newNodeST :: (HasCallStack, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> a -> ST s P.Index
newNodeST DynLazySegTree {..} !x = do
i <- P.alloc poolLdst ()
VGM.write lLdst (coerce i) P.undefIndex
VGM.write rLdst (coerce i) P.undefIndex
VGM.write xLdst (coerce i) x
VGM.write lazyLdst (coerce i) mempty
pure i
-- | \(O(L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE newSeqST #-}
newSeqST :: (HasCallStack, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> VU.Vector a -> ST s P.Index
newSeqST dst@DynLazySegTree {..} !xs = do
let !_ = ACIA.runtimeAssert (l0Ldst == 0 && r0Ldst == VU.length xs) "AtCoder.Extra.DynLazySegTree.Raw: mismatch between the bounds and the input vector: the bounds must be [0, n)"
-- run DFS and allocate nodes from left to right
let dfs l r
| l == r = pure P.undefIndex
| r - l == 1 = newNodeST dst $ xs VG.! l
| otherwise = do
let m = (l + r) `div` 2
lRoot <- dfs l m
rRoot <- dfs m r
xlRoot <- VGM.read xLdst (coerce lRoot)
xrRoot <- VGM.read xLdst (coerce rRoot)
let !x = xlRoot <> xrRoot
root <- newNodeST dst x
VGM.write lLdst (coerce root) lRoot
VGM.write rLdst (coerce root) rRoot
pure root
dfs 0 (VU.length xs)
-- | \(O(1)\)
--
-- ==== Constraints
-- - The interval must be non-empty
--
-- @since 1.2.1.0
{-# INLINE newNodeInST #-}
newNodeInST :: (HasCallStack, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> Int -> Int -> ST s P.Index
newNodeInST dst@DynLazySegTree {initialProdLdst} l r = do
let !_ = ACIA.runtimeAssert (r > l) $ "AtCoder.Extra.DynLazySegTree.Raw.nodeNodeInST: not empty or negative interval: " ++ show (l, r)
newNodeST dst $! initialProdLdst l r
-- | \(O(\log L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE modifyMST #-}
modifyMST :: forall m f a. (HasCallStack, PrimMonad m, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree (PrimState m) f a -> P.Index -> (a -> m a) -> Int -> m P.Index
modifyMST dst@DynLazySegTree {..} root f i = inner root l0Ldst r0Ldst
where
!_ = ACIA.checkIndexBounded "AtCoder.Extra.DynLazySegTree.Raw.modifyMST" i l0Ldst r0Ldst
inner :: P.Index -> Int -> Int -> m P.Index
inner c l r
| r - l == 1 = do
-- let !_ = ACIA.runtimeAssert (i == l) ""
c' <- stToPrim $ cloneOnWriteST dst c
VGM.modifyM xLdst f (coerce c')
stToPrim $ VGM.write lazyLdst (coerce c') mempty
pure c'
| otherwise = do
stToPrim $ propST dst c l r
let m = (l + r) `div` 2
-- lazily allocate left and right children:
cl <- stToPrim $ do
j <- VGM.read lLdst (coerce c)
if P.nullIndex j
then do
j' <- newNodeInST dst l m
VGM.write lLdst (coerce c) j'
pure j'
else pure j
cr <- stToPrim $ do
j <- VGM.read rLdst (coerce c)
if P.nullIndex j
then do
j' <- newNodeInST dst m r
VGM.write rLdst (coerce c) j'
pure j'
else pure j
c' <- stToPrim $ cloneOnWriteST dst c
if i < m
then stToPrim . VGM.write lLdst (coerce c') =<< inner cl l m
else stToPrim . VGM.write rLdst (coerce c') =<< inner cr m r
stToPrim $ do
-- Note that either left or right child of `c'` is updated in the above `if`
clx <- VGM.read xLdst . coerce =<< VGM.read lLdst (coerce c')
crx <- VGM.read xLdst . coerce =<< VGM.read rLdst (coerce c')
VGM.write xLdst (coerce c') $! clx <> crx
pure c'
-- | \(O(\log L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE applyInST #-}
applyInST :: forall f a s. (HasCallStack, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> P.Index -> Int -> Int -> f -> ST s P.Index
applyInST dst@DynLazySegTree {..} root0 ql0 qr0 !f
| ql0 == qr0 = pure root0
| otherwise = inner root0 l0Ldst r0Ldst ql0 qr0
where
!_ = ACIA.checkIntervalBounded "AtCoder.Extra.DynLazySegTree.Raw.applyInST" ql0 qr0 l0Ldst r0Ldst
!_ = ACIA.runtimeAssert (not (P.nullIndex root0)) "AtCoder.Extra.DynLazySegTree.Raw.applyInST"
-- left to right
-- - l, r: node interval
-- - ql, qr: queried interval
inner :: P.Index -> Int -> Int -> Int -> Int -> ST s P.Index
inner c_ l r ql_ qr_ = do
c <- if P.nullIndex c_ then newNodeInST dst l r else pure c_
if len <= 0
then do
-- A node of length zero would be created if the interval length is odd. It's OK:
pure c
else
if l == ql && r == qr
then do
c' <- cloneOnWriteST dst c
VGM.modify xLdst (segActWithLength len f) (coerce c')
VGM.modify lazyLdst (f <>) (coerce c')
pure c'
else do
propST dst c l r
let m = (l + r) `div` 2
c' <- cloneOnWriteST dst c
VGM.modifyM lLdst (\i -> inner i l m ql qr) (coerce c')
VGM.modifyM rLdst (\i -> inner i m r ql qr) (coerce c')
clx <- VGM.read xLdst . coerce =<< VGM.read lLdst (coerce c')
crx <- VGM.read xLdst . coerce =<< VGM.read rLdst (coerce c')
VGM.write xLdst (coerce c') $! clx <> crx
pure c'
where
-- shrink target interval to node interval
ql = max ql_ l
qr = min qr_ r
len = qr - ql
-- | \(O(\log L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE prodST #-}
prodST :: forall f a s. (HasCallStack, SegAct f a, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> P.Index -> Int -> Int -> ST s a
prodST DynLazySegTree {..} root0 ql0 qr0
| ql0 >= qr0 || P.nullIndex root0 = pure mempty
| otherwise = inner root0 l0Ldst r0Ldst ql0 qr0 mempty mempty
where
!_ = ACIA.checkIntervalBounded "AtCoder.Extra.DynLazySegTree.Raw.prodST" ql0 qr0 l0Ldst r0Ldst
-- left to right
-- - l, r: node interval
-- - ql, qr: queried interval
inner :: P.Index -> Int -> Int -> Int -> Int -> a -> f -> ST s a
inner c l r ql_ qr_ !x !f
| len <= 0 = pure x
| P.nullIndex c = do
pure $! x <> segActWithLength len f (initialProdLdst ql qr)
| l == ql && r == qr = do
cx <- VGM.read xLdst (coerce c)
pure $! x <> segActWithLength len f cx
| otherwise = do
let m = (l + r) `div` 2
!f' <- (f <>) <$> VGM.read lazyLdst (coerce c)
cl <- VGM.read lLdst (coerce c)
x' <- inner cl l m ql qr x f'
cr <- VGM.read rLdst (coerce c)
x'' <- inner cr m r ql qr x' f'
pure x''
where
-- shrink target interval to node interval
ql = max ql_ l
qr = min qr_ r
len = qr - ql
-- | \(O(\log L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE copyIntervalWithST #-}
copyIntervalWithST ::
forall f a s.
(HasCallStack, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) =>
-- | Dynamic segment tree
DynLazySegTree s f a ->
-- | Root to be modified
P.Index ->
-- | Another segment tree
P.Index ->
-- | \(l\)
Int ->
-- | \(r\)
Int ->
-- | Action \(f\)
f ->
-- | New root
ST s P.Index
copyIntervalWithST dst@DynLazySegTree {..} rootA rootB ql0 qr0 !f0
| rootA == rootB = pure rootA
| otherwise = do
if ql0 >= qr0
then pure rootA
else do
rootA' <- cloneOnWriteST dst rootA
inner rootA' rootB l0Ldst r0Ldst ql0 qr0 f0
pure rootA'
where
!_ = ACIA.checkIntervalBounded "AtCoder.Extra.DynLazySegTree.Raw.copyIntervalWithST" ql0 qr0 l0Ldst r0Ldst
!_ = ACIA.runtimeAssert (not (P.nullIndex rootA)) "AtCoder.Extra.DynLazySegTree.Raw.copyIntervalWithST: given null"
-- left to right
-- - l, r: node interval
-- - ql, qr: queried interval
inner :: P.Index -> P.Index -> Int -> Int -> Int -> Int -> f -> ST s ()
inner c d l r ql_ qr_ !f
| len <= 0 = pure () -- `c` is already cloned
| l == ql && r == qr = do
if not (P.nullIndex d)
then do
VGM.write xLdst (coerce c) . segActWithLength len f =<< VGM.read xLdst (coerce d)
VGM.write lazyLdst (coerce c) . (f <>) =<< VGM.read lazyLdst (coerce d)
VGM.write lLdst (coerce c) =<< VGM.read lLdst (coerce d)
VGM.write rLdst (coerce c) =<< VGM.read rLdst (coerce d)
else do
VGM.write xLdst (coerce c) . segActWithLength len f $! initialProdLdst l r
VGM.write lazyLdst (coerce c) f
VGM.write lLdst (coerce c) P.undefIndex
VGM.write rLdst (coerce c) P.undefIndex
| otherwise = do
let m = (l + r) `div` 2
VGM.modifyM lLdst (\i -> if P.nullIndex i then newNodeST dst (initialProdLdst l m) else cloneOnWriteST dst i) (coerce c)
cl <- VGM.read lLdst (coerce c)
VGM.modifyM rLdst (\i -> if P.nullIndex i then newNodeST dst (initialProdLdst m r) else cloneOnWriteST dst i) (coerce c)
cr <- VGM.read rLdst (coerce c)
cLazy <- VGM.exchange lazyLdst (coerce c) mempty
VGM.modify xLdst (segActWithLength (m - l) cLazy) (coerce cl)
VGM.modify xLdst (segActWithLength (r - m) cLazy) (coerce cr)
VGM.modify lazyLdst (cLazy <>) (coerce cl)
VGM.modify lazyLdst (cLazy <>) (coerce cr)
!f' <- if P.nullIndex d then pure f else (f <>) <$> VGM.read lazyLdst (coerce d)
dl' <- fromMaybe P.undefIndex <$> VGM.readMaybe lLdst (coerce d)
dr' <- fromMaybe P.undefIndex <$> VGM.readMaybe rLdst (coerce d)
inner cl dl' l m ql qr f'
inner cr dr' m r ql qr f'
clx <- VGM.read xLdst (coerce cl)
crx <- VGM.read xLdst (coerce cr)
VGM.write xLdst (coerce c) $! clx <> crx
where
!_ = ACIA.runtimeAssert (not (P.nullIndex c)) "AtCoder.Extra.DynLazySegTree.Raw.copyIntervalWithST: implementation error"
-- shrink target interval to node interval
ql = max ql_ l
qr = min qr_ r
len = qr - ql
-- | \(O(\log L)\) Resets an interval \([l, r)\) to initial monoid values.
--
-- @since 1.2.1.0
{-# INLINEABLE resetIntervalST #-}
resetIntervalST ::
forall f a s.
(HasCallStack, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) =>
DynLazySegTree s f a ->
P.Index ->
Int ->
Int ->
ST s P.Index
resetIntervalST dst@DynLazySegTree {..} root ql0 qr0
| ql0 == qr0 = pure root
| P.nullIndex root = pure P.undefIndex
| ql0 == l0Ldst && qr0 == r0Ldst = do
-- for the case of non-persistent segment tere, we should update the root in-place:
root' <- cloneOnWriteST dst root
VGM.write xLdst (coerce root') $! initialProdLdst l0Ldst r0Ldst
VGM.write lLdst (coerce root') P.undefIndex
VGM.write rLdst (coerce root') P.undefIndex
VGM.write lazyLdst (coerce root') mempty
pure root'
| otherwise = inner root l0Ldst r0Ldst ql0 qr0
where
!_ = ACIA.checkIntervalBounded "AtCoder.Extra.DynLazySegTree.Raw.resetIntervalST" ql0 qr0 l0Ldst r0Ldst
-- replace interval with null
inner :: P.Index -> Int -> Int -> Int -> Int -> ST s P.Index
inner c l r ql_ qr_
-- TODO: shall we allocate new node?
| len <= 0 = pure c
| P.nullIndex c = pure P.undefIndex
-- NOTE: we're returning `undefIndex`, but we can instead free the subtree if it's not persistent
| ql <= l && r <= qr = pure P.undefIndex
| r - l == 1 = pure c
| otherwise = do
propST dst c l r
let m = (l + r) `div` 2
c' <- cloneOnWriteST dst c
VGM.modifyM lLdst (\i -> inner i l m ql qr) (coerce c')
cl <- VGM.read lLdst (coerce c')
VGM.modifyM rLdst (\i -> inner i m r ql qr) (coerce c')
cr <- VGM.read rLdst (coerce c')
clx <- if P.nullIndex cl then pure $! initialProdLdst l m else VGM.read xLdst (coerce cl)
crx <- if P.nullIndex cr then pure $! initialProdLdst m r else VGM.read xLdst (coerce cr)
VGM.write xLdst (coerce c') $! clx <> crx
pure c'
where
-- shrink target interval to node interval
ql = max ql_ l
qr = min qr_ r
len = qr - ql
-- It returns only allocated leaf values
-- {-# INLINEABLE freezeST #-}
-- freezeST :: forall f a s. (HasCallStack, SegAct f a, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> P.Index -> ST s (VU.Vector a)
-- freezeST DynLazySegTree {..} root = do
-- buf <- B.new (r0Ldst - l0Ldst)
--
-- let inner c l r !f
-- | P.nullIndex c = pure ()
-- | r - l == 1 = do
-- x <- VGM.read xLdst (coerce c)
-- B.pushBack buf $! segAct f x
-- | otherwise = do
-- let m = (l + r) `div` 2
-- !f' <- (f <>) <$> VGM.read lazyLdst (coerce c)
-- cl <- VGM.read lLdst (coerce c)
-- cr <- VGM.read rLdst (coerce c)
-- inner cl l m f'
-- inner cr m r f'
--
-- inner root l0Ldst r0Ldst mempty
-- B.unsafeFreeze buf
-- | \(O(\log L)\)
--
-- @since 1.2.1.0
{-# INLINEABLE maxRightM #-}
maxRightM :: (HasCallStack, PrimMonad m, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree (PrimState m) f a -> P.Index -> (a -> m Bool) -> m Int
maxRightM dst@DynLazySegTree {..} root !f = do
(!r, !_) <- inner root l0Ldst r0Ldst mempty
pure r
where
-- FIXME: it should not allocate new nodes
inner c_ l r !x = do
c <- if P.nullIndex c_ then stToPrim $ newNodeInST dst l r else pure c_
xWhole <- stToPrim $ (x <>) <$> VGM.read xLdst (coerce c)
b <- f xWhole
if b
then do
pure (r, xWhole)
else do
if r - l == 1
then pure (l, x)
else do
stToPrim $ propST dst c l r
let m = (l + r) `div` 2
cl <- stToPrim $ VGM.read lLdst (coerce c)
(!k, !xl) <- inner cl l m x
if k < m
then pure (k, xl)
else do
cr <- stToPrim $ VGM.read rLdst (coerce c)
inner cr m r xl
-- -------------------------------------------------------------------------------------------------
-- Internals
-- -------------------------------------------------------------------------------------------------
-- | \(O(1)\) Optionally clones a node depending on the persistency setting.
{-# INLINEABLE cloneOnWriteST #-}
cloneOnWriteST :: (HasCallStack, SegAct f a, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> P.Index -> ST s P.Index
cloneOnWriteST DynLazySegTree {..} c
| not isPersistentLdst || P.nullIndex c = pure c
| otherwise = do
i <- P.alloc poolLdst ()
VGM.write lLdst (coerce i) =<< VGM.read lLdst (coerce c)
VGM.write rLdst (coerce i) =<< VGM.read rLdst (coerce c)
VGM.write xLdst (coerce i) =<< VGM.read xLdst (coerce c)
VGM.write lazyLdst (coerce i) =<< VGM.read lazyLdst (coerce c)
pure i
-- | \(O(1)\)
{-# INLINEABLE propST #-}
propST :: (HasCallStack, SegAct f a, Eq f, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) => DynLazySegTree s f a -> P.Index -> Int -> Int -> ST s ()
propST dst@DynLazySegTree {..} c l r = do
let !_ = ACIA.runtimeAssert (r - l >= 2) "AtCoder.Extra.DynLazySegTree.Raw.propST: the interval must have length more than or equal to `2`"
cLazy <- VGM.read lazyLdst (coerce c)
when (cLazy /= mempty) $ do
let m = (l + r) `div` 2
-- create or clone left child
cl <- do
i <- VGM.read lLdst (coerce c)
if P.nullIndex i
then newNodeInST dst l m
else cloneOnWriteST dst i
VGM.write lLdst (coerce c) cl
VGM.modify xLdst (segActWithLength (m - l) cLazy) (coerce cl)
VGM.modify lazyLdst (cLazy <>) (coerce cl)
-- create or clone right child
cr <- do
i <- VGM.read rLdst (coerce c)
if P.nullIndex i
then newNodeInST dst m r
else cloneOnWriteST dst i
VGM.write rLdst (coerce c) cr
VGM.modify xLdst (segActWithLength (r - m) cLazy) (coerce cr)
VGM.modify lazyLdst (cLazy <>) (coerce cr)
-- clear the lazy action
VGM.write lazyLdst (coerce c) mempty