packages feed

apply-merge-0.1.0.0: src/Data/PQueue/Prio/Min/Mutable.hs

-- SPDX-FileCopyrightText: Copyright Preetham Gujjula
-- SPDX-License-Identifier: BSD-3-Clause
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE NoFieldSelectors #-}

module Data.PQueue.Prio.Min.Mutable
  ( -- * Documentation
    MMinPQueue,

    -- * Construction
    empty,
    singleton,
    insert,

    -- * Query
    deleteMin,

    -- * List conversion
    fromAscList,
  )
where

import Control.Monad (when)
import Control.Monad.ST (ST)
import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
import Data.Vector qualified as Vector
import Data.Vector.Mutable (MVector)
import Data.Vector.Mutable qualified as MVector

data MMinPQueue s k a
  = MMinPQueue
  { vectorRef :: STRef s (MVector s (k, a)),
    sizeRef :: STRef s Int
  }

empty :: ST s (MMinPQueue s k a)
empty = do
  vec <- MVector.unsafeNew 1
  vecRef <- newSTRef vec
  sizeRef <- newSTRef 0
  pure $
    MMinPQueue
      { vectorRef = vecRef,
        sizeRef = sizeRef
      }

singleton :: k -> a -> ST s (MMinPQueue s k a)
singleton k v = do
  vec <- MVector.replicate 1 (k, v)
  vecRef <- newSTRef vec
  sizeRef <- newSTRef 1
  pure $
    MMinPQueue
      { vectorRef = vecRef,
        sizeRef = sizeRef
      }

resizeIfNeeded :: MMinPQueue s k a -> ST s ()
resizeIfNeeded pqueue = do
  vec <- readSTRef pqueue.vectorRef
  size <- readSTRef pqueue.sizeRef
  when (MVector.length vec == size) $ do
    vec' <- MVector.unsafeGrow vec size
    writeSTRef pqueue.vectorRef vec'

insert :: (Ord k) => k -> a -> MMinPQueue s k a -> ST s ()
insert k v pqueue = do
  resizeIfNeeded pqueue

  n <- readSTRef pqueue.sizeRef
  vec <- readSTRef pqueue.vectorRef
  MVector.unsafeWrite vec n (k, v)
  writeSTRef pqueue.sizeRef (n + 1)

  bubbleUp vec n

bubbleUp :: (Ord k) => MVector s (k, a) -> Int -> ST s ()
bubbleUp vector = go
  where
    go i =
      if i == 0
        then pure ()
        else do
          let parentIndex = (i - 1) `quot` 2
          parentVal <- MVector.unsafeRead vector parentIndex
          currentVal <- MVector.unsafeRead vector i
          when (fst parentVal > fst currentVal) $ do
            MVector.unsafeSwap vector parentIndex i
            go parentIndex

deleteMin :: (Ord k) => MMinPQueue s k a -> ST s (Maybe (k, a))
deleteMin pqueue = do
  n <- readSTRef pqueue.sizeRef
  if n == 0
    then pure Nothing
    else do
      vector <- readSTRef pqueue.vectorRef
      minNode <- MVector.unsafeRead vector 0
      MVector.unsafeSwap vector 0 (n - 1)
      writeSTRef pqueue.sizeRef (n - 1)
      bubbleDown vector 0 (n - 1)
      pure (Just minNode)

bubbleDown :: (Ord k) => MVector s (k, a) -> Int -> Int -> ST s ()
bubbleDown vector i n = go i
  where
    go j = do
      case compare n (2 * j + 2) of
        LT -> pure ()
        EQ -> do
          x <- MVector.unsafeRead vector j
          x' <- MVector.unsafeRead vector (2 * j + 1)
          let j' = 2 * j + 1
          when (fst x > fst x') $ do
            MVector.unsafeWrite vector j' x
            MVector.unsafeWrite vector j x'
            go j'
        GT -> do
          x <- MVector.unsafeRead vector j
          xleft <- MVector.unsafeRead vector (2 * j + 1)
          xright <- MVector.unsafeRead vector (2 * j + 2)
          let (x', j') =
                if fst xleft <= fst xright
                  then (xleft, 2 * j + 1)
                  else (xright, 2 * j + 2)
          when (fst x > fst x') $ do
            MVector.unsafeWrite vector j' x
            MVector.unsafeWrite vector j x'
            go j'

fromAscList :: forall s k a. [(k, a)] -> ST s (MMinPQueue s k a)
fromAscList xs = do
  vector <- Vector.thaw (Vector.fromList xs)
  vectorRef <- newSTRef vector
  sizeRef <- newSTRef (length xs)
  pure $
    MMinPQueue
      { vectorRef = vectorRef,
        sizeRef = sizeRef
      }