packages feed

moonlight-core-0.1.0.0: src-solver/Moonlight/Core/UnionFind/Transaction/Internal/Snapshot.hs

-- | Freezing the editor back to immutable maps: parent/rank snapshots that
-- choose between a full dense sweep and a dirty-key overlay depending on which
-- is cheaper, with the dense sweeps producing ascending entry lists.
module Moonlight.Core.UnionFind.Transaction.Internal.Snapshot
  ( parentMap,
    rankMap,
  )
where

import Control.Monad.ST (ST)
import Data.Foldable (foldlM)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.STRef (STRef, readSTRef)
import Data.Vector.Unboxed.Mutable qualified as Mutable
import Moonlight.Core.Identifier.EGraph (ClassId (..))
import Moonlight.Core.UnionFind.Internal.Types (UnionFind (..))
import Moonlight.Core.UnionFind.Transaction.Internal.DenseStore
  ( readDenseParentKey,
    readDenseRank,
  )
import Moonlight.Core.UnionFind.Transaction.Internal.Types
  ( DenseStore (..),
    UnionFindEditor (..),
    denseFlagSet,
  )
import Prelude

parentMap ::
  UnionFindEditor state ->
  ST state (IntMap ClassId)
parentMap editor = do
  sparseWrites <- readSTRef (sparseParentWrites editor)
  useDenseSnapshot <- denseOutweighsDirtyOverlay editor (dirtyDenseParentCount editor)
  if IntMap.null (ufParent (base editor)) || useDenseSnapshot
    then do
      denseParents <- denseParentMap editor
      pure (IntMap.union denseParents (IntMap.union sparseWrites (ufParent (base editor))))
    else do
      dirtyDenseKeys <- readSTRef (dirtyDenseParents editor)
      foldlM
        insertDenseParent
        (IntMap.union sparseWrites (ufParent (base editor)))
        dirtyDenseKeys
  where
    insertDenseParent parents key = do
      maybeParentKey <- readDenseParentKey editor key
      pure $
        case maybeParentKey of
          Nothing ->
            parents
          Just parentKey ->
            IntMap.insert key (ClassId parentKey) parents

denseOutweighsDirtyOverlay ::
  UnionFindEditor state ->
  STRef state Int ->
  ST state Bool
denseOutweighsDirtyOverlay editor dirtyCountReference = do
  dirtyCount <- readSTRef dirtyCountReference
  denseMemberCount <- readSTRef (denseMemberCount editor)
  pure (dirtyCount * 4 >= denseMemberCount)

denseParentMap ::
  UnionFindEditor state ->
  ST state (IntMap ClassId)
denseParentMap editor = do
  store <- readSTRef (dense editor)
  entries <-
    denseParentEntries
      store
      0
      (Mutable.length (parent store))
      []
  pure (IntMap.fromDistinctAscList entries)

denseParentEntries ::
  DenseStore state ->
  Int ->
  Int ->
  [(Int, ClassId)] ->
  ST state [(Int, ClassId)]
denseParentEntries store key limit entries
  | key >= limit =
      pure (reverse entries)
  | otherwise = do
      present <- Mutable.read (present store) key
      if present == denseFlagSet
        then do
          parentKey <- Mutable.read (parent store) key
          denseParentEntries
            store
            (key + 1)
            limit
            ((key, ClassId parentKey) : entries)
        else denseParentEntries store (key + 1) limit entries

rankMap ::
  UnionFindEditor state ->
  ST state (IntMap Int)
rankMap editor = do
  sparseWrites <- readSTRef (sparseRankWrites editor)
  useDenseSnapshot <- denseOutweighsDirtyOverlay editor (dirtyDenseRankCount editor)
  if IntMap.null (ufRank (base editor)) || useDenseSnapshot
    then do
      denseRanks <- denseRankMap editor
      pure
        (IntMap.union denseRanks (IntMap.union sparseWrites (ufRank (base editor))))
    else do
      dirtyDenseKeys <- readSTRef (dirtyDenseRanks editor)
      foldlM
        insertDenseRank
        (IntMap.union sparseWrites (ufRank (base editor)))
        dirtyDenseKeys
  where
    insertDenseRank ranks key = do
      maybeRank <- readDenseRank editor key
      pure $
        case maybeRank of
          Nothing ->
            ranks
          Just rankValue ->
            IntMap.insert key rankValue ranks

denseRankMap ::
  UnionFindEditor state ->
  ST state (IntMap Int)
denseRankMap editor = do
  store <- readSTRef (dense editor)
  entries <-
    denseRankEntries
      store
      0
      (Mutable.length (rank store))
      []
  pure (IntMap.fromDistinctAscList entries)

denseRankEntries ::
  DenseStore state ->
  Int ->
  Int ->
  [(Int, Int)] ->
  ST state [(Int, Int)]
denseRankEntries store key limit entries
  | key >= limit =
      pure (reverse entries)
  | otherwise = do
      present <- Mutable.read (present store) key
      if present == denseFlagSet
        then do
          rankValue <- Mutable.read (rank store) key
          denseRankEntries
            store
            (key + 1)
            limit
            ((key, rankValue) : entries)
        else denseRankEntries store (key + 1) limit entries