ac-library-hs-1.5.1.0: src/AtCoder/Extra/SegTree2d/Dense.hs
{-# LANGUAGE RecordWildCards #-}
-- | Two-dimensional segment tree for commutative monoids in \([0, w) \times [0, h)\).
--
-- ==== __Internals__
-- Take a 2x4 matrix as an example:
--
-- @
-- 5 6 7 8
-- 1 2 3 4
-- @
--
-- Extend each row as a segment tree:
--
-- @
-- - 22 11 15 5 6 7 8
-- - 10 3 7 1 2 3 4
-- @
--
-- Then extend each column as a segment tree:
--
-- @
-- - - - - - - - -
-- - 30 14 22 6 8 10 12
-- - 26 11 15 5 6 7 8
-- - 10 3 7 1 2 3 4
-- @
--
-- ==== __ Example__
-- Create a two-dimensional segment tree for size (w, h) = (4, 2):
--
-- >>> import AtCoder.Extra.SegTree2d.Dense qualified as Seg
-- >>> import Data.Semigroup (Sum (..))
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> seg <- Seg.build @_ @(Sum Int) 4 2 $ VU.fromList [0, 1, 2, 3, 4, 5, 6, 7]
--
-- Get monoid product in \([x_1, x_2) \times [y_1, y_2)\) with `prod`:
--
-- >>> Seg.prod seg {- x -} 1 4 {- y -} 0 1
-- Sum {getSum = 6}
--
-- Monoid values can be altered:
--
-- >>> Seg.write seg 1 1 20
-- >>> Seg.prod seg {- x -} 0 2 {- y -} 0 2
-- Sum {getSum = 25}
--
-- >>> Seg.allProd seg
-- Sum {getSum = 43}
--
-- @since 1.2.3.0
module AtCoder.Extra.SegTree2d.Dense
( -- * DenseSegTree2d
DenseSegTree2d (..),
-- * Constructors
new,
build,
build',
-- * Read
read,
readMaybe,
-- * Write
write,
modify,
modifyM,
-- * Monoid product
prod,
allProd,
)
where
import AtCoder.Internal.Assert qualified as ACIA
import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)
import Control.Monad.ST (ST)
import Data.Bits
import Data.Foldable (for_)
import Data.Maybe (fromMaybe)
import Data.Vector qualified as V
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)
-- | Two-dimensional segment tree.
--
-- @since 1.2.3.0
data DenseSegTree2d s a = DenseSegTree2d
{ -- | Height
--
-- @since 1.2.3.0
hDst :: {-# UNPACK #-} !Int,
-- | Width
--
-- @since 1.2.3.0
wDst :: {-# UNPACK #-} !Int,
-- | Monoid values
--
-- @since 1.2.3.0
dataDst :: !(VUM.MVector s a)
}
-- | \(O(hw)\) Creates a `DenseSegTree2d` for \([0, w) \times [0, h)\) from \(w\) and \(h\).
--
-- @since 1.2.3.0
{-# INLINEABLE new #-}
new ::
(PrimMonad m, Monoid a, VU.Unbox a) =>
-- | Width
Int ->
-- | Height
Int ->
-- | Dense, two-dimensional segment tree
m (DenseSegTree2d (PrimState m) a)
new wDst hDst = stToPrim $ do
dataDst <- VUM.replicate (4 * wDst * hDst) mempty
pure DenseSegTree2d {..}
-- | \(O(hw)\) Creates a `DenseSegTree2d` from width, height and one-dimensional vector of
-- monoid values.
--
-- @since 1.2.3.0
{-# INLINE build #-}
build ::
(HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>
-- | Width
Int ->
-- | Height
Int ->
-- | Vector of monoid values
VU.Vector a ->
-- | Dense, two-dimensional segment tree
m (DenseSegTree2d (PrimState m) a)
build w h xs = stToPrim $ buildST $ V.unfoldrExactN h (VU.splitAt w) xs
where
!_ = ACIA.runtimeAssert (VU.length xs == w * h) "AtCoder.Extra.SegTree2d.Dense.build: vector length mismatch"
-- | \(O(hw)\) Creates a `DenseSegTree2d` from a two-dimensional vector of monoid values.
-- The vector must be indexed by \(y\) first then \(x\): @vec V.! y VU.! x@.
--
-- ==== Constraints
-- - The length of the monoid value vector must be \(hw\).
--
-- @since 1.2.3.0
{-# INLINE build' #-}
build' ::
(HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>
-- | Two-dimensional vector of monoid values
V.Vector (VU.Vector a) ->
-- | Dense, two-dimensional segment tree
m (DenseSegTree2d (PrimState m) a)
build' xs = stToPrim $ buildST xs
-- | \(O(1)\) Returns the monoid value at \((x, y)\).
--
-- @since 1.2.3.0
{-# INLINE read #-}
read :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> m a
read DenseSegTree2d {..} x y = do
let !_ = ACIA.checkPoint2d "AtCoder.Extra.SegTree2d.Dense.read" x y wDst hDst
VGM.read dataDst $ idx wDst (y + hDst) (x + wDst)
-- | \(O(1)\) Returns the monoid value at \((x, y)\), or `Nothing` if the point is out of the
-- bounds.
--
-- @since 1.2.3.0
{-# INLINE readMaybe #-}
readMaybe :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> m (Maybe a)
readMaybe DenseSegTree2d {..} x y
| ACIA.testPoint2d x y wDst hDst = do
Just <$> VGM.read dataDst (idx wDst (y + hDst) (x + wDst))
| otherwise = pure Nothing
-- | \(O(\log h \log w)\) Writes to the \(k\)-th original point's monoid value.
--
-- @since 1.2.3.0
{-# INLINE write #-}
write :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> a -> m ()
write seg x y a = stToPrim $ do
modifyM seg (pure . const a) x y
-- | \(O(\log h \log w)\) Given a user function \(f\), modifies the monoid value at \((x, y)\) with
-- it.
--
-- @since 1.2.3.0
{-# INLINE modify #-}
modify :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> (a -> a) -> Int -> Int -> m ()
modify seg f x y = stToPrim $ do
modifyM seg (pure . f) x y
-- | \(O(\log h \log w)\) Given a user function \(f\), modifies the monoid value at \((x, y)\) with
-- it.
--
-- @since 1.2.3.0
{-# INLINEABLE modifyM #-}
modifyM :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> (a -> m a) -> Int -> Int -> m ()
modifyM DenseSegTree2d {..} f x0_ y0_ = do
let !_ = ACIA.checkPoint2d "AtCoder.Extra.SegTree2d.Dense.modifyM" x0_ y0_ wDst hDst
let y0 = y0_ + hDst
let x0 = x0_ + wDst
VGM.modifyM dataDst f (idx wDst y0 x0)
stToPrim $ do
-- right to left
let updateCurrentRow 0 = pure ()
updateCurrentRow x = do
xl <- VGM.read dataDst (idx wDst y0 (2 * x + 0))
xr <- VGM.read dataDst (idx wDst y0 (2 * x + 1))
VGM.write dataDst (idx wDst y0 x) $! xl <> xr
updateCurrentRow (x `div` 2)
updateCurrentRow (x0 `div` 2)
-- down to up
let updateOtherRow 0 = pure ()
updateOtherRow y = do
let updateRow 0 = pure ()
updateRow x = do
xl <- VGM.read dataDst (idx wDst (2 * y + 0) x)
xr <- VGM.read dataDst (idx wDst (2 * y + 1) x)
VGM.write dataDst (idx wDst y x) $! xl <> xr
updateRow (x `div` 2)
updateRow x0
updateOtherRow (y `div` 2)
updateOtherRow (y0 `div` 2)
-- | \(O(\log h \log w)\) Returns monoid product \(\Pi_{p \in [x_1, x_2) \times [y_1, y_2)} a_p\).
--
-- @since 1.2.3.0
{-# INLINE prod #-}
prod :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> Int -> Int -> m a
prod seg@DenseSegTree2d {..} x1 x2 y1 y2 = stToPrim $ do
let !_ = ACIA.checkRectShape "AtCoder.Extra.SegTree2d.Dense.prodST" x1 x2 y1 y2
prodST seg (max 0 x1) (min wDst x2) (max 0 y1) (min hDst y2)
-- | \(O(1)\) Returns monoid product \(\Pi_{p \in [0, w) \times [0, h)} a_p\).
--
-- @since 1.2.3.0
{-# INLINE allProd #-}
allProd :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => DenseSegTree2d (PrimState m) a -> m a
allProd DenseSegTree2d {..} = stToPrim $ do
fromMaybe mempty <$> VGM.readMaybe dataDst (idx wDst 1 1)
-- -------------------------------------------------------------------------------------------------
-- Internal
-- -------------------------------------------------------------------------------------------------
{-# INLINE idx #-}
idx :: Int -> Int -> Int -> Int
idx w y x = y * (2 * w) + x
{-# INLINEABLE buildST #-}
buildST :: (HasCallStack, Monoid a, VU.Unbox a) => V.Vector (VU.Vector a) -> ST s (DenseSegTree2d s a)
buildST vec = do
let hDst = V.length vec
let wDst = VU.length (V.head vec)
-- NOTE: It's zero-based and we do not ceil H/W to 2^n, still the indexing works fine:
-- 1 2 3
-- 11 6 5 1 2 3
dataDst <- VUM.replicate (4 * hDst * wDst) mempty
-- copy the base matrix in [w, 2w) \times [h, 2h):
V.iforM_ vec $ \y vs -> do
VU.iforM_ vs $ \x v -> do
VGM.write dataDst (idx wDst (hDst + y) (wDst + x)) v
-- extend the row (y >= h) as a segment tree's internal vector:
for_ [hDst .. 2 * hDst - 1] $ \y -> do
for_ [wDst - 1, wDst - 2 .. 0] $ \x -> do
xl <- VGM.read dataDst (idx wDst y (2 * x + 0))
xr <- VGM.read dataDst (idx wDst y (2 * x + 1))
VGM.write dataDst (idx wDst y x) $! xl <> xr
-- extend each column as a segment tree:
-- NOTE (pref): interate from y then x for contiguous memory access
for_ [hDst - 1, hDst - 2 .. 0] $ \y -> do
for_ [0 .. 2 * wDst - 1] $ \x -> do
xl <- VGM.read dataDst (idx wDst (2 * y + 0) x)
xr <- VGM.read dataDst (idx wDst (2 * y + 1) x)
VGM.write dataDst (idx wDst y x) $! xl <> xr
pure DenseSegTree2d {..}
{-# INLINEABLE prodST #-}
prodST :: (HasCallStack, Monoid a, VU.Unbox a) => DenseSegTree2d s a -> Int -> Int -> Int -> Int -> ST s a
prodST seg@DenseSegTree2d {..} x1 x2 y1 y2 = do
inner mempty (y1 + hDst) (y2 + hDst - 1)
where
-- inclusive interval [yl, yr]
inner !acc yl yr
| yl > yr = pure acc
| otherwise = do
acc' <-
if testBit yl 0
then (acc <>) <$> prodX seg yl x1 x2
else pure acc
acc'' <-
if not $ testBit yr 0
then (<> acc') <$> prodX seg yr x1 x2
else pure acc'
inner acc'' ((yl + 1) .>>. 1) ((yr - 1) .>>. 1)
{-# INLINEABLE prodX #-}
prodX :: (HasCallStack, Monoid a, VU.Unbox a) => DenseSegTree2d s a -> Int -> Int -> Int -> ST s a
prodX DenseSegTree2d {..} y x1 x2 = do
inner mempty (x1 + wDst) (x2 + wDst - 1)
where
-- inclusive interval [xl, xr]
inner !acc xl xr
| xl > xr = pure acc
| otherwise =do
acc' <-
if testBit xl 0
then (acc <>) <$> VGM.read dataDst (idx wDst y xl)
else pure acc
acc'' <-
if not $ testBit xr 0
then (<> acc') <$> VGM.read dataDst (idx wDst y xr)
else pure acc'
inner acc'' ((xl + 1) .>>. 1) ((xr - 1) .>>. 1)