packages feed

dawgdic-0.1.0: src/Data/DAWG/Internal/LinkTable.hs

{-|
Module: Data.DAWG.Internal.LinkTable
Description: Exports link table used to build dictionary as well as its internal API.
Copyright: (c) Andrey Prokopenko, 2025
License: BSD-3-Clause
Stability: experimental
-}
{-# LANGUAGE BangPatterns #-}
module Data.DAWG.Internal.LinkTable where

import Control.Monad (forM_, when)
import Control.Monad.Primitive (PrimMonad)
import Data.Maybe (fromMaybe)

import Data.DAWG.Internal.BaseType

import qualified Data.Vector.Hashtables as HT

-- ** 'LinkTable'

-- | Alias for 'UUHT' that holds a pair of index with offset as value
-- associated with a hash as a key.
type LinkTable m = UUHT m BaseType (BaseType, BaseType)

-- | Allocates a table space with given size.
init :: PrimMonad m => LinkTable m -> BaseType -> m ()
init ht size = do
  when (size > 0) $ forM_ [0 .. size - 1] \ix -> do
    HT.insert ht (fromIntegral ix) (0, 0)
{-# INLINE init #-}

-- | Stores index with offset into the table.
insert :: PrimMonad m => LinkTable m -> BaseType -> BaseType -> m ()
insert ht !ix !offset = do
  !hid <- findId ht ix
  HT.insert ht hid (ix, offset)
{-# INLINE insert #-}

-- | Find an offset that corresponds to a given index.
find :: PrimMonad m => LinkTable m -> BaseType -> m BaseType
find ht ix = do
  !hid <- findId ht ix
  HT.lookup ht hid >>= pure . fromMaybe 0 . fmap snd
{-# INLINE find #-}

-- ** Helpers

-- | Finds a hash associated with a given index.
findId :: PrimMonad m => LinkTable m -> BaseType -> m BaseType
findId ht !ix = do
  htsize <- HT.size ht
  let !startHid = fromIntegral (hashBaseType ix `mod` htsize)
      go !hid = do
        let !nextHid = succ hid `mod` fromIntegral htsize
        HT.lookup ht (fromIntegral hid) >>= \case
          Nothing -> pure hid
          Just result -> if fst result == ix || fst result == 0
            then pure hid
            else go nextHid
  go startHid
{-# INLINE findId #-}