packages feed

ac-library-hs 1.2.2.1 → 1.2.3.0

raw patch · 16 files changed

+1631/−81 lines, 16 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ AtCoder.Extra.LazyKdTree: new :: (HasCallStack, PrimMonad m, Monoid f, Unbox f, Monoid a, Unbox a) => Vector Int -> Vector Int -> m (LazyKdTree (PrimState m) f a)
+ AtCoder.Extra.SegTree2d: SegTree2d :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Vector Int -> !Vector Int -> !Vector Int -> !Vector Int -> !MVector s a -> !Vector Int -> SegTree2d s a
+ AtCoder.Extra.SegTree2d: [allYSt] :: SegTree2d s a -> !Vector Int
+ AtCoder.Extra.SegTree2d: [dataSt] :: SegTree2d s a -> !MVector s a
+ AtCoder.Extra.SegTree2d: [dictXSt] :: SegTree2d s a -> {-# UNPACK #-} !Vector Int
+ AtCoder.Extra.SegTree2d: [indptrSt] :: SegTree2d s a -> !Vector Int
+ AtCoder.Extra.SegTree2d: [logSt] :: SegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d: [nSt] :: SegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d: [nxSt] :: SegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d: [posSt] :: SegTree2d s a -> !Vector Int
+ AtCoder.Extra.SegTree2d: [sizeSt] :: SegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d: [toLeftSt] :: SegTree2d s a -> !Vector Int
+ AtCoder.Extra.SegTree2d: allProd :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> m a
+ AtCoder.Extra.SegTree2d: build :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => Vector Int -> Vector Int -> Vector a -> m (SegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d: build2 :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => Vector (Int, Int) -> Vector a -> m (SegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d: build3 :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => Vector (Int, Int, a) -> m (SegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d: count :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> Int -> Int -> Int -> Int -> m Int
+ AtCoder.Extra.SegTree2d: data SegTree2d s a
+ AtCoder.Extra.SegTree2d: modify :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> (a -> a) -> Int -> m ()
+ AtCoder.Extra.SegTree2d: modifyM :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> (a -> m a) -> Int -> m ()
+ AtCoder.Extra.SegTree2d: new :: (PrimMonad m, Monoid a, Unbox a) => Vector (Int, Int) -> m (SegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d: prod :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> Int -> Int -> Int -> Int -> m a
+ AtCoder.Extra.SegTree2d: write :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => SegTree2d (PrimState m) a -> Int -> a -> m ()
+ AtCoder.Extra.SegTree2d.Dense: DenseSegTree2d :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !MVector s a -> DenseSegTree2d s a
+ AtCoder.Extra.SegTree2d.Dense: [dataDst] :: DenseSegTree2d s a -> !MVector s a
+ AtCoder.Extra.SegTree2d.Dense: [hDst] :: DenseSegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d.Dense: [wDst] :: DenseSegTree2d s a -> {-# UNPACK #-} !Int
+ AtCoder.Extra.SegTree2d.Dense: allProd :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> m a
+ AtCoder.Extra.SegTree2d.Dense: build :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => Int -> Int -> Vector a -> m (DenseSegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d.Dense: build' :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => Vector (Vector a) -> m (DenseSegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d.Dense: data DenseSegTree2d s a
+ AtCoder.Extra.SegTree2d.Dense: modify :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> (a -> a) -> Int -> Int -> m ()
+ AtCoder.Extra.SegTree2d.Dense: modifyM :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> (a -> m a) -> Int -> Int -> m ()
+ AtCoder.Extra.SegTree2d.Dense: new :: (PrimMonad m, Monoid a, Unbox a) => Int -> Int -> m (DenseSegTree2d (PrimState m) a)
+ AtCoder.Extra.SegTree2d.Dense: prod :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> Int -> Int -> m a
+ AtCoder.Extra.SegTree2d.Dense: read :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> m a
+ AtCoder.Extra.SegTree2d.Dense: readMaybe :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> m (Maybe a)
+ AtCoder.Extra.SegTree2d.Dense: write :: (HasCallStack, PrimMonad m, Monoid a, Unbox a) => DenseSegTree2d (PrimState m) a -> Int -> Int -> a -> m ()
+ AtCoder.Extra.Vector: argsort :: (Ord a, Unbox a) => Vector a -> Vector Int
+ AtCoder.Extra.Vector: unsafePermuteInPlace :: (PrimMonad m, MVector v a) => v (PrimState m) a -> Vector Int -> m ()
+ AtCoder.Extra.Vector: unsafePermuteInPlaceST :: MVector v a => v s a -> Vector Int -> ST s ()
+ AtCoder.Internal.Assert: checkPoint2d :: HasCallStack => String -> Int -> Int -> Int -> Int -> ()
+ AtCoder.Internal.Assert: checkRect :: HasCallStack => String -> Int -> Int -> Int -> Int -> Int -> Int -> ()
+ AtCoder.Internal.Assert: checkRectShape :: HasCallStack => String -> Int -> Int -> Int -> Int -> ()
+ AtCoder.Internal.Assert: errorPoint2d :: HasCallStack => String -> Int -> Int -> Int -> Int -> a
+ AtCoder.Internal.Assert: errorRect :: HasCallStack => String -> Int -> Int -> Int -> Int -> Int -> Int -> a
+ AtCoder.Internal.Assert: errorRectShape :: HasCallStack => String -> Int -> Int -> Int -> Int -> a
+ AtCoder.Internal.Assert: testPoint2d :: HasCallStack => Int -> Int -> Int -> Int -> Bool
+ AtCoder.Internal.Assert: testRect :: HasCallStack => Int -> Int -> Int -> Int -> Int -> Int -> Bool
+ AtCoder.Internal.Assert: testRectShape :: HasCallStack => Int -> Int -> Int -> Int -> Bool
+ AtCoder.Internal.Queue: modifyBack :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> (a -> a) -> Int -> m ()
+ AtCoder.Internal.Queue: modifyBackM :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> (a -> m a) -> Int -> m ()
+ AtCoder.Internal.Queue: modifyFront :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> (a -> a) -> Int -> m ()
+ AtCoder.Internal.Queue: modifyFrontM :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> (a -> m a) -> Int -> m ()
+ AtCoder.Internal.Queue: peekBack :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> m (Maybe a)
+ AtCoder.Internal.Queue: peekFront :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> m (Maybe a)
+ AtCoder.Internal.Queue: popBack :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> m (Maybe a)
+ AtCoder.Internal.Queue: popBack_ :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> m ()
+ AtCoder.Internal.Queue: readBack :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> m a
+ AtCoder.Internal.Queue: readFront :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> m a
+ AtCoder.Internal.Queue: readMaybeBack :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> m (Maybe a)
+ AtCoder.Internal.Queue: readMaybeFront :: (PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> m (Maybe a)
+ AtCoder.Internal.Queue: writeBack :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> a -> m ()
+ AtCoder.Internal.Queue: writeFront :: (HasCallStack, PrimMonad m, Unbox a) => Queue (PrimState m) a -> Int -> a -> m ()

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for acl-hs +## 1.2.3.0 -- March 2025++- Added `Extra.SegTree2d` and `Extra.SegTree2d.Dense`.+ ## 1.2.2.1 -- March 2025  - Reduced build time with `ST` monad and `INLINEABLE` pragmas.
ac-library-hs.cabal view
@@ -4,7 +4,7 @@ -- PVP summary:  +-+------- breaking API changes --               | | +----- non-breaking API additions --               | | | +--- code changes with no API change-version:         1.2.2.1+version:         1.2.3.0 synopsis:        Data structures and algorithms description:   Haskell port of [ac-library](https://github.com/atcoder/ac-library), a library for competitive@@ -79,6 +79,8 @@     AtCoder.Extra.MultiSet     AtCoder.Extra.Pdsu     AtCoder.Extra.Pool+    AtCoder.Extra.SegTree2d+    AtCoder.Extra.SegTree2d.Dense     AtCoder.Extra.Semigroup.Matrix     AtCoder.Extra.Semigroup.Permutation     AtCoder.Extra.Seq@@ -88,6 +90,7 @@     AtCoder.Extra.Tree.Hld     AtCoder.Extra.Tree.Lct     AtCoder.Extra.Tree.TreeMonoid+    AtCoder.Extra.Vector     AtCoder.Extra.WaveletMatrix     AtCoder.Extra.WaveletMatrix.BitVector     AtCoder.Extra.WaveletMatrix.Raw@@ -150,6 +153,8 @@     Tests.Extra.Math     Tests.Extra.Monoid     Tests.Extra.MultiSet+    Tests.Extra.SegTree2d+    Tests.Extra.SegTree2d.Dense     Tests.Extra.Semigroup.Matrix     Tests.Extra.Semigroup.Permutation     Tests.Extra.Seq
src/AtCoder/Extra/KdTree.hs view
@@ -152,7 +152,7 @@   where     (!xs, !ys) = VU.unzip xys --- | \(O(n \log n)\) Collects points in \([x_l, x_r) \times [y_l, y_r)\).+-- | \(O(n)\) Collects points in \([x_1, x_2) \times [y_1, y_2)\). -- -- @since 1.2.2.0 {-# INLINEABLE findPointsIn #-}@@ -160,17 +160,17 @@   (HasCallStack) =>   -- | `KdTree`   KdTree ->-  -- | \(x_l\)+  -- | \(x_1\)   Int ->-  -- | \(x_r\)+  -- | \(x_2\)   Int ->-  -- | \(y_l\)+  -- | \(y_1\)   Int ->-  -- | \(y_r\)+  -- | \(y_2\)   Int ->-  -- | Maximum number of points in \([x_l, x_r) \times [y_l, y_r)\).+  -- | Maximum number of points in \([x_1, x_2) \times [y_1, y_2)\).   Int ->-  -- | Point indices in \([x_l, x_r) \times [y_l, y_r)\).+  -- | Point indices in \([x_1, x_2) \times [y_1, y_2)\).   VU.Vector Int findPointsIn KdTree {..} x1 x2 y1 y2 capacity   | nKt == 0 = VU.empty
src/AtCoder/Extra/LazyKdTree.hs view
@@ -36,6 +36,7 @@     SegAct (..),      -- * Constructors+    new,     build,     build2,     build3,@@ -106,6 +107,23 @@     posLkt :: !(VU.Vector Int)   } +-- | \(O(n \log n)\) Creates a `LazyKdTree` from @xs@ and @ys@.+--+-- ==== Constraints+-- - \(|\mathrm{xs}| = |\mathrm{ys}|+--+-- @since 1.2.3.0+{-# INLINE new #-}+new ::+  (HasCallStack, PrimMonad m, Monoid f, VU.Unbox f, Monoid a, VU.Unbox a) =>+  -- | \(x\) coordnates+  VU.Vector Int ->+  -- | \(y\) coordnates+  VU.Vector Int ->+  -- | `LazyKdTree`+  m (LazyKdTree (PrimState m) f a)+new xs ys = stToPrim $ buildST xs ys (VU.replicate (VU.length xs) mempty)+ -- | \(O(n \log n)\) Creates a `LazyKdTree` from @xs@, @ys@ and @ws@ vectors. -- -- ==== Constraints@@ -205,6 +223,7 @@   -- | Monadic tuple   m () modifyM kt@LazyKdTree {..} f i0 = do+  let !_ = ACIA.checkIndex "AtCoder.Extra.LazyKdTree.modifyM" i0 nLkt   let i_ = posLkt VG.! i0   -- propagate lazily propagated monoid actions from the root:   stToPrim $ for_ [logLkt, logLkt - 1 .. 1] $ \k -> do@@ -221,7 +240,7 @@             inner i'   stToPrim $ inner i_ --- | \(O(\log n)\) Returns monoid product in \([x_l, x_r) \times [y_l, y_r)\).+-- | \(O(\sqrt n)\) Returns monoid product \(\Pi_{p \in [x_1, x_2) \times [y_1, y_2)} a_p\). -- -- @since 1.2.2.0 {-# INLINE prod #-}@@ -229,19 +248,19 @@   (HasCallStack, PrimMonad m, Eq f, SegAct f a, Eq f, VU.Unbox f, Monoid a, VU.Unbox a) =>   -- | `LazyKdTree`   LazyKdTree (PrimState m) f a ->-  -- | \(x_l\)+  -- | \(x_1\)   Int ->-  -- | \(x_r\)+  -- | \(x_2\)   Int ->-  -- | \(y_l\)+  -- | \(y_1\)   Int ->-  -- | \(y_r\)+  -- | \(y_2\)   Int ->-  -- | Monoid product in \([x_l, x_r) \times [y_l, y_r)\)+  -- | \(\Pi_{p \in [x_1, x_2) \times [y_1, y_2)} a_p\)   m a prod kt x1 x2 y1 y2 = stToPrim $ prodST kt x1 x2 y1 y2 --- | \(O(1)\) Returns monoid product of all the points.+-- | \(O(1)\) Returns monoid product \(\Pi_{p \in [-\infty, \infty) \times [-\infty, \infty)} a_p\). -- -- @since 1.2.2.0 {-# INLINE allProd #-}@@ -249,13 +268,13 @@   (PrimMonad m, Monoid a, VU.Unbox a) =>   -- | `LazyKdTree`   LazyKdTree (PrimState m) f a ->-  -- | Monoid product in the whole space.+  -- | \(\Pi_{p \in [-\infty, \infty) \times [-\infty, \infty)} a_p\)   m a allProd kt = do   -- In case of zero vertices, use `Maybe`:   fromMaybe mempty <$> VGM.readMaybe (dataLkt kt) 1 --- | \(O(\log n)\) Applies a monoid action to points in \([x_l, x_r) \times [y_l, y_r)\).+-- | \(O(\log n)\) Applies a monoid action to points in \([x_1, x_2) \times [y_1, y_2)\). -- -- @since 1.2.2.0 {-# INLINE applyIn #-}@@ -263,13 +282,13 @@   (HasCallStack, PrimMonad m, Eq f, SegAct f a, VU.Unbox f, Monoid a, VU.Unbox a) =>   -- | `LazyKdTree`   LazyKdTree (PrimState m) f a ->-  -- | \(x_l\)+  -- | \(x_1\)   Int ->-  -- | \(x_r\)+  -- | \(x_2\)   Int ->-  -- | \(y_l\)+  -- | \(y_1\)   Int ->-  -- | \(y_r\)+  -- | \(y_2\)   Int ->   -- | \(f\)   f ->
+ src/AtCoder/Extra/SegTree2d.hs view
@@ -0,0 +1,478 @@+{-# LANGUAGE RecordWildCards #-}++-- | Two-dimensional segment tree for commutative monoids at fixed points.+--+-- ==== SegTree2d vs WaveletMatrix2d+-- They basically the same functionalities and performance, however, in @ac-library-hs@, `SegTree2d`+-- has better API and even outperforms @WaveletMatrix2d@.+--+-- ==== __Examples__+-- Create a two-dimensional segment tree for points \((0, 0)\) with weight \(10\) and \((1, 1)\)+-- with weight \(20\):+--+-- >>> import AtCoder.Extra.SegTree2d qualified as Seg+-- >>> import Data.Semigroup (Sum (..))+-- >>> import Data.Vector.Unboxed qualified as VU+-- >>> seg <- Seg.build3 @_ @(Sum Int) $ VU.fromList [(0, 0, 10), (1, 1, 20)]+--+-- Get monoid product in \([x_1, x_2) \times [y_1, y_2)\) with `prod`:+--+-- >>> Seg.prod seg {- x -} 0 2 {- y -} 0 2+-- Sum {getSum = 30}+--+-- Monoid values can be altered:+--+-- >>> Seg.write seg 1 50+-- >>> Seg.prod seg {- x -} 1 2 {- y -} 1 2+-- Sum {getSum = 50}+--+-- >>> Seg.allProd seg+-- Sum {getSum = 60}+--+-- >>> Seg.count seg {- x -} 0 2 {- y -} 0 2+-- 2+--+-- @since 1.2.3.0+module AtCoder.Extra.SegTree2d+  ( -- * SegTree2d+    SegTree2d (..),++    -- * Constructors+    new,+    build,+    build2,+    build3,++    -- * Write++    -- read,+    write,+    modify,+    modifyM,++    -- * Monoid product+    prod,+    allProd,++    -- * Count+    count,+  )+where++import AtCoder.Extra.Bisect (lowerBound)+import AtCoder.Extra.Vector (argsort)+import AtCoder.Internal.Assert qualified as ACIA+import AtCoder.Internal.Bit qualified as ACIB+import Control.Monad (when)+import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)+import Control.Monad.ST (ST)+import Data.Bits+import Data.Foldable (for_)+import Data.Maybe (fromJust, fromMaybe)+import Data.Vector.Algorithms.Intro qualified as VAI+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)++-- | Two-dimensional segment tree.+--+-- @since 1.2.3.0+data SegTree2d s a = SegTree2d+  { -- | The number of nodes.+    --+    -- @since 1.2.3.0+    nSt :: {-# UNPACK #-} !Int,+    -- | The number of distinct \(x\) coordinates.+    --+    -- @since 1.2.3.0+    nxSt :: {-# UNPACK #-} !Int,+    -- | \(\lceil \log_2 (\mathrm{nx} + 1) \rceil\)+    --+    -- @since 1.2.3.0+    logSt :: {-# UNPACK #-} !Int,+    -- | \(2^{\mathrm{logSt}}\)+    --+    -- @since 1.2.3.0+    sizeSt :: {-# UNPACK #-} !Int,+    -- | \(x\) coordinates sorted and uniquified+    --+    -- @since 1.2.3.0+    dictXSt :: {-# UNPACK #-} !(VU.Vector Int),+    -- | \(y\) coordinates sorted (not uniquified)+    --+    -- @since 1.2.3.0+    allYSt :: !(VU.Vector Int),+    -- |+    --+    -- @since 1.2.3.0+    posSt :: !(VU.Vector Int),+    -- |+    --+    -- @since 1.2.3.0+    indptrSt :: !(VU.Vector Int),+    -- | Monoid values+    --+    -- @since 1.2.3.0+    dataSt :: !(VUM.MVector s a),+    -- |+    --+    -- @since 1.2.3.0+    toLeftSt :: !(VU.Vector Int)+  }++-- | \(O(n \log n)\) Creates a `SegTree2d` from a vector of \((x, y)\) coordinates.+--+-- @since 1.2.3.0+{-# INLINEABLE new #-}+new ::+  (PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | \((x, y)\) vector+  VU.Vector (Int, Int) ->+  -- | Two-dimensional segment tree+  m (SegTree2d (PrimState m) a)+new xys = stToPrim $ buildST xs ys (VU.replicate n mempty)+  where+    n = VU.length xys+    (!xs, !ys) = VU.unzip xys++-- | \(O(n \log n)\) Creates a `SegTree2d` from vectors of \(x\), \(y\) and \(w\).+--+-- @since 1.2.3.0+{-# INLINE build #-}+build ::+  (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | \(x\) vector+  VU.Vector Int ->+  -- | \(y\) vector+  VU.Vector Int ->+  -- | \(w\) vector+  VU.Vector a ->+  -- | Two-dimensional segment tree+  m (SegTree2d (PrimState m) a)+build xs ys ws = stToPrim $ buildST xs ys ws++-- | \(O(n \log n)\) Creates a `SegTree2d` from vectors of \((x, y)\) and \(w\).+--+-- @since 1.2.3.0+{-# INLINE build2 #-}+build2 ::+  (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | \((x, y)\) vector+  VU.Vector (Int, Int) ->+  -- | \(w\) vector+  VU.Vector a ->+  -- | Two-dimensional segment tree+  m (SegTree2d (PrimState m) a)+build2 xys ws = stToPrim $ do+  let (!xs, !ys) = VU.unzip xys+  buildST xs ys ws++-- | \(O(n \log n)\) Creates a `SegTree2d` from a vector of \((x, y, w)\).+--+-- @since 1.2.3.0+{-# INLINE build3 #-}+build3 :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => VU.Vector (Int, Int, a) -> m (SegTree2d (PrimState m) a)+build3 xyws = stToPrim $ do+  let (!xs, !ys, !ws) = VU.unzip3 xyws+  buildST xs ys ws++-- -- | \(O(\log n)\) Read the \(k\)-th original point's monoid value.+-- --+-- -- @since 1.2.3.0+-- {-# INLINE read #-}+-- read :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => SegTree2d (PrimState m) a -> Int -> m ()+-- read SegTree2d {..} i = do+--   let !_ = ACIA.checkIndex "AtCoder.Extra.SegTree2d.read" i nSt+--   -- FIXME: pos is incorrect+--   VGM.read dataSt (posSt VG.! i)++-- | \(O(\log n)\) 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) =>+  -- | Two-dimensional segment tree+  SegTree2d (PrimState m) a ->+  -- | Original point index+  Int ->+  -- | New monoid value+  a ->+  m ()+write seg i x = stToPrim $ do+  modifyM seg (pure . const x) i++-- | \(O(\log n)\) Given \(f\), modofies the \(k\)-th original point's monoid value.+--+-- @since 1.2.3.0+{-# INLINE modify #-}+modify ::+  (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | Two-dimensional segment tree+  SegTree2d (PrimState m) a ->+  -- | Function that alters the monoid value+  (a -> a) ->+  -- | Original point index+  Int ->+  m ()+modify seg f i = stToPrim $ do+  modifyM seg (pure . f) i++-- | \(O(\log n)\) Given \(f\), modofies the \(k\)-th original point's monoid value.+--+-- @since 1.2.3.0+{-# INLINEABLE modifyM #-}+modifyM ::+  (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | Two-dimensional segment tree+  SegTree2d (PrimState m) a ->+  -- | Function that alters the monoid value+  (a -> m a) ->+  -- | Original point index+  Int ->+  m ()+modifyM seg@SegTree2d {..} f rawIdx = do+  let !_ = ACIA.checkIndex "AtCoder.Extra.SegTree2d.modifyM" rawIdx nSt+  inner 1 $ posSt VG.! rawIdx+  where+    inner i p = do+      let indptrI = indptrSt VG.! i+      modifyIST seg f i $ p - indptrI+      when (i < sizeSt) $ do+        let lc = toLeftSt VG.! p - toLeftSt VG.! indptrI+        let rc = (p - indptrI) - lc+        if toLeftSt VG.! (p + 1) - toLeftSt VG.! p /= 0+          then do+            let i' = 2 * i + 0+            let p' = indptrSt VG.! i' + lc+            inner i' p'+          else do+            let i' = 2 * i + 1+            let p' = indptrSt VG.! i' + rc+            inner i' p'++-- | \(O(\log^2 n)\) 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) =>+  -- | Two-dimensional segment tree+  SegTree2d (PrimState m) a ->+  -- | \(x_1\)+  Int ->+  -- | \(x_2\)+  Int ->+  -- | \(y_1\)+  Int ->+  -- | \(y_2\)+  Int ->+  -- | \(\Pi_{p \in [x_1, x_2) \times [y_1, y_2)} a_p\)+  m a+prod seg lx rx ly ry = stToPrim $ prodST seg lx rx ly ry++-- | \(O(1)\) Returns monoid product \(\Pi_{p \in [-\infty, \infty) \times [-\infty, \infty)} a_p\).+--+-- @since 1.2.3.0+{-# INLINE allProd #-}+allProd :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => SegTree2d (PrimState m) a -> m a+allProd seg = fromMaybe mempty <$> VGM.readMaybe (dataSt seg) 1++-- | \(O(\log n)\) Returns the number of points in \([x_1, x_2) \times [y_1, y_2)\).+--+-- @since 1.2.3.0+{-# INLINE count #-}+count ::+  (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) =>+  -- | Two-dimensional segment tree+  SegTree2d (PrimState m) a ->+  -- | \(x_1\)+  Int ->+  -- | \(x_2\)+  Int ->+  -- | \(y_1\)+  Int ->+  -- | \(y_2\)+  Int ->+  -- | The number of points in \([x_1, x_2) \times [y_1, y_2)\)+  m Int+count seg lx rx ly ry = stToPrim $ countST seg lx rx ly ry++-- -------------------------------------------------------------------------------------------------+-- Internal+-- -------------------------------------------------------------------------------------------------++{-# INLINEABLE buildST #-}+buildST :: forall s a. (HasCallStack, Monoid a, VU.Unbox a) => VU.Vector Int -> VU.Vector Int -> VU.Vector a -> ST s (SegTree2d s a)+buildST xs ys ws = do+  let nSt = VU.length xs+  let !_ = ACIA.runtimeAssert (nSt == VU.length ys && nSt == VU.length ws) "AtCoder.Extra.SegTree2d.buildST: length mismatch among `xs`, `ys` and `ws`"++  -- TODO: use radix sort?+  -- we don't have to take `uniq` though:+  let dictXSt = VU.uniq $ VU.modify VAI.sort xs+  let nxSt = VU.length dictXSt+  let logSt = countTrailingZeros $ ACIB.bitCeil (nxSt + 1)+  let sizeSt = bit logSt+  let compressedXs = VU.map (fromJust . lowerBound dictXSt) xs++  -- TODO: what is this?+  let indptrSt = VU.create $ do+        indptr <- VUM.replicate (2 * sizeSt + 1) (0 :: Int)+        VU.forM_ compressedXs $ \i -> do+          let inner j+                | j /= 0 = do+                    VGM.modify indptr (+ 1) (j + 1) -- +1 for perfix sum+                    inner (j `div` 2)+                | otherwise = pure ()+          inner $ i + sizeSt+        -- calculate prefix sum in-place:+        VUM.iforM_ (VUM.init indptr) $ \i x -> do+          VGM.modify indptr (+ x) (i + 1)+        pure indptr++  dataSt <- VUM.replicate (2 * VU.last indptrSt) (mempty :: a)++  let yis = argsort ys+  let posSt = VU.create $ do+        vec <- VUM.replicate nSt (0 :: Int)+        VU.iforM_ yis $ \i yi -> do+          -- FIXME: It overwrites duplicate yi to i. Isn't it just `accumulate max`?+          VGM.write vec yi i+        pure vec++  -- +1 for prefix cumulative sum+  toLeftSt <- do+    toLeft <- VUM.replicate (indptrSt VG.! sizeSt + 1) (0 :: Int)+    ptr <- VU.thaw indptrSt+    VU.forM_ yis $ \rawIdx -> do+      let inner i j+            | i > 0 = do+                p <- VGM.read ptr i+                VGM.write ptr i $ p + 1+                VGM.write dataSt (indptrSt VG.! (i + 1) + p) $ ws VG.! rawIdx+                when (j /= -1) $ do+                  VGM.write toLeft (p + 1) $ if even j then 1 else 0+                inner (i `div` 2) i+            | otherwise = pure ()+      let i0 = compressedXs VG.! rawIdx + sizeSt+      inner i0 (-1)++    -- calculate prefix cumulative sum in-place:+    VUM.iforM_ (VUM.init toLeft) $ \i x -> do+      VGM.modify toLeft (+ x) (i + 1)+    VU.unsafeFreeze toLeft++  for_ [0 .. 2 * sizeSt - 1] $ \i -> do+    let off = 2 * indptrSt VG.! i+    let n = indptrSt VG.! (i + 1) - indptrSt VG.! i+    for_ [n - 1, n - 2 .. 1] $ \j -> do+      xl <- VGM.read dataSt $ off + 2 * j + 0+      xr <- VGM.read dataSt $ off + 2 * j + 1+      VGM.write dataSt (off + j) $! xl <> xr++  -- TODO: why not uniquified?+  let allYSt = VU.modify VAI.sort ys+  pure SegTree2d {..}++{-# INLINEABLE modifyIST #-}+modifyIST :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => SegTree2d (PrimState m) a -> (a -> m a) -> Int -> Int -> m ()+modifyIST SegTree2d {..} f i j0 = do+  -- TODO: LID?+  let lid = indptrSt VG.! i+  let off = 2 * lid+  let inner j_ = do+        when (j_ > 1) $ do+          let j = j_ `div` 2+          xl <- VGM.read dataSt (off + 2 * j + 0)+          xr <- VGM.read dataSt (off + 2 * j + 1)+          VGM.write dataSt (off + j) $! xl <> xr+          inner j+  let j = j0 + indptrSt VG.! (i + 1) - lid+  VGM.modifyM dataSt f (off + j)+  stToPrim $ inner j++{-# INLINEABLE prodST #-}+prodST :: forall s a. (HasCallStack, Monoid a, VU.Unbox a) => SegTree2d s a -> Int -> Int -> Int -> Int -> ST s a+prodST seg@SegTree2d {..} lx rx ly ry = do+  let a0 = fromMaybe (VU.length allYSt) $ lowerBound allYSt ly+  let b0 = fromMaybe (VU.length allYSt) $ lowerBound allYSt ry+  dfs mempty 1 0 sizeSt a0 b0+  where+    !_ = ACIA.runtimeAssert (lx <= rx && ly <= ry) "AtCoder.Extra.SegTree2d.prodST: given invalid rectangle"+    !l0 = fromMaybe (VU.length dictXSt) $ lowerBound dictXSt lx+    !r0 = fromMaybe (VU.length dictXSt) $ lowerBound dictXSt rx+    dfs :: a -> Int -> Int -> Int -> Int -> Int -> ST s a+    dfs !res i l r a b+      -- empty rect+      | a == b = pure res+      -- not intersecting+      | r0 <= l || r <= l0 = pure res+      -- fully contained in [l_0, r_0)+      | l0 <= l && r <= r0 = do+          xi <- prodIST seg i a b+          pure $! xi <> res+      | otherwise = do+          let indptrI = indptrSt VG.! i+          let toLeftI = toLeftSt VG.! indptrI+          let la = toLeftSt VG.! (indptrI + a) - toLeftI+          let ra = a - la+          let lb = toLeftSt VG.! (indptrI + b) - toLeftI+          let rb = b - lb+          let m = (l + r) `div` 2+          res' <- dfs res (2 * i + 0) l m la lb+          dfs res' (2 * i + 1) m r ra rb++{-# INLINEABLE prodIST #-}+prodIST :: (HasCallStack, Monoid a, VU.Unbox a) => SegTree2d s a -> Int -> Int -> Int -> ST s a+prodIST SegTree2d {..} i a b = inner mempty (n + a) (n + b - 1)+  where+    lid = indptrSt VG.! i+    off = 2 * lid+    n = indptrSt VG.! (i + 1) - lid+    -- NOTE: we're using inclusive interval [l, r] for simplicity+    inner !res l r+      | l <= r = do+          res' <-+            if testBit l 0+              then (res <>) <$> VGM.read dataSt (off + l)+              else pure res+          res'' <-+            if not $ testBit r 0+              then (<> res') <$> VGM.read dataSt (off + r)+              else pure res'+          inner res'' ((l + 1) .>>. 1) ((r - 1) .>>. 1)+      | otherwise = pure res++{-# INLINEABLE countST #-}+countST :: forall s a. (HasCallStack, Monoid a, VU.Unbox a) => SegTree2d s a -> Int -> Int -> Int -> Int -> ST s Int+countST SegTree2d {..} lx rx ly ry = do+  let a0 = fromMaybe (VU.length allYSt) $ lowerBound allYSt ly+  let b0 = fromMaybe (VU.length allYSt) $ lowerBound allYSt ry+  dfs 0 1 0 sizeSt a0 b0+  where+    !_ = ACIA.runtimeAssert (lx <= rx && ly <= ry) "AtCoder.Extra.SegTree2d.countST: given invalid rectangle"+    !l0 = fromMaybe (VU.length dictXSt) $ lowerBound dictXSt lx+    !r0 = fromMaybe (VU.length dictXSt) $ lowerBound dictXSt rx+    dfs :: Int -> Int -> Int -> Int -> Int -> Int -> ST s Int+    dfs (res :: Int) i l r a b+      -- empty rect+      | a == b = pure res+      -- not intersecting+      | r0 <= l || r <= l0 = pure res+      -- fully contained in [l_0, r_0)+      | l0 <= l && r <= r0 = do+          pure $! res + b - a+      | otherwise = do+          let indptrI = indptrSt VG.! i+          let toLeftI = toLeftSt VG.! indptrI+          let la = toLeftSt VG.! (indptrI + a) - toLeftI+          let ra = a - la+          let lb = toLeftSt VG.! (indptrI + b) - toLeftI+          let rb = b - lb+          let m = (l + r) `div` 2+          res' <- dfs res (2 * i + 0) l m la lb+          dfs res' (2 * i + 1) m r ra rb
+ src/AtCoder/Extra/SegTree2d/Dense.hs view
@@ -0,0 +1,326 @@+{-# 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 AtCoder.Internal.Bit qualified as ACIB+import Control.Monad (when)+import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)+import Control.Monad.ST (ST)+import Data.Bits+import Data.Foldable (for_)+import Data.Maybe (fromJust, fromMaybe)+import Data.Vector qualified as V+import Data.Vector.Algorithms.Intro qualified as VAI+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)++-- | 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 seg@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 seg@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@DenseSegTree2d {..} x y a = stToPrim $ do+  modifyM seg (pure . const a) x y++-- | \(O(\log  h \log w)\) Given \(f\), modofies the monoid value at \((x, y)\).+--+-- @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 \(f\), modofies the monoid value at \((x, y)\).+--+-- @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+  -- FIXME: correct?+  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)
+ src/AtCoder/Extra/Vector.hs view
@@ -0,0 +1,67 @@+-- | Miscellaneous vector methods.+--+-- @since 1.2.2.0+module AtCoder.Extra.Vector+  ( argsort,+    unsafePermuteInPlace,+    unsafePermuteInPlaceST,+  )+where++import AtCoder.Internal.Assert qualified as ACIA+import Control.Monad (unless)+import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)+import Control.Monad.ST (ST)+import Data.Vector.Algorithms.Intro qualified as VAI+import Data.Vector.Generic qualified as VG+import Data.Vector.Generic.Mutable qualified as VGM+import Data.Vector.Unboxed qualified as VU++-- TODO: test `unsafePermuteInPlace`+-- TODO: is `unsafePermuteInPlace` fast enough as specialized one?++-- | \(O(n \log n)\) Returns indices of the vector, stably sorted by their value.+--+-- ==== Example+-- >>> import Data.Vector.Algorithms.Intro qualified as VAI+-- >>> import Data.Vector.Unboxed qualified as VU+-- >>> argsort $ VU.fromList [0, 1, 0, 1, 0]+-- [0,2,4,1,3]+{-# INLINE argsort #-}+argsort :: (Ord a, VU.Unbox a) => VU.Vector a -> VU.Vector Int+argsort xs =+  VU.modify+    ( VAI.sortBy+        ( \i j ->+            ( compare (xs VG.! i) (xs VG.! j) <> compare i j+            )+        )+    )+    $ VU.generate (VU.length xs) id++-- | \(O(n)\) Applies a permutation to a mutable vector in-place.+--+-- ==== Constraints+-- - The index array must be a permutation (0-based).+{-# INLINE unsafePermuteInPlace #-}+unsafePermuteInPlace :: (PrimMonad m, VGM.MVector v a) => v (PrimState m) a -> VU.Vector Int -> m ()+unsafePermuteInPlace vec is = stToPrim $ unsafePermuteInPlaceST vec is++-- | \(O(n)\) Applies a permutation to a mutable vector in-place.+--+-- ==== Constraints+-- - The index array must be a permutation (0-based).+{-# INLINEABLE unsafePermuteInPlaceST #-}+unsafePermuteInPlaceST :: (VGM.MVector v a) => v s a -> VU.Vector Int -> ST s ()+unsafePermuteInPlaceST vec is = do+  let !_ = ACIA.runtimeAssert (VGM.length vec == VG.length is) "AtCoder.Extra.Vector.unsafePermuteInPlaceST: the length of the index array must be equal to the length of the permuted vector"+  let inner i lastX = do+        VGM.unsafeWrite vec i lastX+        unless (i == 0) $ do+          let i0' = VG.unsafeIndex is i+          lastX' <- VGM.unsafeRead vec i+          inner i0' lastX'++  let i0' = VG.unsafeIndex is 0+  x0' <- VGM.unsafeRead vec 0+  inner i0' x0'
src/AtCoder/Extra/WaveletMatrix.hs view
@@ -99,14 +99,14 @@ -- original array if you can. -- -- @since 1.1.0.0-{-# INLINEABLE access #-}+{-# INLINE access #-} access :: WaveletMatrix -> Int -> Maybe Int access WaveletMatrix {..} i = (xDictWM VG.!) <$> Rwm.access rawWM i  -- | \(O(\log |S|)\) Returns the number of \(y\) in \([l, r)\). -- -- @since 1.1.0.0-{-# INLINEABLE rank #-}+{-# INLINE rank #-} rank ::   -- | A wavelet matrix   WaveletMatrix ->@@ -151,7 +151,7 @@ -- not found. -- -- @since 1.1.0.0-{-# INLINEABLE select #-}+{-# INLINE select #-} select :: WaveletMatrix -> Int -> Maybe Int select wm = selectKth wm 0 @@ -180,7 +180,7 @@ -- (0-based) of \(y\) in the sequence, or `Nothing` if no such occurrence exists. -- -- @since 1.1.0.0-{-# INLINEABLE selectIn #-}+{-# INLINE selectIn #-} selectIn ::   -- | A wavelet matrix   WaveletMatrix ->@@ -301,7 +301,7 @@ -- | \(O(\log |S|)\) -- -- @since 1.1.0.0-{-# INLINEABLE unsafeKthSmallestIn #-}+{-# INLINE unsafeKthSmallestIn #-} unsafeKthSmallestIn :: WaveletMatrix -> Int -> Int -> Int -> Int unsafeKthSmallestIn WaveletMatrix {..} l r k =   xDictWM VG.! Rwm.unsafeKthSmallestIn rawWM l r k@@ -334,7 +334,7 @@ -- | \(O(\log |S|)\) Looks up the maximum \(y\) in \([l, r) \times (-\infty, y_0)\). -- -- @since 1.1.0.0-{-# INLINEABLE lookupLT #-}+{-# INLINE lookupLT #-} lookupLT ::   -- | A wavelet matrix   WaveletMatrix ->@@ -376,7 +376,7 @@ -- | \(O(\log |S|)\) Looks up the minimum \(y\) in \([l, r) \times (y_0, \infty)\). -- -- @since 1.1.0.0-{-# INLINEABLE lookupGT #-}+{-# INLINE lookupGT #-} lookupGT ::   -- | A wavelet matrix   WaveletMatrix ->@@ -394,7 +394,7 @@ -- ascending order of \(y\). Note that it's only fast when the \(|S|\) is very small. -- -- @since 1.1.0.0-{-# INLINEABLE assocsIn #-}+{-# INLINE assocsIn #-} assocsIn :: WaveletMatrix -> Int -> Int -> [(Int, Int)] assocsIn WaveletMatrix {..} l r = Rwm.assocsWith rawWM l r (xDictWM VG.!) @@ -402,6 +402,6 @@ -- descending order of \(y\). Note that it's only fast when the \(|S|\) is very small. -- -- @since 1.1.0.0-{-# INLINEABLE descAssocsIn #-}+{-# INLINE descAssocsIn #-} descAssocsIn :: WaveletMatrix -> Int -> Int -> [(Int, Int)] descAssocsIn WaveletMatrix {..} l r = Rwm.descAssocsInWith rawWM l r (xDictWM VG.!)
src/AtCoder/Extra/WaveletMatrix/Raw.hs view
@@ -249,7 +249,7 @@ -- | \(O(\log |S|)\) Returns the number of \(y\) in \([l, r)\). -- -- @since 1.1.0.0-{-# INLINEABLE rank #-}+{-# INLINE rank #-} rank ::   RawWaveletMatrix ->   -- | \(l\)@@ -265,7 +265,7 @@ -- | \(O(\log |S|)\) Returns the number of \(y\) in \([l, r) \times [y_1, y_2)\). -- -- @since 1.1.0.0-{-# INLINEABLE rankBetween #-}+{-# INLINE rankBetween #-} rankBetween ::   RawWaveletMatrix ->   -- | \(l\)@@ -284,7 +284,7 @@ -- not found. -- -- @since 1.1.0.0-{-# INLINEABLE select #-}+{-# INLINE select #-} select :: RawWaveletMatrix -> Int -> Maybe Int select wm = selectKth wm 0 @@ -292,7 +292,7 @@ -- if no such occurrence exists. -- -- @since 1.1.0.0-{-# INLINEABLE selectKth #-}+{-# INLINE selectKth #-} selectKth ::   RawWaveletMatrix ->   -- | \(k\)@@ -307,7 +307,7 @@ -- (0-based) of \(y\) in the sequence, or `Nothing` if no such occurrence exists. -- -- @since 1.1.0.0-{-# INLINEABLE selectIn #-}+{-# INLINE selectIn #-} selectIn ::   -- | A wavelet matrix   RawWaveletMatrix ->@@ -453,21 +453,21 @@ -- values are counted as distinct occurrences. -- -- @since 1.1.0.0-{-# INLINEABLE unsafeKthLargestIn #-}+{-# INLINE unsafeKthLargestIn #-} unsafeKthLargestIn :: RawWaveletMatrix -> Int -> Int -> Int -> Int unsafeKthLargestIn wm l r k = unsafeKthSmallestIn wm l r (r - l - (k + 1))  -- | \(O(\log a)\) -- -- @since 1.1.0.0-{-# INLINEABLE unsafeIKthLargestIn #-}+{-# INLINE unsafeIKthLargestIn #-} unsafeIKthLargestIn :: RawWaveletMatrix -> Int -> Int -> Int -> (Int, Int) unsafeIKthLargestIn wm l r k = unsafeIKthSmallestIn wm l r (r - l - (k + 1))  -- | \(O(\log a)\) -- -- @since 1.1.0.0-{-# INLINEABLE unsafeKthSmallestIn #-}+{-# INLINE unsafeKthSmallestIn #-} unsafeKthSmallestIn :: RawWaveletMatrix -> Int -> Int -> Int -> Int unsafeKthSmallestIn wm l_ r_ k_ =   let (!x, !_, !_, !_) = goDown wm l_ r_ k_@@ -511,7 +511,7 @@ -- | \(O(\log a)\) Finds the maximum \(x\) in \([l, r)\) s.t. \(x_{0} \lt x\). -- -- @since 1.1.0.0-{-# INLINEABLE lookupLT #-}+{-# INLINE lookupLT #-} lookupLT ::   RawWaveletMatrix ->   -- | \(l\)@@ -552,7 +552,7 @@ -- | \(O(\log |S|)\) Looks up the minimum \(y\) in \([l, r) \times (y_0, \infty)\). -- -- @since 1.1.0.0-{-# INLINEABLE lookupGT #-}+{-# INLINE lookupGT #-} lookupGT ::   RawWaveletMatrix ->   -- | \(l\)@@ -569,7 +569,7 @@ -- ascending order of \(y\). Note that it's only fast when the \(|S|\) is very small. -- -- @since 1.1.0.0-{-# INLINEABLE assocsIn #-}+{-# INLINE assocsIn #-} assocsIn :: RawWaveletMatrix -> Int -> Int -> [(Int, Int)] assocsIn wm l r = assocsWith wm l r id @@ -612,7 +612,7 @@ -- descending order of \(y\). Note that it's only fast when the \(|S|\) is very small. -- -- @since 1.1.0.0-{-# INLINEABLE descAssocsIn #-}+{-# INLINE descAssocsIn #-} descAssocsIn :: RawWaveletMatrix -> Int -> Int -> [(Int, Int)] descAssocsIn wm l r = descAssocsInWith wm l r id 
src/AtCoder/Extra/WaveletMatrix2d.hs view
@@ -4,6 +4,10 @@ -- queries. Points cannot be added after construction, but monoid values in each point can be -- modified later. --+-- ==== SegTree2d vs WaveletMatrix2d+-- They basically the same functionalities and performance, however, in @ac-library-hs@, `SegTree2d`+-- has better API and even outperforms @WaveletMatrix2d@.+-- -- ==== __Example__ -- Create a `WaveletMatrix2d` with initial vertex values: --@@ -200,7 +204,7 @@     i_     $ V.zip (Rwm.bitsRwm rawWmWm2d) segTreesWm2d --- | \(O(\log^2 n)\) Returns the monoid product in \([l, r) \times [y_1, y_2)\).+-- | \(O(\log^2 n)\) Returns monoid product \(\Pi_{p \in [x_1, x_2) \times [y_1, y_2)} a_p\). -- -- @since 1.1.0.0 {-# INLINEABLE prod #-}@@ -218,7 +222,7 @@     !_ = ACIA.checkInterval "AtCoder.Extra.WaveletMatrix.SegTree.prod (compressed x)" xl' xr' (VG.length xDict)     !_ = ACIA.checkInterval "AtCoder.Extra.WaveletMatrix.SegTree.prod (compressed y)" yl' yr' (VG.length yDictWm2d) --- | \(O(\log^2 n)\) Returns the monoid product in \([l, r) \times [y_1, y_2)\). Returns `Nothing` for invalid+-- | \(O(\log^2 n)\) Returns the monoid product in \([x_1, x_2) \times [y_1, y_2)\). Returns `Nothing` for invalid -- intervals. -- -- @since 1.1.0.0@@ -237,7 +241,7 @@     yl' = fromMaybe 0 $ bisectR 0 (VG.length yDictWm2d) $ (< yl) . VG.unsafeIndex yDictWm2d     yr' = fromMaybe (VG.length yDictWm2d) $ bisectR 0 (VG.length yDictWm2d) $ (< yr) . VG.unsafeIndex yDictWm2d --- | \(O(\log^2 n)\) Return the monoid product of all of the points in the wavelet matrix.+-- | \(O(\log^2 n)\) Return the monoid product in \([-\infty, \infty) \times [-\infty, \infty)\). -- -- @since 1.1.0.0 {-# INLINEABLE allProd #-}
src/AtCoder/FenwickTree.hs view
@@ -116,13 +116,6 @@ add :: (HasCallStack, PrimMonad m, Num a, VU.Unbox a) => FenwickTree (PrimState m) a -> Int -> a -> m () add ft p0 x = stToPrim $ addST ft p0 x --- | \(O(\log n)\) Calculates the sum in a half-open interval @[0, r)@.------ @since 1.0.0.0-{-# INLINE prefixSum #-}-prefixSum :: (PrimMonad m, Num a, VU.Unbox a) => FenwickTree (PrimState m) a -> Int -> m a-prefixSum ft r = stToPrim $ prefixSumST ft r- -- | Calculates the sum in a half-open interval \([l, r)\). -- -- ==== Constraints
src/AtCoder/Internal/Assert.hs view
@@ -40,6 +40,16 @@ -- *** Exception: AtCoder.Internal.Assert.doctest: given invalid interval `[-1, 0)` over bounds `[0, 5)` -- ... --+-- >>> let !_ = checkPoint2d "AtCoder.Internal.Assert.doctest"  1 1 2 2+-- >>> let !_ = checkPoint2d "AtCoder.Internal.Assert.doctest" 4 4 2 2+-- *** Exception: AtCoder.Internal.Assert.doctest: given invalid point `(4, 4)` for rectangle `[0, 2) x [0, 2)`+-- ...+--+-- >>> let !_ = checkRect "AtCoder.Internal.Assert.doctest"  1 2 1 2 3 3+-- >>> let !_ = checkRect "AtCoder.Internal.Assert.doctest" 1 2 1 2 1 1+-- *** Exception: AtCoder.Internal.Assert.doctest: given invalid rectangle `[1, 2) x [1, 2)` for rectangle `[0, 1) x [0, 1)`+-- ...+-- -- @since 1.0.0.0 module AtCoder.Internal.Assert   ( -- * Runtime assertion@@ -49,6 +59,9 @@     testIndex,     testInterval,     testIntervalBounded,+    testPoint2d,+    testRect,+    testRectShape,      -- * Index assertions     checkIndex,@@ -67,6 +80,14 @@     errorInterval,     checkIntervalBounded,     errorIntervalBounded,++    -- * Two-dimensional index assertions+    checkPoint2d,+    errorPoint2d,+    checkRect,+    errorRect,+    checkRectShape,+    errorRectShape,   ) where @@ -102,6 +123,27 @@ testIntervalBounded :: Int -> Int -> Int -> Int -> Bool testIntervalBounded l r l0 r0 = l0 <= l && l <= r && r <= r0 +-- | \(O(1)\) Tests \((x, y) \in [0, w) \times [0, h)\).+--+-- @since 1.2.3.0+{-# INLINE testPoint2d #-}+testPoint2d :: (HasCallStack) => Int -> Int -> Int -> Int -> Bool+testPoint2d x y w h = 0 <= x && x < w && 0 <= y && y < h++-- | \(O(1)\) Tests \([x_1, x_2) \times [y_1 y_2) \in [0, w) \times [0, h)\).+--+-- @since 1.2.3.0+{-# INLINE testRect #-}+testRect :: (HasCallStack) => Int -> Int -> Int -> Int -> Int -> Int -> Bool+testRect x1 x2 y1 y2 w h = 0 <= x1 && x1 <= x2 && x2 <= w && 0 <= y1 && y1 <= y2 && y2 <= h++-- | \(O(1)\) Tests \(x_1 \le x_2\) and \(y_1 \le \y_2\).+--+-- @since 1.2.3.0+{-# INLINE testRectShape #-}+testRectShape :: (HasCallStack) => Int -> Int -> Int -> Int -> Bool+testRectShape x1 x2 y1 y2 = x1 <= x2 && y1 <= y2+ -- | \(O(1)\) Asserts \(0 \leq i \lt n\) for an array index \(i\). -- -- @since 1.0.0.0@@ -145,7 +187,7 @@   | 0 <= i && i < n = ()   | otherwise = errorVertex funcName i n --- | \(O(1)\) Asserts \(0 \leq i \lt n\) for a graph vertex \(i\).+-- | \(O(1)\) Emits vertex boundary error. -- -- @since 1.0.0.0 {-# INLINE errorVertex #-}@@ -162,7 +204,7 @@   | 0 <= i && i < n = ()   | otherwise = errorEdge funcName i n --- | \(O(1)\) Asserts \(0 \leq i \lt m\) for an edge index \(i\).+-- | \(O(1)\) Emits edge index boundary error. -- -- @since 1.0.0.0 {-# INLINE errorEdge #-}@@ -170,7 +212,7 @@ errorEdge funcName i n =   error $ funcName ++ ": given invalid edge index `" ++ show i ++ "` over the number of edges `" ++ show n ++ "`" --- | \(O(1)\) Asserts \(0 \leq i \lt m\) for an edge index \(i\).+-- | \(O(1)\) Asserts index boundary with custom message. -- -- @since 1.0.0.0 {-# INLINE checkCustom #-}@@ -179,7 +221,7 @@   | testIndex i n = ()   | otherwise = errorCustom funcName indexName i setName n --- | \(O(1)\) Asserts \(0 \leq i \lt m\) for an edge index \(i\).+-- | \(O(1)\) Emis custom index error. -- -- @since 1.0.0.0 {-# INLINE errorCustom #-}@@ -195,7 +237,7 @@   | testInterval l r n = ()   | otherwise = errorInterval funcName l r n --- | \(O(1)\) Asserts \(0 \leq l \leq r \leq n\) for a half-open interval \([l, r)\).+-- | \(O(1)\) Emits interval boundary error. -- -- @since 1.0.0.0 {-# INLINE errorInterval #-}@@ -211,9 +253,60 @@   | testIntervalBounded l r l0 r0 = ()   | otherwise = errorIntervalBounded funcName l r l0 r0 --- | \(O(1)\) Asserts \(0 \leq l \leq r \leq n\) for a half-open interval \([l, r)\).+-- | \(O(1)\) Emits interval boundary error. -- -- @since 1.2.1.0 {-# INLINE errorIntervalBounded #-} errorIntervalBounded :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> a errorIntervalBounded funcName l r l0 r0 = error $ funcName ++ ": given invalid interval `[" ++ show l ++ ", " ++ show r ++ ")` over bounds `[" ++ show l0 ++ ", " ++ show r0 ++ ")`"++-- | \(O(1)\) Asserts \(0 \leq i \lt n\) for a graph vertex \(i\).+--+-- @since 1.2.3.0+{-# INLINE checkPoint2d #-}+checkPoint2d :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> ()+checkPoint2d funcName x y w h+  | testPoint2d x y w h = ()+  | otherwise = errorPoint2d funcName x y w h++-- | \(O(1)\) Emits point boundary error.+--+-- @since 1.2.3.0+{-# INLINE errorPoint2d #-}+errorPoint2d :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> a+errorPoint2d funcName x y w h =+  error $ funcName ++ ": given invalid point `(" ++ show x ++ ", " ++ show y ++ ")` for rectangle `[0, " ++ show w ++ ") x [0, " ++ show h ++ ")`"++-- | \(O(1)\) Asserts \([x_1, x_2) \times [y_1 y_2) \in [0, w) \times [0, h)\).+--+-- @since 1.2.3.0+{-# INLINE checkRect #-}+checkRect :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> Int -> Int -> ()+checkRect funcName x1 x2 y1 y2 w h+  | testRect x1 x2 y1 y2 w h = ()+  | otherwise = errorRect funcName x1 x2 y1 y2 w h++-- | \(O(1)\) Asserts rectangle boundary error.+--+-- @since 1.2.3.0+{-# INLINE errorRect #-}+errorRect :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> Int -> Int -> a+errorRect funcName x1 x2 y1 y2 w h =+  error $ funcName ++ ": given invalid rectangle `[" ++ show x1 ++ ", " ++ show x2 ++ ") x [" ++ show y1 ++ ", " ++ show y2 ++ ")` for rectangle `[0, " ++ show w ++ ") x [0, " ++ show h ++ ")`"++-- | \(O(1)\) Asserts \(x_1 \le x_2\) and \(y_1 \le \y_2\).+--+-- @since 1.2.3.0+{-# INLINE checkRectShape #-}+checkRectShape :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> ()+checkRectShape funcName x1 x2 y1 y2+  | testRectShape x1 x2 y1 y2 = ()+  | otherwise = errorRectShape funcName x1 x2 y1 y2++-- | \(O(1)\) Asserts rectangle boundary error.+--+-- @since 1.2.3.0+{-# INLINE errorRectShape #-}+errorRectShape :: (HasCallStack) => String -> Int -> Int -> Int -> Int -> a+errorRectShape funcName x1 x2 y1 y2 =+  error $ funcName ++ ": given invalid rectangle `[" ++ show x1 ++ ", " ++ show x2 ++ ") x [" ++ show y1 ++ ", " ++ show y2 ++ ")`"
src/AtCoder/Internal/Queue.hs view
@@ -20,20 +20,101 @@ -- >>> Q.freeze que     -- [_  1, 2  _] -- [1,2] ----- >>> Q.pushFront que 10   -- [10, 1, 2  _]+-- >>> Q.readFront que 0+-- 1+--+-- >>> Q.readFront que 1+-- 2+--+-- >>> Q.readFront que (-1)+-- *** Exception: AtCoder.Internal.Queue.readFront: index out of bounds+-- ...+--+-- >>> Q.readFront que 2+-- *** Exception: AtCoder.Internal.Queue.readFront: index out of bounds+-- ...+--+-- >>> Q.readMaybeFront que (-1)+-- Nothing+--+-- >>> Q.readMaybeFront que 2+-- Nothing+--+-- >>> Q.writeFront que 0 10 -- [_ 10, 2  _]+-- >>> Q.writeFront que 1 20 -- [_ 10, 20 _]+--+-- >>> Q.writeFront que (-1) 777+-- *** Exception: AtCoder.Internal.Queue.modifyFrontM: index out of bounds+-- ...+--+-- >>> Q.writeFront que 2 777+-- *** Exception: AtCoder.Internal.Queue.modifyFrontM: index out of bounds+-- ...+--+-- >>> Q.readBack que 0+-- 20+--+-- >>> Q.readBack que 1+-- 10+--+-- >>> Q.readBack que (-1)+-- *** Exception: AtCoder.Internal.Queue.readBack: index out of bounds+-- ...+--+-- >>> Q.readBack que 2+-- *** Exception: AtCoder.Internal.Queue.readBack: index out of bounds+-- ...+--+-- >>> Q.readMaybeBack que (-1)+-- Nothing+--+-- >>> Q.readMaybeBack que 2+-- Nothing+--+-- >>> Q.writeBack que 0 200+-- >>> Q.writeBack que 1 100+--+-- >>> Q.writeBack que (-1) 777+-- *** Exception: AtCoder.Internal.Queue.modifyBackM: index out of bounds+-- ...+--+-- >>> Q.writeBack que 2 777+-- *** Exception: AtCoder.Internal.Queue.modifyBackM: index out of bounds+-- ...+--+-- >>> Q.pushFront que 10 -- [10, 100, 200  _] -- >>> Q.pushFront que 1000--- *** Exception: AtCoder.Internal.Queue.pushFront: no empty front space+-- *** Exception: AtCoder.Internal.Queue.pushFrontST: no empty front space -- ... ----- >>> Q.unsafeFreeze que -- [10, 1, 2  _]--- [10,1,2]+-- >>> Q.unsafeFreeze que -- [10, 100, 200  _]+-- [10,100,200] ----- >>> Q.clear que      -- [_  _  _  _]+-- >>> Q.clear que        -- [_  _  _  _]+-- >>> Q.peekBack que+-- Nothing+--+-- >>> Q.popFront que+-- Nothing+--+-- >>> Q.popBack que+-- Nothing+-- -- >>> Q.pushBack que 0 -- [0  _  _  _]+-- >>> Q.peekBack que+-- Just 0+-- -- >>> Q.pushBack que 1 -- [0, 1  _  _] -- >>> Q.pushBack que 2 -- [0, 1, 2  _]+-- >>> Q.popBack que    -- [0, 1  _  _]+-- Just 2+-- -- >>> Q.freeze que--- [0,1,2]+-- [0,1]++-- >>> Q.clear que+-- >>> Q.freeze que+-- [] -- -- @since 1.0.0.0 module AtCoder.Internal.Queue@@ -48,15 +129,35 @@     length,     null, -    -- * Modifications+    -- * Element access -    -- ** Push/pop+    -- ** Peek+    peekBack,+    peekFront,++    -- ** Push     pushBack,     pushFront,++    -- ** op+    popBack,+    popBack_,     popFront,     popFront_, -    -- ** Reset+    -- ** Read/write/modify+    readFront,+    readBack,+    readMaybeFront,+    readMaybeBack,+    writeFront,+    writeBack,+    modifyFront,+    modifyFrontM,+    modifyBack,+    modifyBackM,++    -- ** Clear (reset)     clear,      -- * Conversions@@ -65,8 +166,10 @@   ) where +import AtCoder.Internal.Assert qualified as ACIA import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim) import Control.Monad.ST (ST)+import Data.Maybe (fromMaybe) import Data.Vector.Generic.Mutable qualified as VGM import Data.Vector.Unboxed qualified as VU import Data.Vector.Unboxed.Mutable qualified as VUM@@ -110,6 +213,20 @@ null :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m Bool null = (<$>) (== 0) . length +-- | \(O(1)\) Peeks the last element in the queue.+--+-- @since 1.2.3.0+{-# INLINE peekBack #-}+peekBack :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m (Maybe a)+peekBack que = stToPrim $ peekBackST que++-- | \(O(1)\) Peeks the first element in the queue.+--+-- @since 1.2.3.0+{-# INLINE peekFront #-}+peekFront :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m (Maybe a)+peekFront que = stToPrim $ peekFrontST que+ -- | \(O(1)\) Appends an element to the back. Will throw an exception if the index is out of range. -- -- @since 1.0.0.0@@ -124,6 +241,20 @@ pushFront :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> a -> m () pushFront que e = stToPrim $ pushFrontST que e +-- | \(O(1)\) Removes the last element from the queue and returns it, or `Nothing` if it is empty.+--+-- @since 1.2.3.0+{-# INLINE popBack #-}+popBack :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m (Maybe a)+popBack que = stToPrim $ popBackST que++-- | \(O(1)\) `popBack` with the return value discarded.+--+-- @since 1.2.3.0+{-# INLINE popBack_ #-}+popBack_ :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m ()+popBack_ que = stToPrim $ popBackST_ que+ -- | \(O(1)\) Removes the first element from the queue and returns it, or `Nothing` if it is empty. -- -- @since 1.0.0.0@@ -138,6 +269,92 @@ popFront_ :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> m () popFront_ que = stToPrim $ popFrontST_ que +-- | \(O(1)\) Returns the \(k\)-th value from the first element.+--+-- @since 1.2.3.0+{-# INLINE readFront #-}+readFront :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> m a+readFront que i = stToPrim $ fromMaybe (error msg) <$> readMaybeFrontST que i+  where+    msg = "AtCoder.Internal.Queue.readFront: index out of bounds"++-- | \(O(1)\) Returns the \(k\)-th value from the last element.+--+-- @since 1.2.3.0+{-# INLINE readBack #-}+readBack :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> m a+readBack que i = stToPrim $ fromMaybe (error msg) <$> readMaybeBackST que i+  where+    msg = "AtCoder.Internal.Queue.readBack: index out of bounds"++-- | \(O(1)\) Returns the \(k\)-th value from the first element.+--+-- @since 1.2.3.0+{-# INLINE readMaybeFront #-}+readMaybeFront :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> m (Maybe a)+readMaybeFront que i = stToPrim $ readMaybeFrontST que i++-- | \(O(1)\) Returns the \(k\)-th value from the last element.+--+-- @since 1.2.3.0+{-# INLINE readMaybeBack #-}+readMaybeBack :: (PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> m (Maybe a)+readMaybeBack que i = stToPrim $ readMaybeBackST que i++-- | \(O(1)\) Writes to the \(k\)-th value from the first element.+--+-- @since 1.2.3.0+{-# INLINE writeFront #-}+writeFront :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> a -> m ()+writeFront que i x = stToPrim $ do+  modifyFrontM que (pure . const x) i++-- | \(O(1)\) Writes to the \(k\)-th value from the last element.+--+-- @since 1.2.3.0+{-# INLINE writeBack #-}+writeBack :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> Int -> a -> m ()+writeBack que i x = stToPrim $ do+  modifyBackM que (pure . const x) i++-- | \(O(1)\) Given user function \(f\), returns the \(k\)-th value from the first element.+--+-- @since 1.2.3.0+{-# INLINE modifyFront #-}+modifyFront :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> (a -> a) -> Int -> m ()+modifyFront que f i = stToPrim $ do+  modifyFrontM que (pure . f) i++-- | \(O(1)\) Given user function \(f\), returns the \(k\)-th value from the last element.+--+-- @since 1.2.3.0+{-# INLINE modifyBack #-}+modifyBack :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> (a -> a) -> Int -> m ()+modifyBack que f i = stToPrim $ do+  modifyBackM que (pure . f) i++-- | \(O(1)\) Given user function \(f\), returns the \(k\)-th value from the first element.+--+-- @since 1.2.3.0+{-# INLINE modifyFrontM #-}+modifyFrontM :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> (a -> m a) -> Int -> m ()+modifyFrontM Queue {..} f i = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  let !_ = ACIA.runtimeAssert (0 <= i && i < r - l) "AtCoder.Internal.Queue.modifyFrontM: index out of bounds"+  VGM.modifyM vecQ f (l + i)++-- | \(O(1)\) Given user function \(f\), returns the \(k\)-th value from the last element.+--+-- @since 1.2.3.0+{-# INLINE modifyBackM #-}+modifyBackM :: (HasCallStack, PrimMonad m, VU.Unbox a) => Queue (PrimState m) a -> (a -> m a) -> Int -> m ()+modifyBackM Queue {..} f i = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  let !_ = ACIA.runtimeAssert (0 <= i && i < r - l) "AtCoder.Internal.Queue.modifyBackM: index out of bounds"+  VGM.modifyM vecQ f (r - 1 - i)+ -- | \(O(1)\) Sets the `length` to zero. -- -- @since 1.0.0.0@@ -179,6 +396,24 @@   r <- VGM.unsafeRead posQ 1   pure $ r - l +{-# INLINEABLE peekBackST #-}+peekBackST :: (VU.Unbox a) => Queue s a -> ST s (Maybe a)+peekBackST Queue {..} = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  if l >= r+    then pure Nothing+    else Just <$> VGM.read vecQ (r - 1)++{-# INLINEABLE peekFrontST #-}+peekFrontST :: (VU.Unbox a) => Queue s a -> ST s (Maybe a)+peekFrontST Queue {..} = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  if l >= r+    then pure Nothing+    else Just <$> VGM.read vecQ l+ {-# INLINEABLE pushBackST #-} pushBackST :: (HasCallStack, VU.Unbox a) => Queue s a -> a -> ST s () pushBackST Queue {..} e = do@@ -194,17 +429,33 @@ pushFrontST :: (HasCallStack, VU.Unbox a) => Queue s a -> a -> ST s () pushFrontST Queue {..} e = do   l0 <- VGM.unsafeRead posQ 0-  if l0 == 0-    then error "AtCoder.Internal.Queue.pushFront: no empty front space"+  let !_ = ACIA.runtimeAssert (l0 > 0) "AtCoder.Internal.Queue.pushFrontST: no empty front space"+  VGM.unsafeModifyM+    posQ+    ( \l -> do+        VGM.write vecQ (l - 1) e+        pure $ l - 1+    )+    0++{-# INLINEABLE popBackST #-}+popBackST :: (VU.Unbox a) => Queue s a -> ST s (Maybe a)+popBackST Queue {..} = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  if l >= r+    then pure Nothing     else do-      VGM.unsafeModifyM-        posQ-        ( \l -> do-            VGM.write vecQ (l - 1) e-            pure $ l - 1-        )-        0+      x <- VGM.read vecQ (r - 1)+      VGM.unsafeWrite posQ 1 (r - 1)+      pure $ Just x +{-# INLINEABLE popBackST_ #-}+popBackST_ :: (VU.Unbox a) => Queue s a -> ST s ()+popBackST_ que = do+  _ <- popBackST que+  pure ()+ {-# INLINEABLE popFrontST #-} popFrontST :: (VU.Unbox a) => Queue s a -> ST s (Maybe a) popFrontST Queue {..} = do@@ -220,13 +471,26 @@ {-# INLINEABLE popFrontST_ #-} popFrontST_ :: (VU.Unbox a) => Queue s a -> ST s () popFrontST_ que = do-  _ <- popFront que+  _ <- popFrontST que   pure () -{-# INLINEABLE clearST #-}-clearST :: (VU.Unbox a) => Queue s a -> ST s ()-clearST Queue {..} = do-  VGM.set posQ 0+{-# INLINEABLE readMaybeFrontST #-}+readMaybeFrontST :: (VU.Unbox a) => Queue s a -> Int -> ST s (Maybe a)+readMaybeFrontST Queue {..} i = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  if 0 <= i && i < r - l+    then Just <$> VGM.read vecQ (l + i)+    else pure Nothing++{-# INLINEABLE readMaybeBackST #-}+readMaybeBackST :: (VU.Unbox a) => Queue s a -> Int -> ST s (Maybe a)+readMaybeBackST Queue {..} i = do+  l <- VGM.unsafeRead posQ 0+  r <- VGM.unsafeRead posQ 1+  if 0 <= i && i < r - l+    then Just <$> VGM.read vecQ (r - 1 - i)+    else pure Nothing  {-# INLINEABLE freezeST #-} freezeST :: (VU.Unbox a) => Queue s a -> ST s (VU.Vector a)
test/Main.hs view
@@ -20,6 +20,8 @@ import Tests.Extra.Math qualified import Tests.Extra.Monoid qualified import Tests.Extra.MultiSet qualified+import Tests.Extra.SegTree2d qualified+import Tests.Extra.SegTree2d.Dense qualified import Tests.Extra.Semigroup.Matrix qualified import Tests.Extra.Semigroup.Permutation qualified import Tests.Extra.Seq qualified@@ -71,6 +73,8 @@             testGroup "Math" Tests.Extra.Math.tests,             testGroup "Monoid" Tests.Extra.Monoid.tests,             testGroup "MultiSet" Tests.Extra.MultiSet.tests,+            testGroup "SegTree2d" Tests.Extra.SegTree2d.tests,+            testGroup "SegTree2d.Dense" Tests.Extra.SegTree2d.Dense.tests,             testGroup "Semigroup.Matrix" Tests.Extra.Semigroup.Matrix.tests,             testGroup "Semigroup.Permutation" Tests.Extra.Semigroup.Permutation.tests,             testGroup "Seq" Tests.Extra.Seq.tests,
+ test/Tests/Extra/SegTree2d.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE RecordWildCards #-}++module Tests.Extra.SegTree2d (tests) where++import AtCoder.Extra.SegTree2d 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+  { capacity :: {-# UNPACK #-} !Int,+    q :: {-# UNPACK #-} !Int,+    debugPoints :: VU.Vector (Int, Int),+    vecM :: !(IO (VUM.MVector RealWorld (Int, Int, Sum Int))),+    segM :: !(IO (Seg.SegTree2d RealWorld (Sum Int)))+  }++instance Show Init where+  show Init {..} = show ("Init", capacity, q, debugPoints)++instance QC.Arbitrary Init where+  arbitrary = do+    QC.Positive n <- QC.arbitrary+    q <- QC.chooseInt (1, 2 * n)+    yxs <- (VU.fromList <$>) $ QC.vectorOf n $ do+      x <- QC.chooseInt (-16, 16)+      y <- QC.chooseInt (-16, 16)+      pure (x, y)+    let vecM = VU.thaw $ VU.map (\(!x, !y) -> (x, y, mempty)) yxs+    pure $ Init n q yxs vecM (Seg.new yxs)++data Query+  = -- Read !Int |+    Write !Int !Int+  | ModifyAdd !Int !Int+  | Prod !(Int, Int, Int, Int)+  | AllProd+  | Count !(Int, Int, Int, Int)+  deriving+    ( Eq,+      Show+    )++genQuery :: Int -> QC.Gen Query+genQuery n = do+  QC.oneof+    [ -- Read <$> i,+      Write <$> i <*> val,+      ModifyAdd <$> val <*> i,+      Prod <$> rect,+      pure AllProd,+      Count <$> rect+    ]+  where+    i = QC.chooseInt (0, n - 1)+    rect = do+      xl <- QC.chooseInt (-18, 18)+      xr <- QC.chooseInt (xl, 18)+      yl <- QC.chooseInt (-18, 18)+      yr <- QC.chooseInt (yl, 18)+      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 :: VUM.MVector RealWorld (Int, Int, Sum Int) -> Query -> IO Result+handleRef vec q = case q of+  -- Read i -> do+  --   S . (\(!_, !_, !w) -> w) <$> VGM.read vec i+  Write i v -> do+    (!x, !y, !_) <- VGM.read vec i+    VGM.write vec i (x, y, Sum v)+    pure None+  ModifyAdd w i -> do+    VGM.modify vec (\(!x, !y, !w0) -> (x, y, w0 + Sum w)) i+    pure None+  Prod (!x1, !x2, !y1, !y2) -> do+    vec' <- VU.unsafeFreeze vec+    pure+      . S+      . VU.sum+      . VU.map (\(!_, !_, !w) -> w)+      $ VU.filter (\(!x, !y, !_) -> x1 <= x && x < x2 && y1 <= y && y < y2) vec'+  AllProd -> do+    vec' <- VU.unsafeFreeze vec+    pure . S . VU.sum $ VU.map (\(!_, !_, !w) -> w) vec'+  Count (!x1, !x2, !y1, !y2) -> do+    vec' <- VU.unsafeFreeze vec+    pure+      . I+      . VU.length+      $ VU.filter (\(!x, !y, !_) -> x1 <= x && x < x2 && y1 <= y && y < y2) vec'++handleAcl :: (PrimMonad m) => Seg.SegTree2d (PrimState m) (Sum Int) -> Query -> m Result+handleAcl seg q = case q of+  -- Read i -> do+  --   S <$> Seg.read seg i+  Write i v -> do+    Seg.write seg i $ Sum v+    pure None+  ModifyAdd w i -> do+    Seg.modify seg (+ Sum w) i+    pure None+  Prod (!x1, !x2, !y1, !y2) -> do+    S <$> Seg.prod seg x1 x2 y1 y2+  AllProd -> do+    S <$> Seg.allProd seg+  Count (!x1, !x2, !y1, !y2) -> do+    I <$> Seg.count seg x1 x2 y1 y2++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 capacity)+  for_ qs $ \query -> do+    expected <- QCM.run $ handleRef 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.build @_ @(Sum Int) VU.empty VU.empty VU.empty+  (@?= mempty) =<< Seg.prod seg 0 10 0 10+  (@?= mempty) =<< Seg.allProd seg+  (@?= 0) =<< Seg.count seg 0 10 0 10++tests :: [TestTree]+tests =+  [ unit_zero,+    QC.testProperty "random test" prop_randomTest+  ]
+ test/Tests/Extra/SegTree2d/Dense.hs view
@@ -0,0 +1,146 @@+{-# 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+  ]