packages feed

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

-- | The commit/abort boundary: thawing an immutable 'UnionFind' into a mutable
-- editor, materializing its dense prefix, and freezing a mutated editor back to
-- an immutable owner (returning the base unchanged when nothing was written).
module Moonlight.Core.UnionFind.Transaction.Internal.Lifecycle
  ( thawUnionFind,
    freezeUnionFind,
    materializeDenseBase,
  )
where

import Control.Monad.ST (ST)
import Data.Foldable (traverse_)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.STRef (newSTRef, 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.Policy
  ( chooseDenseLength,
    densePrefixMap,
  )
import Moonlight.Core.UnionFind.Transaction.Internal.Snapshot
  ( parentMap,
    rankMap,
  )
import Moonlight.Core.UnionFind.Transaction.Internal.Types
  ( DenseStore (..),
    UnionFindEditor (..),
    denseFlagSet,
    denseFlagUnset,
  )
import Prelude

thawUnionFind :: UnionFind -> ST state (UnionFindEditor state)
thawUnionFind base = do
  let denseLength = chooseDenseLength (ufParent base)
      denseBaseParents = densePrefixMap denseLength (ufParent base)
  denseParents <- Mutable.replicate denseLength 0
  denseRanks <- Mutable.replicate denseLength 0
  densePresence <- Mutable.replicate denseLength denseFlagUnset
  dirtyParents <- Mutable.replicate denseLength denseFlagUnset
  dirtyRanks <- Mutable.replicate denseLength denseFlagUnset
  denseReference <-
    newSTRef
      DenseStore
        { parent = denseParents,
          rank = denseRanks,
          present = densePresence,
          parentDirty = dirtyParents,
          rankDirty = dirtyRanks
        }
  sparseParentWrites <- newSTRef IntMap.empty
  sparseRankWrites <- newSTRef IntMap.empty
  dirtyDenseParents <- newSTRef []
  dirtyDenseRanks <- newSTRef []
  dirtyDenseParentCount <- newSTRef 0
  dirtyDenseRankCount <- newSTRef 0
  denseMemberCount <- newSTRef (IntMap.size denseBaseParents)
  nextFresh <- newSTRef (ufNextFresh base)
  let editor =
        UnionFindEditor
          { base = base,
            dense = denseReference,
            sparseParentWrites = sparseParentWrites,
            sparseRankWrites = sparseRankWrites,
            dirtyDenseParents = dirtyDenseParents,
            dirtyDenseRanks = dirtyDenseRanks,
            dirtyDenseParentCount = dirtyDenseParentCount,
            dirtyDenseRankCount = dirtyDenseRankCount,
            denseMemberCount = denseMemberCount,
            nextFresh = nextFresh
          }
  materializeDenseBase editor denseBaseParents
  pure editor

freezeUnionFind :: UnionFindEditor state -> ST state UnionFind
freezeUnionFind editor = do
  sparseParentWrites <- readSTRef (sparseParentWrites editor)
  sparseRankWrites <- readSTRef (sparseRankWrites editor)
  dirtyDenseParents <- readSTRef (dirtyDenseParents editor)
  dirtyDenseRanks <- readSTRef (dirtyDenseRanks editor)
  nextFresh <- readSTRef (nextFresh editor)
  if IntMap.null sparseParentWrites
    && IntMap.null sparseRankWrites
    && null dirtyDenseParents
    && null dirtyDenseRanks
    && nextFresh == ufNextFresh (base editor)
    then pure (base editor)
    else do
      parents <- parentMap editor
      ranks <- rankMap editor
      pure
        UnionFind
          { ufParent = parents,
            ufRank = ranks,
            ufNextFresh = nextFresh
          }

materializeDenseBase ::
  UnionFindEditor state ->
  IntMap ClassId ->
  ST state ()
materializeDenseBase editor denseParents = do
  store <- readSTRef (dense editor)
  traverse_
    (\(key, ClassId parentKey) -> do
       Mutable.write (parent store) key parentKey
       Mutable.write (rank store) key (IntMap.findWithDefault 0 key (ufRank (base editor)))
       Mutable.write (present store) key denseFlagSet
    )
    (IntMap.toAscList denseParents)