packages feed

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

{-# LANGUAGE StandaloneKindSignatures #-}

module Moonlight.Core.UnionFind.Internal.Semantics
  ( LinkDecision (..),
    chooseLink,
    rootKeyBy,
  )
where

import Data.Kind
  ( Type,
  )
import Moonlight.Core.Identifier.EGraph
  ( ClassId,
  )
import Prelude
  ( Eq ((==)),
    Int,
    Maybe (..),
    Monad,
    Ord (compare, max, min),
    Ordering (..),
    Show,
    otherwise,
    (+),
    pure,
    (>>=),
  )

type LinkDecision :: Type
data LinkDecision
  = AttachRoot !ClassId !ClassId
  | AttachRootAndRaise !ClassId !ClassId !Int
  deriving stock (Eq, Show)

-- | Decide how two distinct roots are linked. Equal-rank links deterministically
-- retain the lesser 'ClassId'.
chooseLink :: ClassId -> Int -> ClassId -> Int -> LinkDecision
chooseLink leftRoot leftRank rightRoot rightRank =
  case compare leftRank rightRank of
    LT ->
      AttachRoot leftRoot rightRoot
    GT ->
      AttachRoot rightRoot leftRoot
    EQ ->
      AttachRootAndRaise
        (max leftRoot rightRoot)
        (min leftRoot rightRoot)
        (leftRank + 1)
{-# INLINE chooseLink #-}

-- | Shared forest ascent law for both persistent and transient storage.
-- A missing parent is a singleton root, and a self-parent terminates the path.
rootKeyBy ::
  Monad m =>
  (Int -> m (Maybe Int)) ->
  Int ->
  m Int
rootKeyBy readParentKey startKey =
  readParentKey startKey >>= \maybeParentKey ->
    case maybeParentKey of
      Nothing ->
        pure startKey
      Just parentKey
        | parentKey == startKey ->
            pure startKey
        | otherwise ->
            rootKeyBy readParentKey parentKey
{-# INLINE rootKeyBy #-}